diff --git a/openlp/core/__init__.py b/openlp/core/__init__.py
index 6f6addbbd..5de5e69de 100644
--- a/openlp/core/__init__.py
+++ b/openlp/core/__init__.py
@@ -177,6 +177,38 @@ class OpenLP(OpenLPMixin, QtWidgets.QApplication):
self.shared_memory.create(1)
return False
+ def is_data_path_missing(self):
+ """
+ Check if the data folder path exists.
+ """
+ data_folder_path = AppLocation.get_data_path()
+ if not os.path.exists(data_folder_path):
+ log.critical('Database was not found in: ' + data_folder_path)
+ status = QtWidgets.QMessageBox.critical(None, translate('OpenLP', 'Data Directory Error'),
+ translate('OpenLP', 'OpenLP data folder was not found in:\n\n{path}'
+ '\n\nThe location of the data folder was '
+ 'previously changed from the OpenLP\'s '
+ 'default location. If the data was stored on '
+ 'removable device, that device needs to be '
+ 'made available.\n\nYou may reset the data '
+ 'location back to the default location, '
+ 'or you can try to make the current location '
+ 'available.\n\nDo you want to reset to the '
+ 'default data location? If not, OpenLP will be '
+ 'closed so you can try to fix the the problem.')
+ .format(path=data_folder_path),
+ QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes |
+ QtWidgets.QMessageBox.No),
+ QtWidgets.QMessageBox.No)
+ if status == QtWidgets.QMessageBox.No:
+ # If answer was "No", return "True", it will shutdown OpenLP in def main
+ log.info('User requested termination')
+ return True
+ # If answer was "Yes", remove the custom data path thus resetting the default location.
+ Settings().remove('advanced/data path')
+ log.info('Database location has been reset to the default settings.')
+ return False
+
def hook_exception(self, exc_type, value, traceback):
"""
Add an exception hook so that any uncaught exceptions are displayed in this window rather than somewhere where
@@ -208,8 +240,8 @@ class OpenLP(OpenLPMixin, QtWidgets.QApplication):
# If data_version is different from the current version ask if we should backup the data folder
elif data_version != openlp_version:
if QtWidgets.QMessageBox.question(None, translate('OpenLP', 'Backup'),
- translate('OpenLP', 'OpenLP has been upgraded, do you want to create '
- 'a backup of OpenLPs data folder?'),
+ translate('OpenLP', 'OpenLP has been upgraded, do you want to create\n'
+ 'a backup of the old data folder?'),
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.Yes:
# Create copy of data folder
@@ -223,8 +255,8 @@ class OpenLP(OpenLPMixin, QtWidgets.QApplication):
translate('OpenLP', 'Backup of the data folder failed!'))
return
message = translate('OpenLP',
- 'A backup of the data folder has been created'
- 'at {text}').format(text=data_folder_backup_path)
+ 'A backup of the data folder has been created at:\n\n'
+ '{text}').format(text=data_folder_backup_path)
QtWidgets.QMessageBox.information(None, translate('OpenLP', 'Backup'), message)
# Update the version in the settings
@@ -368,9 +400,13 @@ def main(args=None):
Registry.create()
Registry().register('application', application)
application.setApplicationVersion(get_application_version()['version'])
- # Instance check
+ # Check if an instance of OpenLP is already running. Quit if there is a running instance and the user only wants one
if application.is_already_running():
sys.exit()
+ # If the custom data path is missing and the user wants to restore the data path, quit OpenLP.
+ if application.is_data_path_missing():
+ application.shared_memory.detach()
+ sys.exit()
# Remove/convert obsolete settings.
Settings().remove_obsolete_settings()
# First time checks in settings
diff --git a/openlp/core/common/settings.py b/openlp/core/common/settings.py
index 2caf04dab..8f70fafff 100644
--- a/openlp/core/common/settings.py
+++ b/openlp/core/common/settings.py
@@ -214,7 +214,10 @@ class Settings(QtCore.QSettings):
('media/players', 'media/players_temp', [(media_players_conv, None)]), # Convert phonon to system
('media/players_temp', 'media/players', []), # Move temp setting from above to correct setting
('advanced/default color', 'core/logo background color', []), # Default image renamed + moved to general > 2.4.
- ('advanced/default image', '/core/logo file', []) # Default image renamed + moved to general after 2.4.
+ ('advanced/default image', 'core/logo file', []), # Default image renamed + moved to general after 2.4.
+ ('shortcuts/escapeItem', 'shortcuts/desktopScreenEnable', []), # Escape item was removed in 2.6.
+ ('shortcuts/offlineHelpItem', 'shortcuts/HelpItem', []), # Online and Offline help were combined in 2.6.
+ ('shortcuts/onlineHelpItem', 'shortcuts/HelpItem', []) # Online and Offline help were combined in 2.6.
]
@staticmethod
@@ -261,10 +264,10 @@ class Settings(QtCore.QSettings):
'shortcuts/blankScreen': [QtGui.QKeySequence(QtCore.Qt.Key_Period)],
'shortcuts/collapse': [QtGui.QKeySequence(QtCore.Qt.Key_Minus)],
'shortcuts/desktopScreen': [QtGui.QKeySequence(QtCore.Qt.Key_D)],
+ 'shortcuts/desktopScreenEnable': [QtGui.QKeySequence(QtCore.Qt.Key_Escape)],
'shortcuts/delete': [QtGui.QKeySequence(QtGui.QKeySequence.Delete)],
'shortcuts/down': [QtGui.QKeySequence(QtCore.Qt.Key_Down)],
'shortcuts/editSong': [],
- 'shortcuts/escapeItem': [QtGui.QKeySequence(QtCore.Qt.Key_Escape)],
'shortcuts/expand': [QtGui.QKeySequence(QtCore.Qt.Key_Plus)],
'shortcuts/exportThemeItem': [],
'shortcuts/fileNewItem': [QtGui.QKeySequence(QtGui.QKeySequence.New)],
@@ -273,6 +276,7 @@ class Settings(QtCore.QSettings):
'shortcuts/fileSaveItem': [QtGui.QKeySequence(QtGui.QKeySequence.Save)],
'shortcuts/fileOpenItem': [QtGui.QKeySequence(QtGui.QKeySequence.Open)],
'shortcuts/goLive': [],
+ 'shortcuts/HelpItem': [QtGui.QKeySequence(QtGui.QKeySequence.HelpContents)],
'shortcuts/importThemeItem': [],
'shortcuts/importBibleItem': [],
'shortcuts/listViewBiblesDeleteItem': [QtGui.QKeySequence(QtGui.QKeySequence.Delete)],
@@ -333,8 +337,6 @@ class Settings(QtCore.QSettings):
QtGui.QKeySequence(QtCore.Qt.Key_PageDown)],
'shortcuts/nextService': [QtGui.QKeySequence(QtCore.Qt.Key_Right)],
'shortcuts/newService': [],
- 'shortcuts/offlineHelpItem': [QtGui.QKeySequence(QtGui.QKeySequence.HelpContents)],
- 'shortcuts/onlineHelpItem': [QtGui.QKeySequence(QtGui.QKeySequence.HelpContents)],
'shortcuts/openService': [],
'shortcuts/saveService': [],
'shortcuts/previousItem_live': [QtGui.QKeySequence(QtCore.Qt.Key_Up),
@@ -379,6 +381,7 @@ class Settings(QtCore.QSettings):
'shortcuts/themeScreen': [QtGui.QKeySequence(QtCore.Qt.Key_T)],
'shortcuts/toolsReindexItem': [],
'shortcuts/toolsFindDuplicates': [],
+ 'shortcuts/toolsSongListReport': [],
'shortcuts/toolsAlertItem': [QtGui.QKeySequence(QtCore.Qt.Key_F7)],
'shortcuts/toolsFirstTimeWizard': [],
'shortcuts/toolsOpenDataFolder': [],
diff --git a/openlp/core/common/uistrings.py b/openlp/core/common/uistrings.py
index 25df5ac75..3eb4e254d 100644
--- a/openlp/core/common/uistrings.py
+++ b/openlp/core/common/uistrings.py
@@ -119,6 +119,7 @@ class UiStrings(object):
self.NFSp = translate('OpenLP.Ui', 'No Files Selected', 'Plural')
self.NISs = translate('OpenLP.Ui', 'No Item Selected', 'Singular')
self.NISp = translate('OpenLP.Ui', 'No Items Selected', 'Plural')
+ self.NoResults = translate('OpenLP.Ui', 'No Search Results')
self.OLP = translate('OpenLP.Ui', 'OpenLP')
self.OLPV2 = "{name} {version}".format(name=self.OLP, version="2")
self.OLPV2x = "{name} {version}".format(name=self.OLP, version="2.4")
@@ -149,6 +150,7 @@ class UiStrings(object):
self.Settings = translate('OpenLP.Ui', 'Settings')
self.SaveService = translate('OpenLP.Ui', 'Save Service')
self.Service = translate('OpenLP.Ui', 'Service')
+ self.ShortResults = translate('OpenLP.Ui', 'Please type more text to use \'Search As You Type\'')
self.Split = translate('OpenLP.Ui', 'Optional &Split')
self.SplitToolTip = translate('OpenLP.Ui',
'Split a slide into two only if it does not fit on the screen as one slide.')
diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py
index d00d85b54..49365600d 100644
--- a/openlp/core/lib/__init__.py
+++ b/openlp/core/lib/__init__.py
@@ -129,16 +129,16 @@ def build_icon(icon):
location like ``/path/to/file.png``. However, the **recommended** way is to specify a resource string.
:return: The build icon.
"""
- button_icon = QtGui.QIcon()
if isinstance(icon, QtGui.QIcon):
- button_icon = icon
- elif isinstance(icon, str):
- if icon.startswith(':/'):
- button_icon.addPixmap(QtGui.QPixmap(icon), QtGui.QIcon.Normal, QtGui.QIcon.Off)
- else:
- button_icon.addPixmap(QtGui.QPixmap.fromImage(QtGui.QImage(icon)), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+ return icon
+ pix_map = None
+ button_icon = QtGui.QIcon()
+ if isinstance(icon, str):
+ pix_map = QtGui.QPixmap(icon)
elif isinstance(icon, QtGui.QImage):
- button_icon.addPixmap(QtGui.QPixmap.fromImage(icon), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+ pix_map = QtGui.QPixmap.fromImage(icon)
+ if pix_map:
+ button_icon.addPixmap(pix_map, QtGui.QIcon.Normal, QtGui.QIcon.Off)
return button_icon
@@ -310,30 +310,23 @@ def expand_tags(text):
def create_separated_list(string_list):
"""
- Returns a string that represents a join of a list of strings with a localized separator. This function corresponds
+ Returns a string that represents a join of a list of strings with a localized separator.
+ Localized separation will be done via the translate() function by the translators.
- to QLocale::createSeparatedList which was introduced in Qt 4.8 and implements the algorithm from
- http://www.unicode.org/reports/tr35/#ListPatterns
-
- :param string_list: List of unicode strings
+ :param string_list: List of unicode strings
+ :return: Formatted string
"""
- if LooseVersion(Qt.PYQT_VERSION_STR) >= LooseVersion('4.9') and LooseVersion(Qt.qVersion()) >= LooseVersion('4.8'):
- return QtCore.QLocale().createSeparatedList(string_list)
- if not string_list:
- return ''
- elif len(string_list) == 1:
- return string_list[0]
- # TODO: Verify mocking of translate() test before conversion
- elif len(string_list) == 2:
- return translate('OpenLP.core.lib', '%s and %s',
- 'Locale list separator: 2 items') % (string_list[0], string_list[1])
+ list_length = len(string_list)
+ if list_length == 1:
+ list_to_string = string_list[0]
+ elif list_length == 2:
+ list_to_string = translate('OpenLP.core.lib', '{one} and {two}').format(one=string_list[0], two=string_list[1])
+ elif list_length > 2:
+ list_to_string = translate('OpenLP.core.lib', '{first} and {last}').format(first=', '.join(string_list[:-1]),
+ last=string_list[-1])
else:
- merged = translate('OpenLP.core.lib', '%s, and %s',
- 'Locale list separator: end') % (string_list[-2], string_list[-1])
- for index in reversed(list(range(1, len(string_list) - 2))):
- merged = translate('OpenLP.core.lib', '%s, %s',
- 'Locale list separator: middle') % (string_list[index], merged)
- return translate('OpenLP.core.lib', '%s, %s', 'Locale list separator: start') % (string_list[0], merged)
+ list_to_string = ''
+ return list_to_string
from .exceptions import ValidationError
diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py
index 2edea93cf..25de144ad 100644
--- a/openlp/core/lib/mediamanageritem.py
+++ b/openlp/core/lib/mediamanageritem.py
@@ -266,7 +266,7 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties):
self.search_text_layout.setObjectName('search_text_layout')
self.search_text_label = QtWidgets.QLabel(self.search_widget)
self.search_text_label.setObjectName('search_text_label')
- self.search_text_edit = SearchEdit(self.search_widget)
+ self.search_text_edit = SearchEdit(self.search_widget, self.settings_section)
self.search_text_edit.setObjectName('search_text_edit')
self.search_text_label.setBuddy(self.search_text_edit)
self.search_text_layout.addRow(self.search_text_label, self.search_text_edit)
@@ -397,8 +397,6 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties):
# Decide if we have to show the context menu or not.
if item is None:
return
- if not item.flags() & QtCore.Qt.ItemIsSelectable:
- return
self.menu.exec(self.list_view.mapToGlobal(point))
def get_file_list(self):
@@ -638,34 +636,6 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties):
"""
return item
- def check_search_result(self):
- """
- Checks if the list_view is empty and adds a "No Search Results" item.
- """
- if self.list_view.count():
- return
- message = translate('OpenLP.MediaManagerItem', 'No Search Results')
- item = QtWidgets.QListWidgetItem(message)
- item.setFlags(QtCore.Qt.NoItemFlags)
- font = QtGui.QFont()
- font.setItalic(True)
- item.setFont(font)
- self.list_view.addItem(item)
-
- def check_search_result_search_while_typing_short(self):
- """
- This is used in Bible "Search while typing" if the search is shorter than the min required len.
- """
- if self.list_view.count():
- return
- message = translate('OpenLP.MediaManagerItem', 'Search is too short to be used in: "Search while typing"')
- item = QtWidgets.QListWidgetItem(message)
- item.setFlags(QtCore.Qt.NoItemFlags)
- font = QtGui.QFont()
- font.setItalic(True)
- item.setFont(font)
- self.list_view.addItem(item)
-
def _get_id_of_item_to_generate(self, item, remote_item):
"""
Utility method to check items being submitted for slide generation.
diff --git a/openlp/core/lib/searchedit.py b/openlp/core/lib/searchedit.py
index 41e54957d..1fcc0e618 100644
--- a/openlp/core/lib/searchedit.py
+++ b/openlp/core/lib/searchedit.py
@@ -26,6 +26,7 @@ from PyQt5 import QtCore, QtWidgets
from openlp.core.lib import build_icon
from openlp.core.lib.ui import create_widget_action
+from openlp.core.common import Settings
log = logging.getLogger(__name__)
@@ -37,11 +38,12 @@ class SearchEdit(QtWidgets.QLineEdit):
searchTypeChanged = QtCore.pyqtSignal(QtCore.QVariant)
cleared = QtCore.pyqtSignal()
- def __init__(self, parent):
+ def __init__(self, parent, settings_section):
"""
Constructor.
"""
- super(SearchEdit, self).__init__(parent)
+ super().__init__(parent)
+ self.settings_section = settings_section
self._current_search_type = -1
self.clear_button = QtWidgets.QToolButton(self)
self.clear_button.setIcon(build_icon(':/system/clear_shortcut.png'))
@@ -100,14 +102,10 @@ class SearchEdit(QtWidgets.QLineEdit):
menu = self.menu_button.menu()
for action in menu.actions():
if identifier == action.data():
- # setPlaceholderText has been implemented in Qt 4.7 and in at least PyQt 4.9 (I am not sure, if it was
- # implemented in PyQt 4.8).
- try:
- self.setPlaceholderText(action.placeholder_text)
- except AttributeError:
- pass
+ self.setPlaceholderText(action.placeholder_text)
self.menu_button.setDefaultAction(action)
self._current_search_type = identifier
+ Settings().setValue('{section}/last search type'.format(section=self.settings_section), identifier)
self.searchTypeChanged.emit(identifier)
return True
@@ -130,14 +128,10 @@ class SearchEdit(QtWidgets.QLineEdit):
(2, ":/songs/authors.png", "Authors", "Search Authors...")
"""
menu = QtWidgets.QMenu(self)
- first = None
for identifier, icon, title, placeholder in items:
action = create_widget_action(
menu, text=title, icon=icon, data=identifier, triggers=self._on_menu_action_triggered)
action.placeholder_text = placeholder
- if first is None:
- first = action
- self._current_search_type = identifier
if not hasattr(self, 'menu_button'):
self.menu_button = QtWidgets.QToolButton(self)
self.menu_button.setIcon(build_icon(':/system/clear_shortcut.png'))
@@ -146,7 +140,8 @@ class SearchEdit(QtWidgets.QLineEdit):
self.menu_button.setStyleSheet('QToolButton { border: none; padding: 0px 10px 0px 0px; }')
self.menu_button.resize(QtCore.QSize(28, 18))
self.menu_button.setMenu(menu)
- self.menu_button.setDefaultAction(first)
+ self.set_current_search_type(
+ Settings().value('{section}/last search type'.format(section=self.settings_section)))
self.menu_button.show()
self._update_style_sheet()
diff --git a/openlp/core/ui/advancedtab.py b/openlp/core/ui/advancedtab.py
index ca91e882a..fed712ed2 100644
--- a/openlp/core/ui/advancedtab.py
+++ b/openlp/core/ui/advancedtab.py
@@ -397,27 +397,6 @@ class AdvancedTab(SettingsTab):
self.data_directory_cancel_button.hide()
# Since data location can be changed, make sure the path is present.
self.current_data_path = AppLocation.get_data_path()
- if not os.path.exists(self.current_data_path):
- log.error('Data path not found {path}'.format(path=self.current_data_path))
- answer = QtWidgets.QMessageBox.critical(
- self, translate('OpenLP.AdvancedTab', 'Data Directory Error'),
- translate('OpenLP.AdvancedTab', 'OpenLP data directory was not found\n\n{path}\n\n'
- 'This data directory was previously changed from the OpenLP '
- 'default location. If the new location was on removable '
- 'media, that media needs to be made available.\n\n'
- 'Click "No" to stop loading OpenLP. allowing you to fix the the problem.\n\n'
- 'Click "Yes" to reset the data directory to the default '
- 'location.').format(path=self.current_data_path),
- QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No),
- QtWidgets.QMessageBox.No)
- if answer == QtWidgets.QMessageBox.No:
- log.info('User requested termination')
- self.main_window.clean_up()
- sys.exit()
- # Set data location to default.
- settings.remove('advanced/data path')
- self.current_data_path = AppLocation.get_data_path()
- log.warning('User requested data path set to default {path}'.format(path=self.current_data_path))
self.data_directory_label.setText(os.path.abspath(self.current_data_path))
# Don't allow data directory move if running portable.
if settings.value('advanced/is portable'):
diff --git a/openlp/core/ui/exceptiondialog.py b/openlp/core/ui/exceptiondialog.py
index 7341287c7..36e039cee 100644
--- a/openlp/core/ui/exceptiondialog.py
+++ b/openlp/core/ui/exceptiondialog.py
@@ -38,7 +38,7 @@ class Ui_ExceptionDialog(object):
Set up the UI.
"""
exception_dialog.setObjectName('exception_dialog')
- exception_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
+ exception_dialog.setWindowIcon(build_icon(':/icon/openlp-logo.svg'))
self.exception_layout = QtWidgets.QVBoxLayout(exception_dialog)
self.exception_layout.setObjectName('exception_layout')
self.message_layout = QtWidgets.QHBoxLayout()
@@ -97,9 +97,9 @@ class Ui_ExceptionDialog(object):
translate('OpenLP.ExceptionDialog', 'Please describe what you were trying to do. '
' If possible, write in English.'))
exception_part1 = (translate('OpenLP.ExceptionDialog',
- 'Oops, OpenLP hit a problem and couldn\'t recover!
'
- 'You can help the OpenLP developers to fix this'
- ' by sending them a bug report to {email}{newlines}'
+ 'Oops, OpenLP hit a problem and couldn\'t recover!
'
+ 'You can help the OpenLP developers to fix this'
+ ' by sending them a bug report to {email}{newlines}'
).format(email=' bugs@openlp.org',
newlines='
'))
self.message_label.setText(
@@ -107,7 +107,7 @@ class Ui_ExceptionDialog(object):
'No email app? You can save this '
'information to a file and '
'send it from your mail on browser via an attachment.
'
- 'Thank you for being part of making OpenLP better! '
+ 'Thank you for being part of making OpenLP better! '
).format(first_part=exception_part1))
self.send_report_button.setText(translate('OpenLP.ExceptionDialog', 'Send E-Mail'))
self.save_report_button.setText(translate('OpenLP.ExceptionDialog', 'Save to File'))
diff --git a/openlp/core/ui/exceptionform.py b/openlp/core/ui/exceptionform.py
index 2122acfaa..9e58ac8b2 100644
--- a/openlp/core/ui/exceptionform.py
+++ b/openlp/core/ui/exceptionform.py
@@ -208,7 +208,7 @@ class ExceptionForm(QtWidgets.QDialog, Ui_ExceptionDialog, RegistryProperties):
self.__button_state(False)
self.description_word_count.setText(
translate('OpenLP.ExceptionDialog', 'Please enter a more detailed description of the situation'
- ))
+ ''))
def on_attach_file_button_clicked(self):
"""
diff --git a/openlp/core/ui/filerenamedialog.py b/openlp/core/ui/filerenamedialog.py
index ede60bcfb..cc4574279 100644
--- a/openlp/core/ui/filerenamedialog.py
+++ b/openlp/core/ui/filerenamedialog.py
@@ -37,7 +37,7 @@ class Ui_FileRenameDialog(object):
Set up the UI
"""
file_rename_dialog.setObjectName('file_rename_dialog')
- file_rename_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
+ file_rename_dialog.setWindowIcon(build_icon(':/icon/openlp-logo.svg'))
file_rename_dialog.resize(300, 10)
self.dialog_layout = QtWidgets.QGridLayout(file_rename_dialog)
self.dialog_layout.setObjectName('dialog_layout')
diff --git a/openlp/core/ui/firsttimelanguagedialog.py b/openlp/core/ui/firsttimelanguagedialog.py
index dd1389ba2..f32ec9076 100644
--- a/openlp/core/ui/firsttimelanguagedialog.py
+++ b/openlp/core/ui/firsttimelanguagedialog.py
@@ -38,7 +38,7 @@ class Ui_FirstTimeLanguageDialog(object):
Set up the UI.
"""
language_dialog.setObjectName('language_dialog')
- language_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
+ language_dialog.setWindowIcon(build_icon(':/icon/openlp-logo.svg'))
language_dialog.resize(300, 50)
self.dialog_layout = QtWidgets.QVBoxLayout(language_dialog)
self.dialog_layout.setContentsMargins(8, 8, 8, 8)
diff --git a/openlp/core/ui/firsttimewizard.py b/openlp/core/ui/firsttimewizard.py
index 7b93c1680..63937ebb2 100644
--- a/openlp/core/ui/firsttimewizard.py
+++ b/openlp/core/ui/firsttimewizard.py
@@ -55,7 +55,7 @@ class UiFirstTimeWizard(object):
:param first_time_wizard: The wizard form
"""
first_time_wizard.setObjectName('first_time_wizard')
- first_time_wizard.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
+ first_time_wizard.setWindowIcon(build_icon(':/icon/openlp-logo.svg'))
first_time_wizard.resize(550, 386)
first_time_wizard.setModal(True)
first_time_wizard.setOptions(QtWidgets.QWizard.IndependentPages | QtWidgets.QWizard.NoBackButtonOnStartPage |
diff --git a/openlp/core/ui/formattingtagdialog.py b/openlp/core/ui/formattingtagdialog.py
index 19412921d..83ec18115 100644
--- a/openlp/core/ui/formattingtagdialog.py
+++ b/openlp/core/ui/formattingtagdialog.py
@@ -38,7 +38,7 @@ class Ui_FormattingTagDialog(object):
Set up the UI
"""
formatting_tag_dialog.setObjectName('formatting_tag_dialog')
- formatting_tag_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
+ formatting_tag_dialog.setWindowIcon(build_icon(':/icon/openlp-logo.svg'))
formatting_tag_dialog.resize(725, 548)
self.list_data_grid_layout = QtWidgets.QVBoxLayout(formatting_tag_dialog)
self.list_data_grid_layout.setContentsMargins(8, 8, 8, 8)
diff --git a/openlp/core/ui/generaltab.py b/openlp/core/ui/generaltab.py
index 463aad73f..629e55e0f 100644
--- a/openlp/core/ui/generaltab.py
+++ b/openlp/core/ui/generaltab.py
@@ -44,7 +44,7 @@ class GeneralTab(SettingsTab):
self.logo_file = ':/graphics/openlp-splash-screen.png'
self.logo_background_color = '#ffffff'
self.screens = ScreenList()
- self.icon_path = ':/icon/openlp-logo-16x16.png'
+ self.icon_path = ':/icon/openlp-logo.svg'
general_translated = translate('OpenLP.GeneralTab', 'General')
super(GeneralTab, self).__init__(parent, 'Core', general_translated)
diff --git a/openlp/core/ui/lib/listwidgetwithdnd.py b/openlp/core/ui/lib/listwidgetwithdnd.py
index 43ec741d0..de601fa13 100644
--- a/openlp/core/ui/lib/listwidgetwithdnd.py
+++ b/openlp/core/ui/lib/listwidgetwithdnd.py
@@ -26,7 +26,7 @@ import os
from PyQt5 import QtCore, QtGui, QtWidgets
-from openlp.core.common import Registry
+from openlp.core.common import Registry, UiStrings
class ListWidgetWithDnD(QtWidgets.QListWidget):
@@ -37,8 +37,9 @@ class ListWidgetWithDnD(QtWidgets.QListWidget):
"""
Initialise the list widget
"""
- super(ListWidgetWithDnD, self).__init__(parent)
+ super().__init__(parent)
self.mime_data_text = name
+ self.no_results_text = UiStrings().NoResults
def activateDnD(self):
"""
@@ -48,6 +49,19 @@ class ListWidgetWithDnD(QtWidgets.QListWidget):
self.setDragDropMode(QtWidgets.QAbstractItemView.DragDrop)
Registry().register_function(('%s_dnd' % self.mime_data_text), self.parent().load_file)
+ def clear(self, search_while_typing=False):
+ """
+ Re-implement clear, so that we can customise feedback when using 'Search as you type'
+
+ :param search_while_typing: True if we want to display the customised message
+ :return: None
+ """
+ if search_while_typing:
+ self.no_results_text = UiStrings().ShortResults
+ else:
+ self.no_results_text = UiStrings().NoResults
+ super().clear()
+
def mouseMoveEvent(self, event):
"""
Drag and drop event does not care what data is selected as the recipient will use events to request the data
@@ -102,6 +116,24 @@ class ListWidgetWithDnD(QtWidgets.QListWidget):
listing = os.listdir(local_file)
for file in listing:
files.append(os.path.join(local_file, file))
- Registry().execute('%s_dnd' % self.mime_data_text, {'files': files, 'target': self.itemAt(event.pos())})
+ Registry().execute('{mime_data}_dnd'.format(mime_data=self.mime_data_text),
+ {'files': files, 'target': self.itemAt(event.pos())})
else:
event.ignore()
+
+ def paintEvent(self, event):
+ """
+ Re-implement paintEvent so that we can add 'No Results' text when the listWidget is empty.
+
+ :param event: A QPaintEvent
+ :return: None
+ """
+ super().paintEvent(event)
+ if not self.count():
+ viewport = self.viewport()
+ painter = QtGui.QPainter(viewport)
+ font = QtGui.QFont()
+ font.setItalic(True)
+ painter.setFont(font)
+ painter.drawText(QtCore.QRect(0, 0, viewport.width(), viewport.height()),
+ (QtCore.Qt.AlignHCenter | QtCore.Qt.TextWordWrap), self.no_results_text)
diff --git a/openlp/core/ui/lib/treewidgetwithdnd.py b/openlp/core/ui/lib/treewidgetwithdnd.py
index c49fc144e..f410e453a 100644
--- a/openlp/core/ui/lib/treewidgetwithdnd.py
+++ b/openlp/core/ui/lib/treewidgetwithdnd.py
@@ -26,7 +26,7 @@ import os
from PyQt5 import QtCore, QtGui, QtWidgets
-from openlp.core.common import Registry
+from openlp.core.common import Registry, is_win
class TreeWidgetWithDnD(QtWidgets.QTreeWidget):
@@ -108,6 +108,11 @@ class TreeWidgetWithDnD(QtWidgets.QTreeWidget):
:param event: Handle of the event pint passed
"""
+ # If we are on Windows, OpenLP window will not be set on top. For example, user can drag images to Library and
+ # the folder stays on top of the group creation box. This piece of code fixes this issue.
+ if is_win():
+ self.setWindowState(self.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive)
+ self.setWindowState(QtCore.Qt.WindowNoState)
if event.mimeData().hasUrls():
event.setDropAction(QtCore.Qt.CopyAction)
event.accept()
diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py
index 92b29d16f..b8bd126dd 100644
--- a/openlp/core/ui/mainwindow.py
+++ b/openlp/core/ui/mainwindow.py
@@ -309,21 +309,13 @@ class Ui_MainWindow(object):
self.about_item.setMenuRole(QtWidgets.QAction.AboutRole)
if is_win():
self.local_help_file = os.path.join(AppLocation.get_directory(AppLocation.AppDir), 'OpenLP.chm')
- self.offline_help_item = create_action(main_window, 'offlineHelpItem',
- icon=':/system/system_help_contents.png',
- can_shortcuts=True,
- category=UiStrings().Help, triggers=self.on_offline_help_clicked)
elif is_macosx():
self.local_help_file = os.path.join(AppLocation.get_directory(AppLocation.AppDir),
'..', 'Resources', 'OpenLP.help')
- self.offline_help_item = create_action(main_window, 'offlineHelpItem',
- icon=':/system/system_help_contents.png',
- can_shortcuts=True,
- category=UiStrings().Help, triggers=self.on_offline_help_clicked)
- self.on_line_help_item = create_action(main_window, 'onlineHelpItem',
- icon=':/system/system_online_help.png',
- can_shortcuts=True,
- category=UiStrings().Help, triggers=self.on_online_help_clicked)
+ self.on_help_item = create_action(main_window, 'HelpItem',
+ icon=':/system/system_help_contents.png',
+ can_shortcuts=True,
+ category=UiStrings().Help, triggers=self.on_help_clicked)
self.web_site_item = create_action(main_window, 'webSiteItem', can_shortcuts=True, category=UiStrings().Help)
# Shortcuts not connected to buttons or menu entries.
self.search_shortcut_action = create_action(main_window,
@@ -362,11 +354,7 @@ class Ui_MainWindow(object):
add_actions(self.tools_menu, (self.tools_open_data_folder, None))
add_actions(self.tools_menu, (self.tools_first_time_wizard, None))
add_actions(self.tools_menu, [self.update_theme_images])
- if (is_win() or is_macosx()) and (hasattr(sys, 'frozen') and sys.frozen == 1):
- add_actions(self.help_menu, (self.offline_help_item, self.on_line_help_item, None, self.web_site_item,
- self.about_item))
- else:
- add_actions(self.help_menu, (self.on_line_help_item, None, self.web_site_item, self.about_item))
+ add_actions(self.help_menu, (self.on_help_item, None, self.web_site_item, self.about_item))
add_actions(self.menu_bar, (self.file_menu.menuAction(), self.view_menu.menuAction(),
self.tools_menu.menuAction(), self.settings_menu.menuAction(), self.help_menu.menuAction()))
add_actions(self, [self.search_shortcut_action])
@@ -462,9 +450,7 @@ class Ui_MainWindow(object):
'from here.'))
self.about_item.setText(translate('OpenLP.MainWindow', '&About'))
self.about_item.setStatusTip(translate('OpenLP.MainWindow', 'More information about OpenLP.'))
- if is_win() or is_macosx():
- self.offline_help_item.setText(translate('OpenLP.MainWindow', '&User Guide'))
- self.on_line_help_item.setText(translate('OpenLP.MainWindow', '&Online Help'))
+ self.on_help_item.setText(translate('OpenLP.MainWindow', '&User Manual'))
self.search_shortcut_action.setText(UiStrings().Search)
self.search_shortcut_action.setToolTip(
translate('OpenLP.MainWindow', 'Jump to the search box of the current active plugin.'))
@@ -778,18 +764,16 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
import webbrowser
webbrowser.open_new('http://openlp.org/')
- def on_offline_help_clicked(self):
+ def on_help_clicked(self):
"""
- Load the local OpenLP help file
+ If is_macosx or is_win, open the local OpenLP help file.
+ Use the Online manual in other cases. (Linux)
"""
- QtGui.QDesktopServices.openUrl(QtCore.QUrl("file:///" + self.local_help_file))
-
- def on_online_help_clicked(self):
- """
- Load the online OpenLP manual
- """
- import webbrowser
- webbrowser.open_new('http://manual.openlp.org/')
+ if is_macosx() or is_win():
+ QtGui.QDesktopServices.openUrl(QtCore.QUrl("file:///" + self.local_help_file))
+ else:
+ import webbrowser
+ webbrowser.open_new('http://manual.openlp.org/')
def on_about_item_clicked(self):
"""
diff --git a/openlp/core/ui/plugindialog.py b/openlp/core/ui/plugindialog.py
index ad461cd44..cd41d77a8 100644
--- a/openlp/core/ui/plugindialog.py
+++ b/openlp/core/ui/plugindialog.py
@@ -38,7 +38,7 @@ class Ui_PluginViewDialog(object):
Set up the UI
"""
plugin_view_dialog.setObjectName('plugin_view_dialog')
- plugin_view_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
+ plugin_view_dialog.setWindowIcon(build_icon(':/icon/openlp-logo.svg'))
plugin_view_dialog.setWindowModality(QtCore.Qt.ApplicationModal)
self.plugin_layout = QtWidgets.QVBoxLayout(plugin_view_dialog)
self.plugin_layout.setObjectName('plugin_layout')
diff --git a/openlp/core/ui/printservicedialog.py b/openlp/core/ui/printservicedialog.py
index c658d1496..f82bfecd4 100644
--- a/openlp/core/ui/printservicedialog.py
+++ b/openlp/core/ui/printservicedialog.py
@@ -50,7 +50,7 @@ class Ui_PrintServiceDialog(object):
Set up the UI
"""
print_service_dialog.setObjectName('print_service_dialog')
- print_service_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
+ print_service_dialog.setWindowIcon(build_icon(':/icon/openlp-logo.svg'))
print_service_dialog.resize(664, 594)
self.main_layout = QtWidgets.QVBoxLayout(print_service_dialog)
self.main_layout.setSpacing(0)
diff --git a/openlp/core/ui/projector/editform.py b/openlp/core/ui/projector/editform.py
index f4cf8a774..f15d5b550 100644
--- a/openlp/core/ui/projector/editform.py
+++ b/openlp/core/ui/projector/editform.py
@@ -47,7 +47,7 @@ class Ui_ProjectorEditForm(object):
Create the interface layout.
"""
edit_projector_dialog.setObjectName('edit_projector_dialog')
- edit_projector_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo-32x32.png'))
+ edit_projector_dialog.setWindowIcon(build_icon(':/icon/openlp-logo.svg'))
edit_projector_dialog.setMinimumWidth(400)
edit_projector_dialog.setModal(True)
# Define the basic layout
diff --git a/openlp/core/ui/serviceitemeditdialog.py b/openlp/core/ui/serviceitemeditdialog.py
index bbff73f3d..6dc45a847 100644
--- a/openlp/core/ui/serviceitemeditdialog.py
+++ b/openlp/core/ui/serviceitemeditdialog.py
@@ -38,7 +38,7 @@ class Ui_ServiceItemEditDialog(object):
Set up the UI
"""
serviceItemEditDialog.setObjectName('serviceItemEditDialog')
- serviceItemEditDialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
+ serviceItemEditDialog.setWindowIcon(build_icon(':/icon/openlp-logo.svg'))
self.dialog_layout = QtWidgets.QGridLayout(serviceItemEditDialog)
self.dialog_layout.setContentsMargins(8, 8, 8, 8)
self.dialog_layout.setSpacing(8)
diff --git a/openlp/core/ui/settingsdialog.py b/openlp/core/ui/settingsdialog.py
index 3b6c4bc00..97c4c7233 100644
--- a/openlp/core/ui/settingsdialog.py
+++ b/openlp/core/ui/settingsdialog.py
@@ -38,7 +38,7 @@ class Ui_SettingsDialog(object):
Set up the UI
"""
settings_dialog.setObjectName('settings_dialog')
- settings_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
+ settings_dialog.setWindowIcon(build_icon(':/icon/openlp-logo.svg'))
settings_dialog.resize(800, 700)
self.dialog_layout = QtWidgets.QGridLayout(settings_dialog)
self.dialog_layout.setObjectName('dialog_layout')
diff --git a/openlp/core/ui/shortcutlistdialog.py b/openlp/core/ui/shortcutlistdialog.py
index ed1156025..4de2d4861 100644
--- a/openlp/core/ui/shortcutlistdialog.py
+++ b/openlp/core/ui/shortcutlistdialog.py
@@ -72,7 +72,7 @@ class Ui_ShortcutListDialog(object):
Set up the UI
"""
shortcutListDialog.setObjectName('shortcutListDialog')
- shortcutListDialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
+ shortcutListDialog.setWindowIcon(build_icon(':/icon/openlp-logo.svg'))
shortcutListDialog.resize(500, 438)
self.shortcut_list_layout = QtWidgets.QVBoxLayout(shortcutListDialog)
self.shortcut_list_layout.setObjectName('shortcut_list_layout')
diff --git a/openlp/core/ui/shortcutlistform.py b/openlp/core/ui/shortcutlistform.py
index 1266d1cc4..eb91313c9 100644
--- a/openlp/core/ui/shortcutlistform.py
+++ b/openlp/core/ui/shortcutlistform.py
@@ -426,11 +426,11 @@ class ShortcutListForm(QtWidgets.QDialog, Ui_ShortcutListDialog, RegistryPropert
is_valid = False
if not is_valid:
text = translate('OpenLP.ShortcutListDialog',
- 'The shortcut "{key}" is already assigned to another action, please'
- ' use a different shortcut.'
+ 'The shortcut "{key}" is already assigned to another action,\n'
+ 'please use a different shortcut.'
).format(key=self.get_shortcut_string(key_sequence))
self.main_window.warning_message(translate('OpenLP.ShortcutListDialog', 'Duplicate Shortcut'),
- text, for_display=True)
+ text)
self.dialog_was_shown = True
return is_valid
diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py
index 7121e5227..01bc2bd0f 100644
--- a/openlp/core/ui/slidecontroller.py
+++ b/openlp/core/ui/slidecontroller.py
@@ -234,25 +234,32 @@ class SlideController(DisplayController, RegistryProperties):
self.hide_menu.setPopupMode(QtWidgets.QToolButton.MenuButtonPopup)
self.hide_menu.setMenu(QtWidgets.QMenu(translate('OpenLP.SlideController', 'Hide'), self.toolbar))
self.toolbar.add_toolbar_widget(self.hide_menu)
- self.blank_screen = create_action(self, 'blankScreen',
- text=translate('OpenLP.SlideController', 'Blank Screen'),
- icon=':/slides/slide_blank.png',
- checked=False, can_shortcuts=True, category=self.category,
- triggers=self.on_blank_display)
- self.theme_screen = create_action(self, 'themeScreen',
- text=translate('OpenLP.SlideController', 'Blank to Theme'),
- icon=':/slides/slide_theme.png',
- checked=False, can_shortcuts=True, category=self.category,
- triggers=self.on_theme_display)
+ # The order of the blank to modes in Shortcuts list comes from here.
+ self.desktop_screen_enable = create_action(self, 'desktopScreenEnable',
+ text=translate('OpenLP.SlideController', 'Show Desktop'),
+ icon=':/slides/slide_desktop.png', can_shortcuts=True,
+ context=QtCore.Qt.WidgetWithChildrenShortcut,
+ category=self.category, triggers=self.on_hide_display_enable)
self.desktop_screen = create_action(self, 'desktopScreen',
- text=translate('OpenLP.SlideController', 'Show Desktop'),
+ text=translate('OpenLP.SlideController', 'Toggle Desktop'),
icon=':/slides/slide_desktop.png',
checked=False, can_shortcuts=True, category=self.category,
triggers=self.on_hide_display)
+ self.theme_screen = create_action(self, 'themeScreen',
+ text=translate('OpenLP.SlideController', 'Toggle Blank to Theme'),
+ icon=':/slides/slide_theme.png',
+ checked=False, can_shortcuts=True, category=self.category,
+ triggers=self.on_theme_display)
+ self.blank_screen = create_action(self, 'blankScreen',
+ text=translate('OpenLP.SlideController', 'Toggle Blank Screen'),
+ icon=':/slides/slide_blank.png',
+ checked=False, can_shortcuts=True, category=self.category,
+ triggers=self.on_blank_display)
self.hide_menu.setDefaultAction(self.blank_screen)
self.hide_menu.menu().addAction(self.blank_screen)
self.hide_menu.menu().addAction(self.theme_screen)
self.hide_menu.menu().addAction(self.desktop_screen)
+ self.hide_menu.menu().addAction(self.desktop_screen_enable)
# Wide menu of display control buttons.
self.blank_screen_button = QtWidgets.QToolButton(self.toolbar)
self.blank_screen_button.setObjectName('blank_screen_button')
@@ -512,23 +519,6 @@ class SlideController(DisplayController, RegistryProperties):
can_shortcuts=True, context=QtCore.Qt.WidgetWithChildrenShortcut,
category=self.category,
triggers=self.service_next)
- self.escape_item = create_action(parent, 'escapeItem',
- text=translate('OpenLP.SlideController', 'Escape Item'),
- can_shortcuts=True, context=QtCore.Qt.WidgetWithChildrenShortcut,
- category=self.category,
- triggers=self.live_escape)
-
- def live_escape(self, field=None):
- """
- If you press ESC on the live screen it should close the display temporarily.
- """
- self.display.setVisible(False)
- self.media_controller.media_stop(self)
- # Stop looping if active
- if self.play_slides_loop.isChecked():
- self.on_play_slides_loop(False)
- elif self.play_slides_once.isChecked():
- self.on_play_slides_once(False)
def toggle_display(self, action):
"""
@@ -622,7 +612,7 @@ class SlideController(DisplayController, RegistryProperties):
widget.addActions([
self.previous_item, self.next_item,
self.previous_service, self.next_service,
- self.escape_item,
+ self.desktop_screen_enable,
self.desktop_screen,
self.theme_screen,
self.blank_screen])
@@ -732,8 +722,10 @@ class SlideController(DisplayController, RegistryProperties):
# Reset the button
self.play_slides_once.setChecked(False)
self.play_slides_once.setIcon(build_icon(':/media/media_time.png'))
+ self.play_slides_once.setText(UiStrings().PlaySlidesToEnd)
self.play_slides_loop.setChecked(False)
self.play_slides_loop.setIcon(build_icon(':/media/media_time.png'))
+ self.play_slides_loop.setText(UiStrings().PlaySlidesInLoop)
if item.is_text():
if (Settings().value(self.main_window.songs_settings_section + '/display songbar') and
not self.song_menu.menu().isEmpty()):
@@ -965,7 +957,7 @@ class SlideController(DisplayController, RegistryProperties):
else:
Registry().execute('live_display_show')
else:
- self.live_escape()
+ self.on_hide_display_enable()
def on_slide_blank(self):
"""
@@ -1025,6 +1017,7 @@ class SlideController(DisplayController, RegistryProperties):
def on_hide_display(self, checked=None):
"""
Handle the Hide screen button
+ This toggles the desktop screen.
:param checked: the new state of the of the widget
"""
@@ -1043,6 +1036,20 @@ class SlideController(DisplayController, RegistryProperties):
self.update_preview()
self.on_toggle_loop()
+ def on_hide_display_enable(self, checked=None):
+ """
+ Handle the on_hide_display_enable
+ This only enables the desktop screen.
+
+ :param checked: the new state of the of the widget
+ """
+ self.blank_screen.setChecked(False)
+ self.theme_screen.setChecked(False)
+ Registry().execute('live_display_hide', HideMode.Screen)
+ self.desktop_screen.setChecked(True)
+ self.update_preview()
+ self.on_toggle_loop()
+
def blank_plugin(self):
"""
Blank/Hide the display screen within a plugin if required.
diff --git a/openlp/core/ui/starttimedialog.py b/openlp/core/ui/starttimedialog.py
index 42287596c..e20d625d0 100644
--- a/openlp/core/ui/starttimedialog.py
+++ b/openlp/core/ui/starttimedialog.py
@@ -38,7 +38,7 @@ class Ui_StartTimeDialog(object):
Set up the UI
"""
StartTimeDialog.setObjectName('StartTimeDialog')
- StartTimeDialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
+ StartTimeDialog.setWindowIcon(build_icon(':/icon/openlp-logo.svg'))
StartTimeDialog.resize(350, 10)
self.dialog_layout = QtWidgets.QGridLayout(StartTimeDialog)
self.dialog_layout.setObjectName('dialog_layout')
diff --git a/openlp/core/ui/themelayoutdialog.py b/openlp/core/ui/themelayoutdialog.py
index 5289e1cc2..119611f33 100644
--- a/openlp/core/ui/themelayoutdialog.py
+++ b/openlp/core/ui/themelayoutdialog.py
@@ -38,7 +38,7 @@ class Ui_ThemeLayoutDialog(object):
Set up the UI
"""
themeLayoutDialog.setObjectName('themeLayoutDialogDialog')
- themeLayoutDialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
+ themeLayoutDialog.setWindowIcon(build_icon(':/icon/openlp-logo.svg'))
self.preview_layout = QtWidgets.QVBoxLayout(themeLayoutDialog)
self.preview_layout.setObjectName('preview_layout')
self.preview_area = QtWidgets.QWidget(themeLayoutDialog)
diff --git a/openlp/core/ui/themewizard.py b/openlp/core/ui/themewizard.py
index 427e9836b..95262cf8f 100644
--- a/openlp/core/ui/themewizard.py
+++ b/openlp/core/ui/themewizard.py
@@ -40,7 +40,7 @@ class Ui_ThemeWizard(object):
Set up the UI
"""
theme_wizard.setObjectName('OpenLP.ThemeWizard')
- theme_wizard.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
+ theme_wizard.setWindowIcon(build_icon(':/icon/openlp-logo.svg'))
theme_wizard.setModal(True)
theme_wizard.setOptions(QtWidgets.QWizard.IndependentPages |
QtWidgets.QWizard.NoBackButtonOnStartPage | QtWidgets.QWizard.HaveCustomButton1)
diff --git a/openlp/plugins/alerts/forms/alertdialog.py b/openlp/plugins/alerts/forms/alertdialog.py
index 81af23ae1..83df7a09c 100644
--- a/openlp/plugins/alerts/forms/alertdialog.py
+++ b/openlp/plugins/alerts/forms/alertdialog.py
@@ -39,7 +39,7 @@ class Ui_AlertDialog(object):
"""
alert_dialog.setObjectName('alert_dialog')
alert_dialog.resize(400, 300)
- alert_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
+ alert_dialog.setWindowIcon(build_icon(':/icon/openlp-logo.svg'))
self.alert_dialog_layout = QtWidgets.QGridLayout(alert_dialog)
self.alert_dialog_layout.setObjectName('alert_dialog_layout')
self.alert_text_layout = QtWidgets.QFormLayout()
diff --git a/openlp/plugins/bibles/forms/booknamedialog.py b/openlp/plugins/bibles/forms/booknamedialog.py
index 6679dc1f3..18adde74b 100644
--- a/openlp/plugins/bibles/forms/booknamedialog.py
+++ b/openlp/plugins/bibles/forms/booknamedialog.py
@@ -30,7 +30,7 @@ from openlp.core.lib.ui import create_button_box
class Ui_BookNameDialog(object):
def setupUi(self, book_name_dialog):
book_name_dialog.setObjectName('book_name_dialog')
- book_name_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
+ book_name_dialog.setWindowIcon(build_icon(':/icon/openlp-logo.svg'))
book_name_dialog.resize(400, 271)
self.book_name_layout = QtWidgets.QVBoxLayout(book_name_dialog)
self.book_name_layout.setSpacing(8)
diff --git a/openlp/plugins/bibles/forms/editbibledialog.py b/openlp/plugins/bibles/forms/editbibledialog.py
index f326f2af0..7a2de263e 100644
--- a/openlp/plugins/bibles/forms/editbibledialog.py
+++ b/openlp/plugins/bibles/forms/editbibledialog.py
@@ -32,7 +32,7 @@ from openlp.plugins.bibles.lib.db import BiblesResourcesDB
class Ui_EditBibleDialog(object):
def setupUi(self, edit_bible_dialog):
edit_bible_dialog.setObjectName('edit_bible_dialog')
- edit_bible_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
+ edit_bible_dialog.setWindowIcon(build_icon(':/icon/openlp-logo.svg'))
edit_bible_dialog.resize(520, 400)
edit_bible_dialog.setModal(True)
self.dialog_layout = QtWidgets.QVBoxLayout(edit_bible_dialog)
diff --git a/openlp/plugins/bibles/forms/languagedialog.py b/openlp/plugins/bibles/forms/languagedialog.py
index ea5c291a0..554c7e35d 100644
--- a/openlp/plugins/bibles/forms/languagedialog.py
+++ b/openlp/plugins/bibles/forms/languagedialog.py
@@ -30,7 +30,7 @@ from openlp.core.lib.ui import create_button_box
class Ui_LanguageDialog(object):
def setupUi(self, language_dialog):
language_dialog.setObjectName('language_dialog')
- language_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
+ language_dialog.setWindowIcon(build_icon(':/icon/openlp-logo.svg'))
language_dialog.resize(400, 165)
self.language_layout = QtWidgets.QVBoxLayout(language_dialog)
self.language_layout.setSpacing(8)
diff --git a/openlp/plugins/bibles/lib/importers/http.py b/openlp/plugins/bibles/lib/importers/http.py
index 6921c9005..d41187d93 100644
--- a/openlp/plugins/bibles/lib/importers/http.py
+++ b/openlp/plugins/bibles/lib/importers/http.py
@@ -493,7 +493,7 @@ class CWExtract(RegistryProperties):
for verse in verses_div:
self.application.process_events()
verse_number = int(verse.find('strong').contents[0])
- verse_span = verse.find('span')
+ verse_span = verse.find('span', class_='verse-%d' % verse_number)
tags_to_remove = verse_span.find_all(['a', 'sup'])
for tag in tags_to_remove:
tag.decompose()
diff --git a/openlp/plugins/bibles/lib/manager.py b/openlp/plugins/bibles/lib/manager.py
index 10a02172b..d2913053a 100644
--- a/openlp/plugins/bibles/lib/manager.py
+++ b/openlp/plugins/bibles/lib/manager.py
@@ -131,7 +131,7 @@ class BibleManager(OpenLPMixin, RegistryProperties):
name = bible.get_name()
# Remove corrupted files.
if name is None:
- bible.session.close()
+ bible.session.close_all()
delete_file(os.path.join(self.path, filename))
continue
log.debug('Bible Name: "{name}"'.format(name=name))
@@ -178,7 +178,7 @@ class BibleManager(OpenLPMixin, RegistryProperties):
"""
log.debug('BibleManager.delete_bible("{name}")'.format(name=name))
bible = self.db_cache[name]
- bible.session.close()
+ bible.session.close_all()
bible.session = None
return delete_file(os.path.join(bible.path, bible.file))
@@ -367,7 +367,6 @@ class BibleManager(OpenLPMixin, RegistryProperties):
second_web_bible = self.db_cache[second_bible].get_object(BibleMeta, 'download_source')
if web_bible or second_web_bible:
# If either Bible is Web, cursor is reset to normal and search ends w/o any message.
- self.check_search_result()
self.application.set_normal_cursor()
return None
# Fetch the results from db. If no results are found, return None, no message is given for this.
diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py
index b8ff3a752..9a04e5360 100644
--- a/openlp/plugins/bibles/lib/mediaitem.py
+++ b/openlp/plugins/bibles/lib/mediaitem.py
@@ -75,21 +75,16 @@ class BibleMediaItem(MediaManagerItem):
self.has_search = True
self.search_results = {}
self.second_search_results = {}
- self.check_search_result()
Registry().register_function('bibles_load_list', self.reload_bibles)
def __check_second_bible(self, bible, second_bible):
"""
Check if the first item is a second bible item or not.
"""
- bitem = self.list_view.item(0)
- if not bitem.flags() & QtCore.Qt.ItemIsSelectable:
- # The item is the "No Search Results" item.
- self.list_view.clear()
+ if not self.list_view.count():
self.display_results(bible, second_bible)
return
- else:
- item_second_bible = self._decode_qt_object(bitem, 'second_bible')
+ item_second_bible = self._decode_qt_object(self.list_view.item(0), 'second_bible')
if item_second_bible and second_bible or not item_second_bible and not second_bible:
self.display_results(bible, second_bible)
elif critical_error_message_box(
@@ -199,7 +194,7 @@ class BibleMediaItem(MediaManagerItem):
self.quick_search_label = QtWidgets.QLabel(self.quickTab)
self.quick_search_label.setObjectName('quick_search_label')
self.quickLayout.addWidget(self.quick_search_label, 0, 0, QtCore.Qt.AlignRight)
- self.quick_search_edit = SearchEdit(self.quickTab)
+ self.quick_search_edit = SearchEdit(self.quickTab, self.settings_section)
self.quick_search_edit.setSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Fixed)
self.quick_search_edit.setObjectName('quick_search_edit')
self.quick_search_label.setBuddy(self.quick_search_edit)
@@ -254,8 +249,8 @@ class BibleMediaItem(MediaManagerItem):
self.quickStyleComboBox.activated.connect(self.on_quick_style_combo_box_changed)
self.advancedStyleComboBox.activated.connect(self.on_advanced_style_combo_box_changed)
# Buttons
- self.advancedClearButton.clicked.connect(self.on_clear_button)
- self.quickClearButton.clicked.connect(self.on_clear_button)
+ self.advancedClearButton.clicked.connect(self.on_advanced_clear_button_clicked)
+ self.quickClearButton.clicked.connect(self.on_clear_button_clicked)
self.advancedSearchButton.clicked.connect(self.on_advanced_search_button)
self.quickSearchButton.clicked.connect(self.on_quick_search_button)
# Other stuff
@@ -333,8 +328,8 @@ class BibleMediaItem(MediaManagerItem):
translate('BiblesPlugin.MediaItem', 'Text Search'),
translate('BiblesPlugin.MediaItem', 'Search Text...'))
])
- text = self.settings_section
- self.quick_search_edit.set_current_search_type(Settings().value('{text}/last search type'.format(text=text)))
+ if Settings().value(self.settings_section + '/reset to combined quick search'):
+ self.quick_search_edit.set_current_search_type(BibleSearch.Combined)
self.config_update()
log.debug('bible manager initialise complete')
@@ -444,15 +439,6 @@ class BibleMediaItem(MediaManagerItem):
only updated when we are doing reference or combined search, in text search the completion list is removed.
"""
log.debug('update_auto_completer')
- # Save the current search type to the configuration. If setting for automatically resetting the search type to
- # Combined is enabled, use that otherwise use the currently selected search type.
- # Note: This setting requires a restart to take effect.
- if Settings().value(self.settings_section + '/reset to combined quick search'):
- Settings().setValue('{section}/last search type'.format(section=self.settings_section),
- BibleSearch.Combined)
- else:
- Settings().setValue('{section}/last search type'.format(section=self.settings_section),
- self.quick_search_edit.current_search_type())
# Save the current bible to the configuration.
Settings().setValue('{section}/quick bible'.format(section=self.settings_section),
self.quickVersionComboBox.currentText())
@@ -548,19 +534,29 @@ class BibleMediaItem(MediaManagerItem):
self.advancedTab.setVisible(True)
self.advanced_book_combo_box.setFocus()
- def on_clear_button(self):
+ def on_clear_button_clicked(self):
# Clear the list, then set the "No search Results" message, then clear the text field and give it focus.
self.list_view.clear()
- self.check_search_result()
self.quick_search_edit.clear()
self.quick_search_edit.setFocus()
+ def on_advanced_clear_button_clicked(self):
+ # The same as the on_clear_button_clicked, but gives focus to Book name field in "Select" (advanced).
+ self.list_view.clear()
+ self.advanced_book_combo_box.setFocus()
+
def on_lock_button_toggled(self, checked):
- self.quick_search_edit.setFocus()
+ """
+ Toggle the lock button, if Search tab is used, set focus to search field.
+ :param checked: The state of the toggle button. bool
+ :return: None
+ """
if checked:
self.sender().setIcon(self.lock_icon)
else:
self.sender().setIcon(self.unlock_icon)
+ if self.quickTab.isVisible():
+ self.quick_search_edit.setFocus()
def on_quick_style_combo_box_changed(self):
self.settings.layout_style = self.quickStyleComboBox.currentIndex()
@@ -680,7 +676,6 @@ class BibleMediaItem(MediaManagerItem):
elif self.search_results:
self.display_results(bible, second_bible)
self.advancedSearchButton.setEnabled(True)
- self.check_search_result()
self.application.set_normal_cursor()
def on_quick_reference_search(self):
@@ -874,7 +869,6 @@ class BibleMediaItem(MediaManagerItem):
elif self.search_results:
self.display_results(bible, second_bible)
self.quickSearchButton.setEnabled(True)
- self.check_search_result()
self.application.set_normal_cursor()
def on_quick_search_while_typing(self):
@@ -905,7 +899,6 @@ class BibleMediaItem(MediaManagerItem):
self.__check_second_bible(bible, second_bible)
elif self.search_results:
self.display_results(bible, second_bible)
- self.check_search_result()
self.application.set_normal_cursor()
def on_search_text_edit_changed(self):
@@ -944,17 +937,13 @@ class BibleMediaItem(MediaManagerItem):
if len(text) == 0:
if not self.quickLockButton.isChecked():
self.list_view.clear()
- self.check_search_result()
else:
if limit == 3 and (len(text) < limit or len(count_space_digit_reference) == 0):
if not self.quickLockButton.isChecked():
self.list_view.clear()
- self.check_search_result()
- elif (limit == 8 and (len(text) < limit or len(count_spaces_two_chars_text) == 0 or
- len(count_two_chars_text) < 2)):
+ elif limit == 8 and (len(text) < limit or len(count_two_chars_text) < 2):
if not self.quickLockButton.isChecked():
- self.list_view.clear()
- self.check_search_result_search_while_typing_short()
+ self.list_view.clear(search_while_typing=True)
else:
"""
Start search if no chars are entered or deleted for 0.2 s
diff --git a/openlp/plugins/custom/forms/editcustomdialog.py b/openlp/plugins/custom/forms/editcustomdialog.py
index b686e5522..d25ff337b 100644
--- a/openlp/plugins/custom/forms/editcustomdialog.py
+++ b/openlp/plugins/custom/forms/editcustomdialog.py
@@ -34,7 +34,7 @@ class Ui_CustomEditDialog(object):
:param custom_edit_dialog: The Dialog
"""
custom_edit_dialog.setObjectName('custom_edit_dialog')
- custom_edit_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
+ custom_edit_dialog.setWindowIcon(build_icon(':/icon/openlp-logo.svg'))
custom_edit_dialog.resize(450, 350)
self.dialog_layout = QtWidgets.QVBoxLayout(custom_edit_dialog)
self.dialog_layout.setObjectName('dialog_layout')
diff --git a/openlp/plugins/custom/forms/editcustomslidedialog.py b/openlp/plugins/custom/forms/editcustomslidedialog.py
index 5f46c6f28..d08679eee 100644
--- a/openlp/plugins/custom/forms/editcustomslidedialog.py
+++ b/openlp/plugins/custom/forms/editcustomslidedialog.py
@@ -31,7 +31,7 @@ from openlp.core.ui.lib import SpellTextEdit
class Ui_CustomSlideEditDialog(object):
def setupUi(self, custom_slide_edit_dialog):
custom_slide_edit_dialog.setObjectName('custom_slide_edit_dialog')
- custom_slide_edit_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
+ custom_slide_edit_dialog.setWindowIcon(build_icon(':/icon/openlp-logo.svg'))
custom_slide_edit_dialog.resize(350, 300)
self.dialog_layout = QtWidgets.QVBoxLayout(custom_slide_edit_dialog)
self.slide_text_edit = SpellTextEdit(self)
diff --git a/openlp/plugins/custom/lib/mediaitem.py b/openlp/plugins/custom/lib/mediaitem.py
index e9e26111f..eacc5a8f5 100644
--- a/openlp/plugins/custom/lib/mediaitem.py
+++ b/openlp/plugins/custom/lib/mediaitem.py
@@ -105,8 +105,6 @@ class CustomMediaItem(MediaManagerItem):
[(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)])
- text = '{section}/last search type'.format(section=self.settings_section)
- self.search_text_edit.set_current_search_type(Settings().value(text))
self.load_list(self.plugin.db_manager.get_all_objects(CustomSlide, order_by_ref=CustomSlide.title))
self.config_update()
@@ -131,7 +129,6 @@ class CustomMediaItem(MediaManagerItem):
# 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):
"""
@@ -250,9 +247,6 @@ class CustomMediaItem(MediaManagerItem):
"""
Search the plugin database
"""
- # Save the current search type to the configuration.
- Settings().setValue('{section}/last search type'.format(section=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 = '%{search}%'.format(search=self.whitespace.sub(' ', self.search_text_edit.displayText()))
@@ -268,7 +262,6 @@ class CustomMediaItem(MediaManagerItem):
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):
"""
@@ -350,7 +343,7 @@ class CustomMediaItem(MediaManagerItem):
:param string: The search string
:param show_error: The error string to be show.
"""
- search = '%{search}%'.forma(search=string.lower())
+ search = '%{search}%'.format(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)),
diff --git a/openlp/plugins/songs/forms/authorsdialog.py b/openlp/plugins/songs/forms/authorsdialog.py
index 666baffec..7b2995480 100644
--- a/openlp/plugins/songs/forms/authorsdialog.py
+++ b/openlp/plugins/songs/forms/authorsdialog.py
@@ -36,7 +36,7 @@ class Ui_AuthorsDialog(object):
Set up the UI for the dialog.
"""
authors_dialog.setObjectName('authors_dialog')
- authors_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
+ authors_dialog.setWindowIcon(build_icon(':/icon/openlp-logo.svg'))
authors_dialog.resize(300, 10)
authors_dialog.setModal(True)
self.dialog_layout = QtWidgets.QVBoxLayout(authors_dialog)
diff --git a/openlp/plugins/songs/forms/editsongdialog.py b/openlp/plugins/songs/forms/editsongdialog.py
index 46e37c1cd..b23fe1189 100644
--- a/openlp/plugins/songs/forms/editsongdialog.py
+++ b/openlp/plugins/songs/forms/editsongdialog.py
@@ -36,7 +36,7 @@ class Ui_EditSongDialog(object):
"""
def setupUi(self, edit_song_dialog):
edit_song_dialog.setObjectName('edit_song_dialog')
- edit_song_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
+ edit_song_dialog.setWindowIcon(build_icon(':/icon/openlp-logo.svg'))
edit_song_dialog.resize(900, 600)
edit_song_dialog.setModal(True)
self.dialog_layout = QtWidgets.QVBoxLayout(edit_song_dialog)
diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py
index 200e1436f..a17c9fb5f 100644
--- a/openlp/plugins/songs/forms/editsongform.py
+++ b/openlp/plugins/songs/forms/editsongform.py
@@ -317,7 +317,7 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
self.song.verse_order = re.sub('([' + verse.upper() + verse.lower() + '])(\W|$)',
r'\g<1>1\2', self.song.verse_order)
except:
- log.exception('Problem processing song Lyrics \n{xml}'.forma(xml=sxml.dump_xml()))
+ log.exception('Problem processing song Lyrics \n{xml}'.format(xml=sxml.dump_xml()))
raise
def keyPressEvent(self, event):
diff --git a/openlp/plugins/songs/forms/editversedialog.py b/openlp/plugins/songs/forms/editversedialog.py
index 6819f3ca6..13dce97fb 100644
--- a/openlp/plugins/songs/forms/editversedialog.py
+++ b/openlp/plugins/songs/forms/editversedialog.py
@@ -31,7 +31,7 @@ from openlp.plugins.songs.lib import VerseType
class Ui_EditVerseDialog(object):
def setupUi(self, edit_verse_dialog):
edit_verse_dialog.setObjectName('edit_verse_dialog')
- edit_verse_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
+ edit_verse_dialog.setWindowIcon(build_icon(':/icon/openlp-logo.svg'))
edit_verse_dialog.resize(400, 400)
edit_verse_dialog.setModal(True)
self.dialog_layout = QtWidgets.QVBoxLayout(edit_verse_dialog)
diff --git a/openlp/plugins/songs/forms/mediafilesdialog.py b/openlp/plugins/songs/forms/mediafilesdialog.py
index 4309eb91f..2b9bcceab 100644
--- a/openlp/plugins/songs/forms/mediafilesdialog.py
+++ b/openlp/plugins/songs/forms/mediafilesdialog.py
@@ -35,7 +35,7 @@ class Ui_MediaFilesDialog(object):
Set up the user interface.
"""
media_files_dialog.setObjectName('media_files_dialog')
- media_files_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
+ media_files_dialog.setWindowIcon(build_icon(':/icon/openlp-logo.svg'))
media_files_dialog.setWindowModality(QtCore.Qt.ApplicationModal)
media_files_dialog.resize(400, 300)
media_files_dialog.setModal(True)
diff --git a/openlp/plugins/songs/forms/songbookdialog.py b/openlp/plugins/songs/forms/songbookdialog.py
index 7e1c04d20..10ed154b4 100644
--- a/openlp/plugins/songs/forms/songbookdialog.py
+++ b/openlp/plugins/songs/forms/songbookdialog.py
@@ -35,7 +35,7 @@ class Ui_SongBookDialog(object):
Set up the user interface.
"""
song_book_dialog.setObjectName('song_book_dialog')
- song_book_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
+ song_book_dialog.setWindowIcon(build_icon(':/icon/openlp-logo.svg'))
song_book_dialog.resize(300, 10)
self.dialog_layout = QtWidgets.QVBoxLayout(song_book_dialog)
self.dialog_layout.setObjectName('dialog_layout')
diff --git a/openlp/plugins/songs/forms/songexportform.py b/openlp/plugins/songs/forms/songexportform.py
index 27f317963..b0d044b2d 100644
--- a/openlp/plugins/songs/forms/songexportform.py
+++ b/openlp/plugins/songs/forms/songexportform.py
@@ -51,7 +51,7 @@ class SongExportForm(OpenLPWizard):
:param parent: The QWidget-derived parent of the wizard.
:param plugin: The songs plugin.
"""
- super(SongExportForm, self).__init__(parent, plugin, 'song_export_wizard', ':/wizards/wizard_exportsong.bmp')
+ super(SongExportForm, self).__init__(parent, plugin, 'song_export_wizard', ':/wizards/wizard_song.bmp')
self.stop_export_flag = False
Registry().register_function('openlp_stop_wizard', self.stop_export)
diff --git a/openlp/plugins/songs/forms/songimportform.py b/openlp/plugins/songs/forms/songimportform.py
index 2539973da..b25021ca9 100644
--- a/openlp/plugins/songs/forms/songimportform.py
+++ b/openlp/plugins/songs/forms/songimportform.py
@@ -52,7 +52,7 @@ class SongImportForm(OpenLPWizard, RegistryProperties):
:param parent: The QWidget-derived parent of the wizard.
:param plugin: The songs plugin.
"""
- super(SongImportForm, self).__init__(parent, plugin, 'songImportWizard', ':/wizards/wizard_importsong.bmp')
+ super(SongImportForm, self).__init__(parent, plugin, 'songImportWizard', ':/wizards/wizard_song.bmp')
self.clipboard = self.main_window.clipboard
def setupUi(self, image):
diff --git a/openlp/plugins/songs/forms/songmaintenancedialog.py b/openlp/plugins/songs/forms/songmaintenancedialog.py
index cd224267b..c0c532688 100644
--- a/openlp/plugins/songs/forms/songmaintenancedialog.py
+++ b/openlp/plugins/songs/forms/songmaintenancedialog.py
@@ -37,7 +37,7 @@ class Ui_SongMaintenanceDialog(object):
Set up the user interface for the song maintenance dialog
"""
song_maintenance_dialog.setObjectName('song_maintenance_dialog')
- song_maintenance_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
+ song_maintenance_dialog.setWindowIcon(build_icon(':/icon/openlp-logo.svg'))
song_maintenance_dialog.setWindowModality(QtCore.Qt.ApplicationModal)
song_maintenance_dialog.resize(10, 350)
self.dialog_layout = QtWidgets.QGridLayout(song_maintenance_dialog)
diff --git a/openlp/plugins/songs/forms/songreviewwidget.py b/openlp/plugins/songs/forms/songreviewwidget.py
index c8873f028..2e5814626 100644
--- a/openlp/plugins/songs/forms/songreviewwidget.py
+++ b/openlp/plugins/songs/forms/songreviewwidget.py
@@ -182,7 +182,7 @@ class SongReviewWidget(QtWidgets.QWidget):
self.song_vertical_layout.addWidget(self.song_group_box)
self.song_remove_button = QtWidgets.QPushButton(self)
self.song_remove_button.setObjectName('song_remove_button')
- self.song_remove_button.setIcon(build_icon(':/songs/song_delete.png'))
+ self.song_remove_button.setIcon(build_icon(':/general/general_delete.png'))
self.song_remove_button.setSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
self.song_vertical_layout.addWidget(self.song_remove_button, alignment=QtCore.Qt.AlignHCenter)
diff --git a/openlp/plugins/songs/forms/topicsdialog.py b/openlp/plugins/songs/forms/topicsdialog.py
index 1ed3ac61b..90c19238d 100644
--- a/openlp/plugins/songs/forms/topicsdialog.py
+++ b/openlp/plugins/songs/forms/topicsdialog.py
@@ -35,7 +35,7 @@ class Ui_TopicsDialog(object):
Set up the user interface for the topics dialog.
"""
topics_dialog.setObjectName('topics_dialog')
- topics_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
+ topics_dialog.setWindowIcon(build_icon(':/icon/openlp-logo.svg'))
topics_dialog.resize(300, 10)
self.dialog_layout = QtWidgets.QVBoxLayout(topics_dialog)
self.dialog_layout.setObjectName('dialog_layout')
diff --git a/openlp/plugins/songs/lib/importers/mediashout.py b/openlp/plugins/songs/lib/importers/mediashout.py
index a3bd7bbbc..9b916cd43 100644
--- a/openlp/plugins/songs/lib/importers/mediashout.py
+++ b/openlp/plugins/songs/lib/importers/mediashout.py
@@ -101,7 +101,7 @@ class MediaShoutImport(SongImport):
self.song_book_name = song.SongID
for verse in verses:
tag = VERSE_TAGS[verse.Type] + str(verse.Number) if verse.Type < len(VERSE_TAGS) else 'O'
- self.add_verse(verse.Text, tag)
+ self.add_verse(self.tidy_text(verse.Text), tag)
for order in verse_order:
if order.Type < len(VERSE_TAGS):
self.verse_order_list.append(VERSE_TAGS[order.Type] + str(order.Number))
diff --git a/openlp/plugins/songs/lib/importers/songimport.py b/openlp/plugins/songs/lib/importers/songimport.py
index c9ef382a7..c8648db85 100644
--- a/openlp/plugins/songs/lib/importers/songimport.py
+++ b/openlp/plugins/songs/lib/importers/songimport.py
@@ -140,10 +140,13 @@ class SongImport(QtCore.QObject):
text = text.replace('\u2026', '...')
text = text.replace('\u2013', '-')
text = text.replace('\u2014', '-')
+ # Replace vertical tab with 2 linebreaks
+ text = text.replace('\v', '\n\n')
+ # Replace form feed (page break) with 2 linebreaks
+ text = text.replace('\f', '\n\n')
# Remove surplus blank lines, spaces, trailing/leading spaces
- text = re.sub(r'[ \t\v]+', ' ', text)
+ text = re.sub(r'[ \t]+', ' ', text)
text = re.sub(r' ?(\r\n?|\n) ?', '\n', text)
- text = re.sub(r' ?(\n{5}|\f)+ ?', '\f', text)
return text
def process_song_text(self, text):
diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py
index 4e048c6bb..d4aa80a6d 100644
--- a/openlp/plugins/songs/lib/mediaitem.py
+++ b/openlp/plugins/songs/lib/mediaitem.py
@@ -167,14 +167,9 @@ class SongMediaItem(MediaManagerItem):
translate('SongsPlugin.MediaItem', 'CCLI number'),
translate('SongsPlugin.MediaItem', 'Search CCLI number...'))
])
- self.search_text_edit.set_current_search_type(
- Settings().value('{section}/last search type'.format(section=self.settings_section)))
self.config_update()
def on_search_text_button_clicked(self):
- # Save the current search type to the configuration.
- Settings().setValue('{section}/last search type'.format(section=self.settings_section),
- self.search_text_edit.current_search_type())
# Reload the list considering the new search type.
search_keywords = str(self.search_text_edit.displayText())
search_type = self.search_text_edit.current_search_type()
@@ -233,7 +228,6 @@ class SongMediaItem(MediaManagerItem):
search_results = self.plugin.manager.get_all_objects(
Song, and_(Song.ccli_number.like(search_string), Song.ccli_number != ''))
self.display_results_cclinumber(search_results)
- self.check_search_result()
def search_entire(self, search_keywords):
search_string = '%{text}%'.format(text=clean_string(search_keywords))
diff --git a/openlp/plugins/songs/lib/openlyricsxml.py b/openlp/plugins/songs/lib/openlyricsxml.py
index b4b7651af..52b57302f 100644
--- a/openlp/plugins/songs/lib/openlyricsxml.py
+++ b/openlp/plugins/songs/lib/openlyricsxml.py
@@ -458,7 +458,7 @@ class OpenLyrics(object):
self._add_tag_to_formatting(tag, tags_element)
# Replace end tags.
for tag in end_tags:
- text = text.replace('{{{tag}}}'.format(tag=tag), '')
+ text = text.replace('{{/{tag}}}'.format(tag=tag), '')
# Replace \n with .
text = text.replace('\n', ' ')
element = etree.XML('{text}'.format(text=text))
diff --git a/openlp/plugins/songs/lib/songcompare.py b/openlp/plugins/songs/lib/songcompare.py
index 44e17a3ac..1d19deaaa 100644
--- a/openlp/plugins/songs/lib/songcompare.py
+++ b/openlp/plugins/songs/lib/songcompare.py
@@ -46,13 +46,13 @@ MIN_BLOCK_SIZE = 70
MAX_TYPO_SIZE = 3
-def songs_probably_equal(song_tupel):
+def songs_probably_equal(song_tuple):
"""
Calculate and return whether two songs are probably equal.
- :param song_tupel: A tuple of two songs to compare.
+ :param song_tuple: A tuple of two songs to compare.
"""
- song1, song2 = song_tupel
+ song1, song2 = song_tuple
pos1, lyrics1 = song1
pos2, lyrics2 = song2
if len(lyrics1) < len(lyrics2):
diff --git a/openlp/plugins/songs/reporting.py b/openlp/plugins/songs/reporting.py
new file mode 100644
index 000000000..98b6713fe
--- /dev/null
+++ b/openlp/plugins/songs/reporting.py
@@ -0,0 +1,106 @@
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2016 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; 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 :mod:`db` module provides the ability to provide a csv file of all songs
+"""
+import csv
+import logging
+
+from PyQt5 import QtWidgets
+
+from openlp.core.common import Registry, translate
+from openlp.core.lib.ui import critical_error_message_box
+from openlp.plugins.songs.lib.db import Song
+
+
+log = logging.getLogger(__name__)
+
+
+def report_song_list():
+ """
+ Export the song list as a CSV file.
+ :return: Nothing
+ """
+ main_window = Registry().get('main_window')
+ plugin = Registry().get('songs').plugin
+ report_file_name, filter_used = QtWidgets.QFileDialog.getSaveFileName(
+ main_window,
+ translate('SongPlugin.ReportSongList', 'Save File'),
+ translate('SongPlugin.ReportSongList', 'song_extract.csv'),
+ translate('SongPlugin.ReportSongList', 'CSV format (*.csv)'))
+
+ if not report_file_name:
+ main_window.error_message(
+ translate('SongPlugin.ReportSongList', 'Output Path Not Selected'),
+ translate('SongPlugin.ReportSongList', 'You have not set a valid output location for your '
+ 'report. \nPlease select an existing path '
+ 'on your computer.')
+ )
+ return
+ if not report_file_name.endswith('csv'):
+ report_file_name += '.csv'
+ file_handle = None
+ Registry().get('application').set_busy_cursor()
+ try:
+ file_handle = open(report_file_name, 'wt')
+ fieldnames = ('Title', 'Alternative Title', 'Copyright', 'Author(s)', 'Song Book', 'Topic')
+ writer = csv.DictWriter(file_handle, fieldnames=fieldnames, quoting=csv.QUOTE_ALL)
+ headers = dict((n, n) for n in fieldnames)
+ writer.writerow(headers)
+ song_list = plugin.manager.get_all_objects(Song)
+ for song in song_list:
+ author_list = []
+ for author_song in song.authors_songs:
+ author_list.append(author_song.author.display_name)
+ author_string = ' | '.join(author_list)
+ book_list = []
+ for book_song in song.songbook_entries:
+ if hasattr(book_song, 'entry') and book_song.entry:
+ book_list.append('{name} #{entry}'.format(name=book_song.songbook.name, entry=book_song.entry))
+ book_string = ' | '.join(book_list)
+ topic_list = []
+ for topic_song in song.topics:
+ if hasattr(topic_song, 'name'):
+ topic_list.append(topic_song.name)
+ topic_string = ' | '.join(topic_list)
+ writer.writerow({'Title': song.title,
+ 'Alternative Title': song.alternate_title,
+ 'Copyright': song.copyright,
+ 'Author(s)': author_string,
+ 'Song Book': book_string,
+ 'Topic': topic_string})
+ Registry().get('application').set_normal_cursor()
+ main_window.information_message(
+ translate('SongPlugin.ReportSongList', 'Report Creation'),
+ translate('SongPlugin.ReportSongList',
+ 'Report \n{name} \nhas been successfully created. ').format(name=report_file_name)
+ )
+ except OSError as ose:
+ Registry().get('application').set_normal_cursor()
+ log.exception('Failed to write out song usage records')
+ critical_error_message_box(translate('SongPlugin.ReportSongList', 'Song Extraction Failed'),
+ translate('SongPlugin.ReportSongList',
+ 'An error occurred while extracting: {error}'
+ ).format(error=ose.strerror))
+ finally:
+ if file_handle:
+ file_handle.close()
diff --git a/openlp/plugins/songs/songsplugin.py b/openlp/plugins/songs/songsplugin.py
index 91f07814c..c76befeea 100644
--- a/openlp/plugins/songs/songsplugin.py
+++ b/openlp/plugins/songs/songsplugin.py
@@ -36,6 +36,7 @@ from openlp.core.common.actions import ActionList
from openlp.core.lib import Plugin, StringContent, build_icon
from openlp.core.lib.db import Manager
from openlp.core.lib.ui import create_action
+from openlp.plugins.songs import reporting
from openlp.plugins.songs.forms.duplicatesongremovalform import DuplicateSongRemovalForm
from openlp.plugins.songs.forms.songselectform import SongSelectForm
from openlp.plugins.songs.lib import clean_song, upgrade
@@ -103,13 +104,13 @@ class SongsPlugin(Plugin):
self.songselect_form.initialise()
self.song_import_item.setVisible(True)
self.song_export_item.setVisible(True)
- self.tools_reindex_item.setVisible(True)
- self.tools_find_duplicates.setVisible(True)
+ self.song_tools_menu.menuAction().setVisible(True)
action_list = ActionList.get_instance()
action_list.add_action(self.song_import_item, UiStrings().Import)
action_list.add_action(self.song_export_item, UiStrings().Export)
action_list.add_action(self.tools_reindex_item, UiStrings().Tools)
action_list.add_action(self.tools_find_duplicates, UiStrings().Tools)
+ action_list.add_action(self.tools_report_song_list, UiStrings().Tools)
def add_import_menu_item(self, import_menu):
"""
@@ -152,19 +153,37 @@ class SongsPlugin(Plugin):
:param tools_menu: The actual **Tools** menu item, so that your actions can use it as their parent.
"""
log.info('add tools menu')
+ self.tools_menu = tools_menu
+ self.song_tools_menu = QtWidgets.QMenu(tools_menu)
+ self.song_tools_menu.setObjectName('song_tools_menu')
+ self.song_tools_menu.setTitle(translate('SongsPlugin', 'Songs'))
self.tools_reindex_item = create_action(
tools_menu, 'toolsReindexItem',
text=translate('SongsPlugin', '&Re-index Songs'),
icon=':/plugins/plugin_songs.png',
statustip=translate('SongsPlugin', 'Re-index the songs database to improve searching and ordering.'),
- visible=False, triggers=self.on_tools_reindex_item_triggered)
- tools_menu.addAction(self.tools_reindex_item)
+ triggers=self.on_tools_reindex_item_triggered)
self.tools_find_duplicates = create_action(
tools_menu, 'toolsFindDuplicates',
text=translate('SongsPlugin', 'Find &Duplicate Songs'),
statustip=translate('SongsPlugin', 'Find and remove duplicate songs in the song database.'),
- visible=False, triggers=self.on_tools_find_duplicates_triggered, can_shortcuts=True)
- tools_menu.addAction(self.tools_find_duplicates)
+ triggers=self.on_tools_find_duplicates_triggered, can_shortcuts=True)
+ self.tools_report_song_list = create_action(
+ tools_menu, 'toolsSongListReport',
+ text=translate('SongsPlugin', 'Song List Report'),
+ statustip=translate('SongsPlugin', 'Produce a CSV file of all the songs in the database.'),
+ triggers=self.on_tools_report_song_list_triggered)
+
+ self.tools_menu.addAction(self.song_tools_menu.menuAction())
+ self.song_tools_menu.addAction(self.tools_reindex_item)
+ self.song_tools_menu.addAction(self.tools_find_duplicates)
+ self.song_tools_menu.addAction(self.tools_report_song_list)
+
+ self.song_tools_menu.menuAction().setVisible(False)
+
+ @staticmethod
+ def on_tools_report_song_list_triggered():
+ reporting.report_song_list()
def on_tools_reindex_item_triggered(self):
"""
@@ -327,13 +346,13 @@ class SongsPlugin(Plugin):
self.manager.finalise()
self.song_import_item.setVisible(False)
self.song_export_item.setVisible(False)
- self.tools_reindex_item.setVisible(False)
- self.tools_find_duplicates.setVisible(False)
action_list = ActionList.get_instance()
action_list.remove_action(self.song_import_item, UiStrings().Import)
action_list.remove_action(self.song_export_item, UiStrings().Export)
action_list.remove_action(self.tools_reindex_item, UiStrings().Tools)
action_list.remove_action(self.tools_find_duplicates, UiStrings().Tools)
+ action_list.add_action(self.tools_report_song_list, UiStrings().Tools)
+ self.song_tools_menu.menuAction().setVisible(False)
super(SongsPlugin, self).finalise()
def new_service_created(self):
diff --git a/openlp/plugins/songusage/forms/songusagedeletedialog.py b/openlp/plugins/songusage/forms/songusagedeletedialog.py
index 40fabe3f7..6f3f72651 100644
--- a/openlp/plugins/songusage/forms/songusagedeletedialog.py
+++ b/openlp/plugins/songusage/forms/songusagedeletedialog.py
@@ -38,7 +38,7 @@ class Ui_SongUsageDeleteDialog(object):
:param song_usage_delete_dialog:
"""
song_usage_delete_dialog.setObjectName('song_usage_delete_dialog')
- song_usage_delete_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
+ song_usage_delete_dialog.setWindowIcon(build_icon(':/icon/openlp-logo.svg'))
song_usage_delete_dialog.resize(291, 243)
self.vertical_layout = QtWidgets.QVBoxLayout(song_usage_delete_dialog)
self.vertical_layout.setSpacing(8)
diff --git a/openlp/plugins/songusage/forms/songusagedetaildialog.py b/openlp/plugins/songusage/forms/songusagedetaildialog.py
index b665e74c2..e9e4a6bbe 100644
--- a/openlp/plugins/songusage/forms/songusagedetaildialog.py
+++ b/openlp/plugins/songusage/forms/songusagedetaildialog.py
@@ -38,7 +38,7 @@ class Ui_SongUsageDetailDialog(object):
:param song_usage_detail_dialog:
"""
song_usage_detail_dialog.setObjectName('song_usage_detail_dialog')
- song_usage_detail_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
+ song_usage_detail_dialog.setWindowIcon(build_icon(':/icon/openlp-logo.svg'))
song_usage_detail_dialog.resize(609, 413)
self.vertical_layout = QtWidgets.QVBoxLayout(song_usage_detail_dialog)
self.vertical_layout.setSpacing(8)
diff --git a/resources/images/about-new.bmp b/resources/images/about-new.bmp
deleted file mode 100644
index b523f6b60..000000000
Binary files a/resources/images/about-new.bmp and /dev/null differ
diff --git a/resources/images/bibles_search_clear.png b/resources/images/bibles_search_clear.png
index 551ce7c96..19a1665ba 100644
Binary files a/resources/images/bibles_search_clear.png and b/resources/images/bibles_search_clear.png differ
diff --git a/resources/images/bibles_search_combined.png b/resources/images/bibles_search_combined.png
index 161b3fcd6..8fc16c886 100644
Binary files a/resources/images/bibles_search_combined.png and b/resources/images/bibles_search_combined.png differ
diff --git a/resources/images/bibles_upgrade_alert.png b/resources/images/bibles_upgrade_alert.png
deleted file mode 100644
index 331aa2687..000000000
Binary files a/resources/images/bibles_upgrade_alert.png and /dev/null differ
diff --git a/resources/images/custom_delete.png b/resources/images/custom_delete.png
deleted file mode 100644
index ef8e685e2..000000000
Binary files a/resources/images/custom_delete.png and /dev/null differ
diff --git a/resources/images/custom_edit.png b/resources/images/custom_edit.png
deleted file mode 100644
index d79aa5151..000000000
Binary files a/resources/images/custom_edit.png and /dev/null differ
diff --git a/resources/images/custom_new.png b/resources/images/custom_new.png
deleted file mode 100644
index 8431237bd..000000000
Binary files a/resources/images/custom_new.png and /dev/null differ
diff --git a/resources/images/general_search_clear.png b/resources/images/general_search_clear.png
deleted file mode 100644
index 6c4b83b7a..000000000
Binary files a/resources/images/general_search_clear.png and /dev/null differ
diff --git a/resources/images/image_clapperboard.png b/resources/images/image_clapperboard.png
deleted file mode 100644
index e46056914..000000000
Binary files a/resources/images/image_clapperboard.png and /dev/null differ
diff --git a/resources/images/image_delete.png b/resources/images/image_delete.png
deleted file mode 100644
index ef8e685e2..000000000
Binary files a/resources/images/image_delete.png and /dev/null differ
diff --git a/resources/images/image_load.png b/resources/images/image_load.png
deleted file mode 100644
index eb211e233..000000000
Binary files a/resources/images/image_load.png and /dev/null differ
diff --git a/resources/images/import_load.png b/resources/images/import_load.png
deleted file mode 100644
index eb211e233..000000000
Binary files a/resources/images/import_load.png and /dev/null differ
diff --git a/resources/images/import_move_to_list.png b/resources/images/import_move_to_list.png
deleted file mode 100644
index 5c0005856..000000000
Binary files a/resources/images/import_move_to_list.png and /dev/null differ
diff --git a/resources/images/import_remove.png b/resources/images/import_remove.png
deleted file mode 100644
index ef8e685e2..000000000
Binary files a/resources/images/import_remove.png and /dev/null differ
diff --git a/resources/images/import_selectall.png b/resources/images/import_selectall.png
deleted file mode 100644
index 0f0d9f152..000000000
Binary files a/resources/images/import_selectall.png and /dev/null differ
diff --git a/resources/images/messagebox_critical.png b/resources/images/messagebox_critical.png
deleted file mode 100644
index 3fb497a19..000000000
Binary files a/resources/images/messagebox_critical.png and /dev/null differ
diff --git a/resources/images/messagebox_info.png b/resources/images/messagebox_info.png
deleted file mode 100644
index 4e3a257f3..000000000
Binary files a/resources/images/messagebox_info.png and /dev/null differ
diff --git a/resources/images/messagebox_warning.png b/resources/images/messagebox_warning.png
deleted file mode 100644
index 331aa2687..000000000
Binary files a/resources/images/messagebox_warning.png and /dev/null differ
diff --git a/resources/images/openlp-2.qrc b/resources/images/openlp-2.qrc
index b45cc745d..ebff58a0a 100644
--- a/resources/images/openlp-2.qrc
+++ b/resources/images/openlp-2.qrc
@@ -23,8 +23,7 @@
topic_maintenance.pngsong_author_edit.pngsong_book_edit.png
- song_delete.png
-
+
image_group.pngimage_new_group.png
@@ -33,7 +32,6 @@
bibles_search_combined.pngbibles_search_text.pngbibles_search_reference.png
- bibles_upgrade_alert.pngbibles_search_clear.pngbibles_search_unlock.pngbibles_search_lock.png
@@ -71,7 +69,6 @@
general_back.png
- slide_close.pngslide_next.pngslide_blank.pngslide_desktop.png
@@ -83,12 +80,6 @@
media_playback_next.png
- openlp-logo-16x16.png
- openlp-logo-32x32.png
- openlp-logo-48x48.png
- openlp-logo-64x64.png
- openlp-logo-128x128.png
- openlp-logo-256x256.pngopenlp-logo.svg
@@ -97,24 +88,18 @@
openlp-about-logo.png
- import_selectall.png
- import_move_to_list.png
- import_remove.png
- import_load.png
-
+
export_load.pngopenlp-osx-wizard.png
- wizard_exportsong.bmp
- wizard_importsong.bmp
+ wizard_song.bmpwizard_importbible.bmpwizard_firsttime.bmpwizard_createtheme.bmpwizard_duplicateremoval.bmp
- wizard_createprojector.png
-
+
service_collapse_all.pngservice_expand_all.png
@@ -130,7 +115,6 @@
clear_shortcut.pngsystem_about.pngsystem_help_contents.png
- system_online_help.pngsystem_mediamanager.pngsystem_volunteer.pngsystem_servicemanager.png
@@ -156,10 +140,7 @@
multimedia-player.png
- messagebox_critical.png
- messagebox_info.png
- messagebox_warning.png
-
+
network_server.pngnetwork_ssl.png
@@ -171,10 +152,8 @@
tools_add.png
- tools_alert.png
-
+
- theme_delete.pngtheme_new.pngtheme_edit.png
@@ -202,12 +181,10 @@
projector_power_on_tiled.pngprojector_show.pngprojector_show_tiled.png
- projector_spacer.pngprojector_warmup.png
- projector_view.png
-
+
android_app_qr.pngios_app_qr.png
-
+
\ No newline at end of file
diff --git a/resources/images/openlp-about-logo.svg b/resources/images/openlp-about-logo.svg
deleted file mode 100644
index d4f9c2dbe..000000000
--- a/resources/images/openlp-about-logo.svg
+++ /dev/null
@@ -1,323 +0,0 @@
-
-
-
-
diff --git a/resources/images/openlp-default-dualdisplay.svg b/resources/images/openlp-default-dualdisplay.svg
deleted file mode 100644
index 40ebedeac..000000000
--- a/resources/images/openlp-default-dualdisplay.svg
+++ /dev/null
@@ -1,489 +0,0 @@
-
-
-
diff --git a/resources/images/openlp-logo-256x256.png b/resources/images/openlp-logo-256x256.png
deleted file mode 100644
index 0fcf37a7c..000000000
Binary files a/resources/images/openlp-logo-256x256.png and /dev/null differ
diff --git a/resources/images/openlp-logo-420x420.png b/resources/images/openlp-logo-420x420.png
deleted file mode 100644
index 81b893b9a..000000000
Binary files a/resources/images/openlp-logo-420x420.png and /dev/null differ
diff --git a/resources/images/openlp-logo.svg b/resources/images/openlp-logo.svg
index c5e7985e1..d1ff61bec 100644
--- a/resources/images/openlp-logo.svg
+++ b/resources/images/openlp-logo.svg
@@ -1,6 +1,4 @@
-
-
diff --git a/resources/images/openlp-splash-screen.svg b/resources/images/openlp-splash-screen.svg
deleted file mode 100644
index b45f2015c..000000000
--- a/resources/images/openlp-splash-screen.svg
+++ /dev/null
@@ -1,296 +0,0 @@
-
-
-
-
diff --git a/resources/images/openlp.org.ico b/resources/images/openlp.org.ico
deleted file mode 100644
index f11e76809..000000000
Binary files a/resources/images/openlp.org.ico and /dev/null differ
diff --git a/resources/images/presentation_delete.png b/resources/images/presentation_delete.png
deleted file mode 100644
index ef8e685e2..000000000
Binary files a/resources/images/presentation_delete.png and /dev/null differ
diff --git a/resources/images/presentation_load.png b/resources/images/presentation_load.png
deleted file mode 100644
index eb211e233..000000000
Binary files a/resources/images/presentation_load.png and /dev/null differ
diff --git a/resources/images/projector_connectors.png b/resources/images/projector_connectors.png
deleted file mode 100644
index 77a6a4dd7..000000000
Binary files a/resources/images/projector_connectors.png and /dev/null differ
diff --git a/resources/images/projector_spacer.png b/resources/images/projector_spacer.png
deleted file mode 100644
index ca5a54a04..000000000
Binary files a/resources/images/projector_spacer.png and /dev/null differ
diff --git a/resources/images/projector_view.png b/resources/images/projector_view.png
deleted file mode 100644
index e4d7e971b..000000000
Binary files a/resources/images/projector_view.png and /dev/null differ
diff --git a/resources/images/service_delete.png b/resources/images/service_delete.png
deleted file mode 100644
index ef8e685e2..000000000
Binary files a/resources/images/service_delete.png and /dev/null differ
diff --git a/resources/images/service_edit.png b/resources/images/service_edit.png
deleted file mode 100644
index 84e345d22..000000000
Binary files a/resources/images/service_edit.png and /dev/null differ
diff --git a/resources/images/service_new.png b/resources/images/service_new.png
deleted file mode 100644
index 8431237bd..000000000
Binary files a/resources/images/service_new.png and /dev/null differ
diff --git a/resources/images/service_open.png b/resources/images/service_open.png
deleted file mode 100644
index eb211e233..000000000
Binary files a/resources/images/service_open.png and /dev/null differ
diff --git a/resources/images/service_save.png b/resources/images/service_save.png
deleted file mode 100644
index 8072aea32..000000000
Binary files a/resources/images/service_save.png and /dev/null differ
diff --git a/resources/images/slide_close.png b/resources/images/slide_close.png
deleted file mode 100644
index 2c2f99eb4..000000000
Binary files a/resources/images/slide_close.png and /dev/null differ
diff --git a/resources/images/song_delete.png b/resources/images/song_delete.png
deleted file mode 100644
index ef8e685e2..000000000
Binary files a/resources/images/song_delete.png and /dev/null differ
diff --git a/resources/images/song_edit.png b/resources/images/song_edit.png
deleted file mode 100644
index 84e345d22..000000000
Binary files a/resources/images/song_edit.png and /dev/null differ
diff --git a/resources/images/song_export.png b/resources/images/song_export.png
deleted file mode 100644
index d26519b7f..000000000
Binary files a/resources/images/song_export.png and /dev/null differ
diff --git a/resources/images/song_new.png b/resources/images/song_new.png
deleted file mode 100644
index 8431237bd..000000000
Binary files a/resources/images/song_new.png and /dev/null differ
diff --git a/resources/images/system_add.png b/resources/images/system_add.png
deleted file mode 100644
index 7bb9b1be8..000000000
Binary files a/resources/images/system_add.png and /dev/null differ
diff --git a/resources/images/system_live.png b/resources/images/system_live.png
deleted file mode 100644
index cb2be2258..000000000
Binary files a/resources/images/system_live.png and /dev/null differ
diff --git a/resources/images/system_online_help.png b/resources/images/system_online_help.png
deleted file mode 100644
index 670c0716f..000000000
Binary files a/resources/images/system_online_help.png and /dev/null differ
diff --git a/resources/images/system_preview.png b/resources/images/system_preview.png
deleted file mode 100644
index d196792bb..000000000
Binary files a/resources/images/system_preview.png and /dev/null differ
diff --git a/resources/images/theme_delete.png b/resources/images/theme_delete.png
deleted file mode 100644
index ef8e685e2..000000000
Binary files a/resources/images/theme_delete.png and /dev/null differ
diff --git a/resources/images/theme_export.png b/resources/images/theme_export.png
deleted file mode 100644
index d26519b7f..000000000
Binary files a/resources/images/theme_export.png and /dev/null differ
diff --git a/resources/images/theme_import.png b/resources/images/theme_import.png
deleted file mode 100644
index 0ee789269..000000000
Binary files a/resources/images/theme_import.png and /dev/null differ
diff --git a/resources/images/tools_alert.png b/resources/images/tools_alert.png
deleted file mode 100644
index 331aa2687..000000000
Binary files a/resources/images/tools_alert.png and /dev/null differ
diff --git a/resources/images/video_delete.png b/resources/images/video_delete.png
deleted file mode 100644
index ef8e685e2..000000000
Binary files a/resources/images/video_delete.png and /dev/null differ
diff --git a/resources/images/video_load.png b/resources/images/video_load.png
deleted file mode 100644
index eb211e233..000000000
Binary files a/resources/images/video_load.png and /dev/null differ
diff --git a/resources/images/wizard_createprojector.png b/resources/images/wizard_createprojector.png
deleted file mode 100644
index d17a01e41..000000000
Binary files a/resources/images/wizard_createprojector.png and /dev/null differ
diff --git a/resources/images/wizard_importsong.bmp b/resources/images/wizard_importsong.bmp
deleted file mode 100644
index 948422dcc..000000000
Binary files a/resources/images/wizard_importsong.bmp and /dev/null differ
diff --git a/resources/images/wizard_exportsong.bmp b/resources/images/wizard_song.bmp
similarity index 100%
rename from resources/images/wizard_exportsong.bmp
rename to resources/images/wizard_song.bmp
diff --git a/scripts/clean_up_resources.py b/scripts/clean_up_resources.py
new file mode 100755
index 000000000..ce4cc8f6d
--- /dev/null
+++ b/scripts/clean_up_resources.py
@@ -0,0 +1,99 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2016 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; 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 #
+###############################################################################
+
+import fnmatch
+import os
+
+from lxml import etree
+
+resource_path = os.path.join('..', 'resources', 'images')
+resource_file_path = os.path.join(resource_path, 'openlp-2.qrc')
+src_directory = os.path.join('..', 'openlp')
+
+RESOURCES_TO_IGNORE = [
+ 'openlp.svg',
+ 'OpenLP.ico',
+ 'openlp-2.qrc',
+ 'openlp-logo-16x16.png',
+ 'openlp-logo-32x32.png',
+ 'openlp-logo-48x48.png',
+ 'openlp-logo-64x64.png',
+ 'openlp-logo-128x128.png',
+ 'README.txt'
+]
+
+tree = etree.parse(resource_file_path)
+root = tree.getroot()
+
+print('Looking for unused resources listed in openlp-2.qrc')
+print('----------------------------------------------------------------')
+resources = root.findall('.//file')
+for current_dir, dirs, files in os.walk(src_directory):
+ for file_name in files:
+ if not fnmatch.fnmatch(file_name, '*.py'):
+ continue
+ file_path = os.path.join(current_dir, file_name)
+ with open(file_path) as source_file:
+ file_contents = source_file.read()
+ # Create a copy of the resources list so that we don't change the list while we're iterating through it!
+ for resource in list(resources):
+ if resource.text in file_contents:
+ resources.remove(resource)
+if resources:
+ print('Unused resources listed in openlp-2.qrc:')
+ print(*(x.text for x in resources), sep='\n')
+ print('----------------------------------------------------------------')
+ remove = None
+ while remove != 'yes' and remove != 'no':
+ remove = input('Would you like to remove these files from openlp-2.qrc? (yes/no)')
+ if remove == 'yes':
+ for resource in resources:
+ parent = resource.find('..')
+ parent.remove(resource)
+ tree.write(resource_file_path, encoding='utf8')
+else:
+ print('No unused resources listed in openlp-2.qrc')
+print('----------------------------------------------------------------')
+
+print('\nLooking for resource files which are not lited in openlp-2.qrc')
+print('----------------------------------------------------------------')
+resources = [x.text for x in root.findall('.//file')]
+removable_resources = []
+for resource_name in os.listdir(resource_path):
+ if resource_name not in RESOURCES_TO_IGNORE and resource_name not in resources:
+ removable_resources.append(resource_name)
+if removable_resources:
+ print('Resource files not listed in openlp-2.qrc:')
+ print(*removable_resources, sep='\n')
+ print('----------------------------------------------------------------')
+ remove = None
+ while remove != 'yes' and remove != 'no':
+ remove = input('Would you like to delete these files from the resource folder? (yes/no)')
+ print('----------------------------------------------------------------')
+ if remove == 'yes':
+ for resource in removable_resources:
+ resource_file = os.path.join(resource_path, resource)
+ print('Removing {file}'.format(file=resource_file))
+ os.remove(resource_file)
+else:
+ print('All resource files are listed!')
diff --git a/tests/functional/openlp_core_lib/test_lib.py b/tests/functional/openlp_core_lib/test_lib.py
index 145be21f4..fdab52e93 100644
--- a/tests/functional/openlp_core_lib/test_lib.py
+++ b/tests/functional/openlp_core_lib/test_lib.py
@@ -189,16 +189,14 @@ class TestLib(TestCase):
"""
Test the build_icon() function with a QIcon instance
"""
- with patch('openlp.core.lib.QtGui') as MockedQtGui:
- # GIVEN: A mocked QIcon
- MockedQtGui.QIcon = MagicMock
- mocked_icon = MockedQtGui.QIcon()
+ # GIVEN: An icon QIcon
+ icon = QtGui.QIcon()
- # WHEN: We pass a QIcon instance in
- result = build_icon(mocked_icon)
+ # WHEN: We pass a QIcon instance in
+ result = build_icon(icon)
- # THEN: The result should be our mocked QIcon
- self.assertIs(mocked_icon, result, 'The result should be the mocked QIcon')
+ # THEN: The result should be the same icon as we passed in
+ self.assertIs(icon, result, 'The result should be the same icon as we passed in')
def test_build_icon_with_resource(self):
"""
@@ -223,6 +221,30 @@ class TestLib(TestCase):
self.assertIsInstance(result, MagicMock, 'The result should be a MagicMock, because we mocked it out')
def test_image_to_byte(self):
+ """
+ Test the image_to_byte() function
+ """
+ with patch('openlp.core.lib.QtCore') as MockedQtCore:
+ # GIVEN: A set of mocked-out Qt classes
+ mocked_byte_array = MagicMock()
+ MockedQtCore.QByteArray.return_value = mocked_byte_array
+ mocked_buffer = MagicMock()
+ MockedQtCore.QBuffer.return_value = mocked_buffer
+ MockedQtCore.QIODevice.WriteOnly = 'writeonly'
+ mocked_image = MagicMock()
+
+ # WHEN: We convert an image to a byte array
+ result = image_to_byte(mocked_image, base_64=False)
+
+ # THEN: We should receive the mocked_buffer
+ MockedQtCore.QByteArray.assert_called_with()
+ MockedQtCore.QBuffer.assert_called_with(mocked_byte_array)
+ mocked_buffer.open.assert_called_with('writeonly')
+ mocked_image.save.assert_called_with(mocked_buffer, "PNG")
+ self.assertFalse(mocked_byte_array.toBase64.called)
+ self.assertEqual(mocked_byte_array, result, 'The mocked out byte array should be returned')
+
+ def test_image_to_byte_base_64(self):
"""
Test the image_to_byte() function
"""
@@ -666,8 +688,8 @@ class TestLib(TestCase):
string_result = create_separated_list(string_list)
# THEN: We should have "Author 1, Author 2, and Author 3"
- assert string_result == 'Author 1, Author 2, and Author 3', 'The string should be u\'Author 1, ' \
- 'Author 2, and Author 3\'.'
+ self.assertEqual(string_result, 'Author 1, Author 2 and Author 3', 'The string should be "Author 1, '
+ 'Author 2, and Author 3".')
def test_create_separated_list_empty_list(self):
"""
@@ -683,56 +705,44 @@ class TestLib(TestCase):
string_result = create_separated_list(string_list)
# THEN: We shoud have an emptry string.
- assert string_result == '', 'The string sould be empty.'
+ self.assertEqual(string_result, '', 'The string sould be empty.')
def test_create_separated_list_with_one_item(self):
"""
Test the create_separated_list function with a list consisting of only one entry
"""
- with patch('openlp.core.lib.Qt') as mocked_qt:
- # GIVEN: A list with a string and the mocked Qt module.
- mocked_qt.PYQT_VERSION_STR = '4.8'
- mocked_qt.qVersion.return_value = '4.7'
- string_list = ['Author 1']
+ # GIVEN: A list with a string.
+ string_list = ['Author 1']
- # WHEN: We get a string build from the entries it the list and a separator.
- string_result = create_separated_list(string_list)
+ # WHEN: We get a string build from the entries it the list and a separator.
+ string_result = create_separated_list(string_list)
- # THEN: We should have "Author 1"
- assert string_result == 'Author 1', 'The string should be u\'Author 1\'.'
+ # THEN: We should have "Author 1"
+ self.assertEqual(string_result, 'Author 1', 'The string should be "Author 1".')
def test_create_separated_list_with_two_items(self):
"""
Test the create_separated_list function with a list of two entries
"""
- with patch('openlp.core.lib.Qt') as mocked_qt, patch('openlp.core.lib.translate') as mocked_translate:
- # GIVEN: A list of strings and the mocked Qt module.
- mocked_qt.PYQT_VERSION_STR = '4.8'
- mocked_qt.qVersion.return_value = '4.7'
- mocked_translate.return_value = '%s and %s'
- string_list = ['Author 1', 'Author 2']
+ # GIVEN: A list with two strings.
+ string_list = ['Author 1', 'Author 2']
- # WHEN: We get a string build from the entries it the list and a seperator.
- string_result = create_separated_list(string_list)
+ # WHEN: We get a string build from the entries it the list and a seperator.
+ string_result = create_separated_list(string_list)
- # THEN: We should have "Author 1 and Author 2"
- assert string_result == 'Author 1 and Author 2', 'The string should be u\'Author 1 and Author 2\'.'
+ # THEN: We should have "Author 1 and Author 2"
+ self.assertEqual(string_result, 'Author 1 and Author 2', 'The string should be "Author 1 and Author 2".')
def test_create_separated_list_with_three_items(self):
"""
Test the create_separated_list function with a list of three items
"""
- with patch('openlp.core.lib.Qt') as mocked_qt, patch('openlp.core.lib.translate') as mocked_translate:
- # GIVEN: A list with a string and the mocked Qt module.
- mocked_qt.PYQT_VERSION_STR = '4.8'
- mocked_qt.qVersion.return_value = '4.7'
- # Always return the untranslated string.
- mocked_translate.side_effect = lambda module, string_to_translate, comment: string_to_translate
- string_list = ['Author 1', 'Author 2', 'Author 3']
+ # GIVEN: A list with three strings.
+ string_list = ['Author 1', 'Author 2', 'Author 3']
- # WHEN: We get a string build from the entries it the list and a seperator.
- string_result = create_separated_list(string_list)
+ # WHEN: We get a string build from the entries it the list and a seperator.
+ string_result = create_separated_list(string_list)
- # THEN: We should have "Author 1, Author 2, and Author 3"
- assert string_result == 'Author 1, Author 2, and Author 3', 'The string should be u\'Author 1, ' \
- 'Author 2, and Author 3\'.'
+ # THEN: We should have "Author 1, Author 2 and Author 3"
+ self.assertEqual(string_result, 'Author 1, Author 2 and Author 3', 'The string should be "Author 1, '
+ 'Author 2, and Author 3".')
diff --git a/tests/functional/openlp_core_ui/test_servicemanager.py b/tests/functional/openlp_core_ui/test_servicemanager.py
index 3e4af8c97..e0366c7d2 100644
--- a/tests/functional/openlp_core_ui/test_servicemanager.py
+++ b/tests/functional/openlp_core_ui/test_servicemanager.py
@@ -28,6 +28,7 @@ from unittest import TestCase
import PyQt5
from openlp.core.common import Registry, ThemeLevel
+from openlp.core.ui.lib.toolbar import OpenLPToolbar
from openlp.core.lib import ServiceItem, ServiceItemType, ItemCapabilities
from openlp.core.ui import ServiceManager
@@ -544,8 +545,8 @@ class TestServiceManager(TestCase):
self.assertEqual(service_manager.theme_menu.menuAction().setVisible.call_count, 1,
'Should have be called once')
- @patch(u'openlp.core.ui.servicemanager.Settings')
- @patch(u'PyQt5.QtCore.QTimer.singleShot')
+ @patch('openlp.core.ui.servicemanager.Settings')
+ @patch('PyQt5.QtCore.QTimer.singleShot')
def test_single_click_preview_true(self, mocked_singleShot, MockedSettings):
"""
Test that when "Preview items when clicked in Service Manager" enabled the preview timer starts
@@ -561,8 +562,8 @@ class TestServiceManager(TestCase):
mocked_singleShot.assert_called_with(PyQt5.QtWidgets.QApplication.instance().doubleClickInterval(),
service_manager.on_single_click_preview_timeout)
- @patch(u'openlp.core.ui.servicemanager.Settings')
- @patch(u'PyQt5.QtCore.QTimer.singleShot')
+ @patch('openlp.core.ui.servicemanager.Settings')
+ @patch('PyQt5.QtCore.QTimer.singleShot')
def test_single_click_preview_false(self, mocked_singleShot, MockedSettings):
"""
Test that when "Preview items when clicked in Service Manager" disabled the preview timer doesn't start
@@ -577,9 +578,9 @@ class TestServiceManager(TestCase):
# THEN: timer should not be started
self.assertEqual(mocked_singleShot.call_count, 0, 'Should not be called')
- @patch(u'openlp.core.ui.servicemanager.Settings')
- @patch(u'PyQt5.QtCore.QTimer.singleShot')
- @patch(u'openlp.core.ui.servicemanager.ServiceManager.make_live')
+ @patch('openlp.core.ui.servicemanager.Settings')
+ @patch('PyQt5.QtCore.QTimer.singleShot')
+ @patch('openlp.core.ui.servicemanager.ServiceManager.make_live')
def test_single_click_preview_double(self, mocked_make_live, mocked_singleShot, MockedSettings):
"""
Test that when a double click has registered the preview timer doesn't start
@@ -596,7 +597,7 @@ class TestServiceManager(TestCase):
mocked_make_live.assert_called_with()
self.assertEqual(mocked_singleShot.call_count, 0, 'Should not be called')
- @patch(u'openlp.core.ui.servicemanager.ServiceManager.make_preview')
+ @patch('openlp.core.ui.servicemanager.ServiceManager.make_preview')
def test_single_click_timeout_single(self, mocked_make_preview):
"""
Test that when a single click has been registered, the item is sent to preview
@@ -609,8 +610,8 @@ class TestServiceManager(TestCase):
self.assertEqual(mocked_make_preview.call_count, 1,
'ServiceManager.make_preview() should have been called once')
- @patch(u'openlp.core.ui.servicemanager.ServiceManager.make_preview')
- @patch(u'openlp.core.ui.servicemanager.ServiceManager.make_live')
+ @patch('openlp.core.ui.servicemanager.ServiceManager.make_preview')
+ @patch('openlp.core.ui.servicemanager.ServiceManager.make_live')
def test_single_click_timeout_double(self, mocked_make_live, mocked_make_preview):
"""
Test that when a double click has been registered, the item does not goes to preview
@@ -623,9 +624,9 @@ class TestServiceManager(TestCase):
# THEN: make_preview() should not have been called
self.assertEqual(mocked_make_preview.call_count, 0, 'ServiceManager.make_preview() should not be called')
- @patch(u'openlp.core.ui.servicemanager.shutil.copy')
- @patch(u'openlp.core.ui.servicemanager.zipfile')
- @patch(u'openlp.core.ui.servicemanager.ServiceManager.save_file_as')
+ @patch('openlp.core.ui.servicemanager.shutil.copy')
+ @patch('openlp.core.ui.servicemanager.zipfile')
+ @patch('openlp.core.ui.servicemanager.ServiceManager.save_file_as')
def test_save_file_raises_permission_error(self, mocked_save_file_as, mocked_zipfile, mocked_shutil_copy):
"""
Test that when a PermissionError is raised when trying to save a file, it is handled correctly
@@ -652,9 +653,9 @@ class TestServiceManager(TestCase):
self.assertTrue(result)
mocked_save_file_as.assert_called_with()
- @patch(u'openlp.core.ui.servicemanager.shutil.copy')
- @patch(u'openlp.core.ui.servicemanager.zipfile')
- @patch(u'openlp.core.ui.servicemanager.ServiceManager.save_file_as')
+ @patch('openlp.core.ui.servicemanager.shutil.copy')
+ @patch('openlp.core.ui.servicemanager.zipfile')
+ @patch('openlp.core.ui.servicemanager.ServiceManager.save_file_as')
def test_save_local_file_raises_permission_error(self, mocked_save_file_as, mocked_zipfile, mocked_shutil_copy):
"""
Test that when a PermissionError is raised when trying to save a local file, it is handled correctly
@@ -679,3 +680,66 @@ class TestServiceManager(TestCase):
# THEN: The "save_as" method is called to save the service
self.assertTrue(result)
mocked_save_file_as.assert_called_with()
+
+ @patch('openlp.core.ui.servicemanager.ServiceManager.regenerate_service_items')
+ def test_theme_change_global(self, mocked_regenerate_service_items):
+ """
+ Test that when a Toolbar theme combobox displays correctly when the theme is set to Global
+ """
+ # GIVEN: A service manager, a service to display with a theme level in the renderer
+ mocked_renderer = MagicMock()
+ service_manager = ServiceManager(None)
+ Registry().register('renderer', mocked_renderer)
+ service_manager.toolbar = OpenLPToolbar(None)
+ service_manager.toolbar.add_toolbar_action('theme_combo_box', triggers=MagicMock())
+ service_manager.toolbar.add_toolbar_action('theme_label', triggers=MagicMock())
+
+ # WHEN: The service manager has a Global theme
+ mocked_renderer.theme_level = ThemeLevel.Global
+ result = service_manager.theme_change()
+
+ # THEN: The the theme toolbar should not be visible
+ self.assertFalse(service_manager.toolbar.actions['theme_combo_box'].isVisible(),
+ 'The visibility should be False')
+
+ @patch('openlp.core.ui.servicemanager.ServiceManager.regenerate_service_items')
+ def test_theme_change_service(self, mocked_regenerate_service_items):
+ """
+ Test that when a Toolbar theme combobox displays correctly when the theme is set to Theme
+ """
+ # GIVEN: A service manager, a service to display with a theme level in the renderer
+ mocked_renderer = MagicMock()
+ service_manager = ServiceManager(None)
+ Registry().register('renderer', mocked_renderer)
+ service_manager.toolbar = OpenLPToolbar(None)
+ service_manager.toolbar.add_toolbar_action('theme_combo_box', triggers=MagicMock())
+ service_manager.toolbar.add_toolbar_action('theme_label', triggers=MagicMock())
+
+ # WHEN: The service manager has a Service theme
+ mocked_renderer.theme_level = ThemeLevel.Service
+ result = service_manager.theme_change()
+
+ # THEN: The the theme toolbar should be visible
+ self.assertTrue(service_manager.toolbar.actions['theme_combo_box'].isVisible(),
+ 'The visibility should be True')
+
+ @patch('openlp.core.ui.servicemanager.ServiceManager.regenerate_service_items')
+ def test_theme_change_song(self, mocked_regenerate_service_items):
+ """
+ Test that when a Toolbar theme combobox displays correctly when the theme is set to Song
+ """
+ # GIVEN: A service manager, a service to display with a theme level in the renderer
+ mocked_renderer = MagicMock()
+ service_manager = ServiceManager(None)
+ Registry().register('renderer', mocked_renderer)
+ service_manager.toolbar = OpenLPToolbar(None)
+ service_manager.toolbar.add_toolbar_action('theme_combo_box', triggers=MagicMock())
+ service_manager.toolbar.add_toolbar_action('theme_label', triggers=MagicMock())
+
+ # WHEN: The service manager has a Song theme
+ mocked_renderer.theme_level = ThemeLevel.Song
+ result = service_manager.theme_change()
+
+ # THEN: The the theme toolbar should be visible
+ self.assertTrue(service_manager.toolbar.actions['theme_combo_box'].isVisible(),
+ 'The visibility should be True')
diff --git a/tests/functional/openlp_core_ui/test_settingsform.py b/tests/functional/openlp_core_ui/test_settingsform.py
index 051bc7af1..3f6f0aaad 100644
--- a/tests/functional/openlp_core_ui/test_settingsform.py
+++ b/tests/functional/openlp_core_ui/test_settingsform.py
@@ -48,7 +48,7 @@ class TestSettingsForm(TestCase):
general_tab = MagicMock()
general_tab.tab_title = 'mock'
general_tab.tab_title_visible = 'Mock'
- general_tab.icon_path = ':/icon/openlp-logo-16x16.png'
+ general_tab.icon_path = ':/icon/openlp-logo.svg'
# WHEN: We insert the general tab
with patch.object(settings_form.stacked_layout, 'addWidget') as mocked_add_widget, \
@@ -86,14 +86,14 @@ class TestSettingsForm(TestCase):
general_tab = QtWidgets.QWidget(None)
general_tab.tab_title = 'mock-general'
general_tab.tab_title_visible = 'Mock General'
- general_tab.icon_path = ':/icon/openlp-logo-16x16.png'
+ general_tab.icon_path = ':/icon/openlp-logo.svg'
mocked_general_save = MagicMock()
general_tab.save = mocked_general_save
settings_form.insert_tab(general_tab, is_visible=True)
themes_tab = QtWidgets.QWidget(None)
themes_tab.tab_title = 'mock-themes'
themes_tab.tab_title_visible = 'Mock Themes'
- themes_tab.icon_path = ':/icon/openlp-logo-16x16.png'
+ themes_tab.icon_path = ':/icon/openlp-logo.svg'
mocked_theme_save = MagicMock()
themes_tab.save = mocked_theme_save
settings_form.insert_tab(themes_tab, is_visible=False)
@@ -114,7 +114,7 @@ class TestSettingsForm(TestCase):
general_tab = QtWidgets.QWidget(None)
general_tab.tab_title = 'mock'
general_tab.tab_title_visible = 'Mock'
- general_tab.icon_path = ':/icon/openlp-logo-16x16.png'
+ general_tab.icon_path = ':/icon/openlp-logo.svg'
settings_form.insert_tab(general_tab, is_visible=True)
with patch.object(settings_form.stacked_layout, 'count') as mocked_count:
@@ -133,14 +133,14 @@ class TestSettingsForm(TestCase):
general_tab = QtWidgets.QWidget(None)
general_tab.tab_title = 'mock-general'
general_tab.tab_title_visible = 'Mock General'
- general_tab.icon_path = ':/icon/openlp-logo-16x16.png'
+ general_tab.icon_path = ':/icon/openlp-logo.svg'
mocked_general_cancel = MagicMock()
general_tab.cancel = mocked_general_cancel
settings_form.insert_tab(general_tab, is_visible=True)
themes_tab = QtWidgets.QWidget(None)
themes_tab.tab_title = 'mock-themes'
themes_tab.tab_title_visible = 'Mock Themes'
- themes_tab.icon_path = ':/icon/openlp-logo-16x16.png'
+ themes_tab.icon_path = ':/icon/openlp-logo.svg'
mocked_theme_cancel = MagicMock()
themes_tab.cancel = mocked_theme_cancel
settings_form.insert_tab(themes_tab, is_visible=False)
diff --git a/tests/functional/openlp_core_ui/test_slidecontroller.py b/tests/functional/openlp_core_ui/test_slidecontroller.py
index ef1ce5793..b93952097 100644
--- a/tests/functional/openlp_core_ui/test_slidecontroller.py
+++ b/tests/functional/openlp_core_ui/test_slidecontroller.py
@@ -208,29 +208,6 @@ class TestSlideController(TestCase):
mocked_on_theme_display.assert_called_once_with(False)
mocked_on_hide_display.assert_called_once_with(False)
- def test_live_escape(self):
- """
- Test that when the live_escape() method is called, the display is set to invisible and any media is stopped
- """
- # GIVEN: A new SlideController instance and mocked out display and media_controller
- mocked_display = MagicMock()
- mocked_media_controller = MagicMock()
- Registry.create()
- Registry().register('media_controller', mocked_media_controller)
- slide_controller = SlideController(None)
- slide_controller.display = mocked_display
- play_slides = MagicMock()
- play_slides.isChecked.return_value = False
- slide_controller.play_slides_loop = play_slides
- slide_controller.play_slides_once = play_slides
-
- # WHEN: live_escape() is called
- slide_controller.live_escape()
-
- # THEN: the display should be set to invisible and the media controller stopped
- mocked_display.setVisible.assert_called_once_with(False)
- mocked_media_controller.media_stop.assert_called_once_with(slide_controller)
-
def test_on_go_live_live_controller(self):
"""
Test that when the on_go_live() method is called the message is sent to the live controller and focus is
@@ -697,7 +674,7 @@ class TestSlideController(TestCase):
slide_controller.next_item = MagicMock()
slide_controller.previous_service = MagicMock()
slide_controller.next_service = MagicMock()
- slide_controller.escape_item = MagicMock()
+ slide_controller.desktop_screen_enable = MagicMock()
slide_controller.desktop_screen = MagicMock()
slide_controller.blank_screen = MagicMock()
slide_controller.theme_screen = MagicMock()
@@ -709,7 +686,7 @@ class TestSlideController(TestCase):
mocked_widget.addActions.assert_called_with([
slide_controller.previous_item, slide_controller.next_item,
slide_controller.previous_service, slide_controller.next_service,
- slide_controller.escape_item, slide_controller.desktop_screen,
+ slide_controller.desktop_screen_enable, slide_controller.desktop_screen,
slide_controller.theme_screen, slide_controller.blank_screen
])
diff --git a/tests/functional/openlp_core_ui_lib/test_listwidgetwithdnd.py b/tests/functional/openlp_core_ui_lib/test_listwidgetwithdnd.py
new file mode 100644
index 000000000..f6cc8b446
--- /dev/null
+++ b/tests/functional/openlp_core_ui_lib/test_listwidgetwithdnd.py
@@ -0,0 +1,104 @@
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2016 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; 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 #
+###############################################################################
+"""
+This module contains tests for the openlp.core.lib.listwidgetwithdnd module
+"""
+from unittest import TestCase
+
+from openlp.core.common.uistrings import UiStrings
+from openlp.core.ui.lib.listwidgetwithdnd import ListWidgetWithDnD
+from unittest.mock import MagicMock, patch
+
+
+class TestListWidgetWithDnD(TestCase):
+ """
+ Test the :class:`~openlp.core.lib.listwidgetwithdnd.ListWidgetWithDnD` class
+ """
+ def test_clear(self):
+ """
+ Test the clear method when called without any arguments.
+ """
+ # GIVEN: An instance of ListWidgetWithDnD
+ widget = ListWidgetWithDnD()
+
+ # WHEN: Calling clear with out any arguments
+ widget.clear()
+
+ # THEN: The results text should be the standard 'no results' text.
+ self.assertEqual(widget.no_results_text, UiStrings().NoResults)
+
+ def test_clear_search_while_typing(self):
+ """
+ Test the clear method when called with the search_while_typing argument set to True
+ """
+ # GIVEN: An instance of ListWidgetWithDnD
+ widget = ListWidgetWithDnD()
+
+ # WHEN: Calling clear with search_while_typing set to True
+ widget.clear(search_while_typing=True)
+
+ # THEN: The results text should be the 'short results' text.
+ self.assertEqual(widget.no_results_text, UiStrings().ShortResults)
+
+ def test_paint_event(self):
+ """
+ Test the paintEvent method when the list is not empty
+ """
+ # GIVEN: An instance of ListWidgetWithDnD with a mocked out count methode which returns 1
+ # (i.e the list has an item)
+ widget = ListWidgetWithDnD()
+ with patch('openlp.core.ui.lib.listwidgetwithdnd.QtWidgets.QListWidget.paintEvent') as mocked_paint_event, \
+ patch.object(widget, 'count', return_value=1), \
+ patch.object(widget, 'viewport') as mocked_viewport:
+ mocked_event = MagicMock()
+
+ # WHEN: Calling paintEvent
+ widget.paintEvent(mocked_event)
+
+ # THEN: The overridden paintEvnet should have been called
+ mocked_paint_event.assert_called_once_with(mocked_event)
+ self.assertFalse(mocked_viewport.called)
+
+ def test_paint_event_no_items(self):
+ """
+ Test the paintEvent method when the list is empty
+ """
+ # GIVEN: An instance of ListWidgetWithDnD with a mocked out count methode which returns 0
+ # (i.e the list is empty)
+ widget = ListWidgetWithDnD()
+ mocked_painter_instance = MagicMock()
+ mocked_qrect = MagicMock()
+ with patch('openlp.core.ui.lib.listwidgetwithdnd.QtWidgets.QListWidget.paintEvent') as mocked_paint_event, \
+ patch.object(widget, 'count', return_value=0), \
+ patch.object(widget, 'viewport'), \
+ patch('openlp.core.ui.lib.listwidgetwithdnd.QtGui.QPainter',
+ return_value=mocked_painter_instance) as mocked_qpainter, \
+ patch('openlp.core.ui.lib.listwidgetwithdnd.QtCore.QRect', return_value=mocked_qrect):
+ mocked_event = MagicMock()
+
+ # WHEN: Calling paintEvent
+ widget.paintEvent(mocked_event)
+
+ # THEN: The overridden paintEvnet should have been called, and some text should be drawn.
+ mocked_paint_event.assert_called_once_with(mocked_event)
+ mocked_qpainter.assert_called_once_with(widget.viewport())
+ mocked_painter_instance.drawText.assert_called_once_with(mocked_qrect, 4100, 'No Search Results')
diff --git a/tests/functional/openlp_plugins/bibles/test_manager.py b/tests/functional/openlp_plugins/bibles/test_manager.py
new file mode 100644
index 000000000..d18d3cfc6
--- /dev/null
+++ b/tests/functional/openlp_plugins/bibles/test_manager.py
@@ -0,0 +1,69 @@
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2016 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; 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 #
+###############################################################################
+"""
+This module contains tests for the manager submodule of the Bibles plugin.
+"""
+from unittest import TestCase
+from unittest.mock import MagicMock, patch
+
+from openlp.plugins.bibles.lib.manager import BibleManager
+
+
+class TestManager(TestCase):
+ """
+ Test the functions in the :mod:`manager` module.
+ """
+
+ def setUp(self):
+ app_location_patcher = patch('openlp.plugins.bibles.lib.manager.AppLocation')
+ self.addCleanup(app_location_patcher.stop)
+ app_location_patcher.start()
+ log_patcher = patch('openlp.plugins.bibles.lib.manager.log')
+ self.addCleanup(log_patcher.stop)
+ self.mocked_log = log_patcher.start()
+ settings_patcher = patch('openlp.plugins.bibles.lib.manager.Settings')
+ self.addCleanup(settings_patcher.stop)
+ settings_patcher.start()
+
+ def test_delete_bible(self):
+ """
+ Test the BibleManager delete_bible method
+ """
+ # GIVEN: An instance of BibleManager and a mocked bible
+ with patch.object(BibleManager, 'reload_bibles'), \
+ patch('openlp.plugins.bibles.lib.manager.os.path.join', side_effect=lambda x, y: '{}/{}'.format(x, y)),\
+ patch('openlp.plugins.bibles.lib.manager.delete_file', return_value=True) as mocked_delete_file:
+ instance = BibleManager(MagicMock())
+ # We need to keep a reference to the mock for close_all as it gets set to None later on!
+ mocked_close_all = MagicMock()
+ mocked_bible = MagicMock(file='KJV.sqlite', path='bibles', **{'session.close_all': mocked_close_all})
+ instance.db_cache = {'KJV': mocked_bible}
+
+ # WHEN: Calling delete_bible with 'KJV'
+ result = instance.delete_bible('KJV')
+
+ # THEN: The session should have been closed and set to None, the bible should be deleted, and the result of
+ # the deletion returned.
+ self.assertTrue(result)
+ mocked_close_all.assert_called_once_with()
+ self.assertIsNone(mocked_bible.session)
+ mocked_delete_file.assert_called_once_with('bibles/KJV.sqlite')
diff --git a/tests/functional/openlp_plugins/bibles/test_mediaitem.py b/tests/functional/openlp_plugins/bibles/test_mediaitem.py
index 05418f177..d4a6eee39 100644
--- a/tests/functional/openlp_plugins/bibles/test_mediaitem.py
+++ b/tests/functional/openlp_plugins/bibles/test_mediaitem.py
@@ -114,7 +114,28 @@ class TestMediaItem(TestCase, TestMixin):
self.assertEqual(self.media_item.search_results, {})
self.assertEqual(self.media_item.second_search_results, {})
- def on_quick_search_button_general_test(self):
+ def test_required_icons(self):
+ """
+ Test that all the required icons are set properly.
+ """
+ # GIVEN: Mocked icons that need to be called.
+ self.media_item.has_import_icon = MagicMock()
+ self.media_item.has_new_icon = MagicMock()
+ self.media_item.has_edit_icon = MagicMock()
+ self.media_item.has_delete_icon = MagicMock()
+ self.media_item.add_to_service_item = MagicMock()
+
+ # WHEN: self.media_item.required_icons is called
+ self.media_item.required_icons()
+
+ # THEN: On windows it should return True, on other platforms False
+ self.assertTrue(self.media_item.has_import_icon, 'Check that the icon is as True.')
+ self.assertFalse(self.media_item.has_new_icon, 'Check that the icon is called as False.')
+ self.assertTrue(self.media_item.has_edit_icon, 'Check that the icon is called as True.')
+ self.assertTrue(self.media_item.has_delete_icon, 'Check that the icon is called as True.')
+ self.assertFalse(self.media_item.add_to_service_item, 'Check that the icon is called as False')
+
+ def test_on_quick_search_button_general(self):
"""
Test that general things, which should be called on all Quick searches are called.
"""
@@ -134,7 +155,6 @@ class TestMediaItem(TestCase, TestMixin):
self.media_item.list_view = MagicMock()
self.media_item.search_results = MagicMock()
self.media_item.display_results = MagicMock()
- self.media_item.check_search_result = MagicMock()
self.app.set_normal_cursor = MagicMock()
# WHEN: on_quick_search_button is called
@@ -148,5 +168,59 @@ class TestMediaItem(TestCase, TestMixin):
self.assertEqual(1, self.media_item.quickLockButton.isChecked.call_count, 'Lock Should had been called once')
self.assertEqual(1, self.media_item.display_results.call_count, 'Display results Should had been called once')
self.assertEqual(2, self.media_item.quickSearchButton.setEnabled.call_count, 'Disable and Enable the button')
- self.assertEqual(1, self.media_item.check_search_result.call_count, 'Check results Should had been called once')
self.assertEqual(1, self.app.set_normal_cursor.call_count, 'Normal cursor should had been called once')
+
+ def test_on_clear_button_clicked(self):
+ """
+ Test that the on_clear_button_clicked works properly. (Used by Bible search tab)
+ """
+ # GIVEN: Mocked list_view, check_search_results & quick_search_edit.
+ self.media_item.list_view = MagicMock()
+ self.media_item.quick_search_edit = MagicMock()
+
+ # WHEN: on_clear_button_clicked is called
+ self.media_item.on_clear_button_clicked()
+
+ # THEN: Search result should be reset and search field should receive focus.
+ self.media_item.list_view.clear.assert_called_once_with(),
+ self.media_item.quick_search_edit.clear.assert_called_once_with(),
+ self.media_item.quick_search_edit.setFocus.assert_called_once_with()
+
+ def test_on_lock_button_toggled_search_tab_lock_icon(self):
+ """
+ Test that "on_lock_button_toggled" gives focus to the right field and toggles the lock properly.
+ """
+ # GIVEN: Mocked sender & Search edit, quickTab returning value = True on isVisible.
+ self.media_item.sender = MagicMock()
+ self.media_item.quick_search_edit = MagicMock()
+ self.media_item.quickTab = MagicMock(**{'isVisible.return_value': True})
+
+ self.media_item.lock_icon = 'lock icon'
+ sender_instance_mock = MagicMock()
+ self.media_item.sender = MagicMock(return_value=sender_instance_mock)
+
+ # WHEN: on_lock_button_toggled is called and checked returns = True.
+ self.media_item.on_lock_button_toggled(True)
+
+ # THEN: on_quick_search_edit should receive focus and Lock icon should be set.
+ self.media_item.quick_search_edit.setFocus.assert_called_once_with()
+ sender_instance_mock.setIcon.assert_called_once_with('lock icon')
+
+ def test_on_lock_button_toggled_unlock_icon(self):
+ """
+ Test that lock button unlocks properly and lock toggles properly.
+ """
+ # GIVEN: Mocked sender & Search edit, quickTab returning value = False on isVisible.
+ self.media_item.sender = MagicMock()
+ self.media_item.quick_search_edit = MagicMock()
+ self.media_item.quickTab = MagicMock()
+ self.media_item.quickTab.isVisible = MagicMock()
+ self.media_item.unlock_icon = 'unlock icon'
+ sender_instance_mock = MagicMock()
+ self.media_item.sender = MagicMock(return_value=sender_instance_mock)
+
+ # WHEN: on_lock_button_toggled is called and checked returns = False.
+ self.media_item.on_lock_button_toggled(False)
+
+ # THEN: Unlock icon should be set.
+ sender_instance_mock.setIcon.assert_called_once_with('unlock icon')
diff --git a/tests/functional/openlp_plugins/bibles/test_swordimport.py b/tests/functional/openlp_plugins/bibles/test_swordimport.py
index 261df1c0e..14480bdd1 100644
--- a/tests/functional/openlp_plugins/bibles/test_swordimport.py
+++ b/tests/functional/openlp_plugins/bibles/test_swordimport.py
@@ -70,8 +70,7 @@ class TestSwordImport(TestCase):
@patch('openlp.plugins.bibles.lib.importers.sword.SwordBible.application')
@patch('openlp.plugins.bibles.lib.importers.sword.modules')
- @patch('openlp.core.common.languages')
- def test_simple_import(self, mocked_languages, mocked_pysword_modules, mocked_application):
+ def test_simple_import(self, mocked_pysword_modules, mocked_application):
"""
Test that a simple SWORD import works
"""
@@ -88,7 +87,7 @@ class TestSwordImport(TestCase):
importer.create_verse = MagicMock()
importer.create_book = MagicMock()
importer.session = MagicMock()
- mocked_languages.get_language.return_value = 'Danish'
+ importer.get_language = MagicMock(return_value='Danish')
mocked_bible = MagicMock()
mocked_genesis = MagicMock()
mocked_genesis.name = 'Genesis'
diff --git a/tests/functional/openlp_plugins/songs/test_mediashout.py b/tests/functional/openlp_plugins/songs/test_mediashout.py
index 4f03c4f94..c08b04df7 100644
--- a/tests/functional/openlp_plugins/songs/test_mediashout.py
+++ b/tests/functional/openlp_plugins/songs/test_mediashout.py
@@ -22,15 +22,20 @@
"""
Test the MediaShout importer
"""
-from unittest import TestCase
+from unittest import TestCase, skipUnless
from collections import namedtuple
from openlp.core.common import Registry
-from openlp.plugins.songs.lib.importers.mediashout import MediaShoutImport
+try:
+ from openlp.plugins.songs.lib.importers.mediashout import MediaShoutImport
+ CAN_RUN_TESTS = True
+except ImportError:
+ CAN_RUN_TESTS = False
from tests.functional import MagicMock, patch, call
+@skipUnless(CAN_RUN_TESTS, 'Not Windows, skipping test')
class TestMediaShoutImport(TestCase):
"""
Test the MediaShout importer
diff --git a/tests/interfaces/openlp_core_lib/test_searchedit.py b/tests/interfaces/openlp_core_lib/test_searchedit.py
index a4c4472f5..464c6217d 100644
--- a/tests/interfaces/openlp_core_lib/test_searchedit.py
+++ b/tests/interfaces/openlp_core_lib/test_searchedit.py
@@ -23,6 +23,7 @@
Module to test the EditCustomForm.
"""
from unittest import TestCase
+from unittest.mock import MagicMock, call, patch
from PyQt5 import QtCore, QtGui, QtTest, QtWidgets
@@ -58,7 +59,12 @@ class TestSearchEdit(TestCase, TestMixin):
self.main_window = QtWidgets.QMainWindow()
Registry().register('main_window', self.main_window)
- self.search_edit = SearchEdit(self.main_window)
+ settings_patcher = patch(
+ 'openlp.core.lib.searchedit.Settings', return_value=MagicMock(**{'value.return_value': SearchTypes.First}))
+ self.addCleanup(settings_patcher.stop)
+ self.mocked_settings = settings_patcher.start()
+
+ self.search_edit = SearchEdit(self.main_window, 'settings_section')
# To complete set up we have to set the search types.
self.search_edit.set_search_types(SEARCH_TYPES)
@@ -78,8 +84,11 @@ class TestSearchEdit(TestCase, TestMixin):
# WHEN:
- # THEN: The first search type should be the first one in the list.
- assert self.search_edit.current_search_type() == SearchTypes.First, "The first search type should be selected."
+ # THEN: The first search type should be the first one in the list. The selected type should be saved in the
+ # settings
+ self.assertEqual(self.search_edit.current_search_type(), SearchTypes.First,
+ "The first search type should be selected.")
+ self.mocked_settings().setValue.assert_called_once_with('settings_section/last search type', 0)
def test_set_current_search_type(self):
"""
@@ -90,11 +99,13 @@ class TestSearchEdit(TestCase, TestMixin):
result = self.search_edit.set_current_search_type(SearchTypes.Second)
# THEN:
- assert result, "The call should return success (True)."
- assert self.search_edit.current_search_type() == SearchTypes.Second,\
- "The search type should be SearchTypes.Second"
- assert self.search_edit.placeholderText() == SECOND_PLACEHOLDER_TEXT,\
- "The correct placeholder text should be 'Second Placeholder Text'."
+ self.assertTrue(result, "The call should return success (True).")
+ self.assertEqual(self.search_edit.current_search_type(), SearchTypes.Second,
+ "The search type should be SearchTypes.Second")
+ self.assertEqual(self.search_edit.placeholderText(), SECOND_PLACEHOLDER_TEXT,
+ "The correct placeholder text should be 'Second Placeholder Text'.")
+ self.mocked_settings().setValue.assert_has_calls(
+ [call('settings_section/last search type', 0), call('settings_section/last search type', 1)])
def test_clear_button_visibility(self):
"""
diff --git a/tests/interfaces/openlp_plugins/bibles/test_lib_http.py b/tests/interfaces/openlp_plugins/bibles/test_lib_http.py
index 084bfa476..7068898fe 100644
--- a/tests/interfaces/openlp_plugins/bibles/test_lib_http.py
+++ b/tests/interfaces/openlp_plugins/bibles/test_lib_http.py
@@ -163,3 +163,19 @@ class TestBibleHTTP(TestCase):
# THEN: The list should not be None, and some known bibles should be there
self.assertIsNotNone(bibles)
self.assertIn(('Giovanni Diodati 1649 (Italian)', 'gdb', 'it'), bibles)
+
+ def test_crosswalk_get_verse_text(self):
+ """
+ Test verse text from Crosswalk.com
+ """
+ # GIVEN: A new Crosswalk extraction class
+ handler = CWExtract()
+
+ # WHEN: downloading NIV Genesis from Crosswalk
+ niv_genesis_chapter_one = handler.get_bible_chapter('niv', 'Genesis', 1)
+
+ # THEN: The verse list should contain the verses
+ self.assertTrue(niv_genesis_chapter_one.has_verse_list())
+ self.assertEquals('In the beginning God created the heavens and the earth.',
+ niv_genesis_chapter_one.verse_list[1],
+ 'The first chapter of genesis should have been fetched.')