diff --git a/openlp/core/common/languages.py b/openlp/core/common/languages.py
new file mode 100644
index 000000000..18235ac10
--- /dev/null
+++ b/openlp/core/common/languages.py
@@ -0,0 +1,201 @@
+# -*- 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:`languages` module provides a list of language names with utility functions.
+"""
+from collections import namedtuple
+
+from openlp.core.common import translate
+
+
+Language = namedtuple('Language', ['id', 'name', 'code'])
+languages = sorted([
+ Language(1, translate('common.languages', '(Afan) Oromo', 'Language code: om'), 'om'),
+ Language(2, translate('common.languages', 'Abkhazian', 'Language code: ab'), 'ab'),
+ Language(3, translate('common.languages', 'Afar', 'Language code: aa'), 'aa'),
+ Language(4, translate('common.languages', 'Afrikaans', 'Language code: af'), 'af'),
+ Language(5, translate('common.languages', 'Albanian', 'Language code: sq'), 'sq'),
+ Language(6, translate('common.languages', 'Amharic', 'Language code: am'), 'am'),
+ Language(140, translate('common.languages', 'Amuzgo', 'Language code: amu'), 'amu'),
+ Language(152, translate('common.languages', 'Ancient Greek', 'Language code: grc'), 'grc'),
+ Language(7, translate('common.languages', 'Arabic', 'Language code: ar'), 'ar'),
+ Language(8, translate('common.languages', 'Armenian', 'Language code: hy'), 'hy'),
+ Language(9, translate('common.languages', 'Assamese', 'Language code: as'), 'as'),
+ Language(10, translate('common.languages', 'Aymara', 'Language code: ay'), 'ay'),
+ Language(11, translate('common.languages', 'Azerbaijani', 'Language code: az'), 'az'),
+ Language(12, translate('common.languages', 'Bashkir', 'Language code: ba'), 'ba'),
+ Language(13, translate('common.languages', 'Basque', 'Language code: eu'), 'eu'),
+ Language(14, translate('common.languages', 'Bengali', 'Language code: bn'), 'bn'),
+ Language(15, translate('common.languages', 'Bhutani', 'Language code: dz'), 'dz'),
+ Language(16, translate('common.languages', 'Bihari', 'Language code: bh'), 'bh'),
+ Language(17, translate('common.languages', 'Bislama', 'Language code: bi'), 'bi'),
+ Language(18, translate('common.languages', 'Breton', 'Language code: br'), 'br'),
+ Language(19, translate('common.languages', 'Bulgarian', 'Language code: bg'), 'bg'),
+ Language(20, translate('common.languages', 'Burmese', 'Language code: my'), 'my'),
+ Language(21, translate('common.languages', 'Byelorussian', 'Language code: be'), 'be'),
+ Language(142, translate('common.languages', 'Cakchiquel', 'Language code: cak'), 'cak'),
+ Language(22, translate('common.languages', 'Cambodian', 'Language code: km'), 'km'),
+ Language(23, translate('common.languages', 'Catalan', 'Language code: ca'), 'ca'),
+ Language(24, translate('common.languages', 'Chinese', 'Language code: zh'), 'zh'),
+ Language(141, translate('common.languages', 'Comaltepec Chinantec', 'Language code: cco'), 'cco'),
+ Language(25, translate('common.languages', 'Corsican', 'Language code: co'), 'co'),
+ Language(26, translate('common.languages', 'Croatian', 'Language code: hr'), 'hr'),
+ Language(27, translate('common.languages', 'Czech', 'Language code: cs'), 'cs'),
+ Language(28, translate('common.languages', 'Danish', 'Language code: da'), 'da'),
+ Language(29, translate('common.languages', 'Dutch', 'Language code: nl'), 'nl'),
+ Language(30, translate('common.languages', 'English', 'Language code: en'), 'en'),
+ Language(31, translate('common.languages', 'Esperanto', 'Language code: eo'), 'eo'),
+ Language(32, translate('common.languages', 'Estonian', 'Language code: et'), 'et'),
+ Language(33, translate('common.languages', 'Faeroese', 'Language code: fo'), 'fo'),
+ Language(34, translate('common.languages', 'Fiji', 'Language code: fj'), 'fj'),
+ Language(35, translate('common.languages', 'Finnish', 'Language code: fi'), 'fi'),
+ Language(36, translate('common.languages', 'French', 'Language code: fr'), 'fr'),
+ Language(37, translate('common.languages', 'Frisian', 'Language code: fy'), 'fy'),
+ Language(38, translate('common.languages', 'Galician', 'Language code: gl'), 'gl'),
+ Language(39, translate('common.languages', 'Georgian', 'Language code: ka'), 'ka'),
+ Language(40, translate('common.languages', 'German', 'Language code: de'), 'de'),
+ Language(41, translate('common.languages', 'Greek', 'Language code: el'), 'el'),
+ Language(42, translate('common.languages', 'Greenlandic', 'Language code: kl'), 'kl'),
+ Language(43, translate('common.languages', 'Guarani', 'Language code: gn'), 'gn'),
+ Language(44, translate('common.languages', 'Gujarati', 'Language code: gu'), 'gu'),
+ Language(143, translate('common.languages', 'Haitian Creole', 'Language code: ht'), 'ht'),
+ Language(45, translate('common.languages', 'Hausa', 'Language code: ha'), 'ha'),
+ Language(46, translate('common.languages', 'Hebrew (former iw)', 'Language code: he'), 'he'),
+ Language(144, translate('common.languages', 'Hiligaynon', 'Language code: hil'), 'hil'),
+ Language(47, translate('common.languages', 'Hindi', 'Language code: hi'), 'hi'),
+ Language(48, translate('common.languages', 'Hungarian', 'Language code: hu'), 'hu'),
+ Language(49, translate('common.languages', 'Icelandic', 'Language code: is'), 'is'),
+ Language(50, translate('common.languages', 'Indonesian (former in)', 'Language code: id'), 'id'),
+ Language(51, translate('common.languages', 'Interlingua', 'Language code: ia'), 'ia'),
+ Language(52, translate('common.languages', 'Interlingue', 'Language code: ie'), 'ie'),
+ Language(54, translate('common.languages', 'Inuktitut (Eskimo)', 'Language code: iu'), 'iu'),
+ Language(53, translate('common.languages', 'Inupiak', 'Language code: ik'), 'ik'),
+ Language(55, translate('common.languages', 'Irish', 'Language code: ga'), 'ga'),
+ Language(56, translate('common.languages', 'Italian', 'Language code: it'), 'it'),
+ Language(145, translate('common.languages', 'Jakalteko', 'Language code: jac'), 'jac'),
+ Language(57, translate('common.languages', 'Japanese', 'Language code: ja'), 'ja'),
+ Language(58, translate('common.languages', 'Javanese', 'Language code: jw'), 'jw'),
+ Language(150, translate('common.languages', 'K\'iche\'', 'Language code: quc'), 'quc'),
+ Language(59, translate('common.languages', 'Kannada', 'Language code: kn'), 'kn'),
+ Language(60, translate('common.languages', 'Kashmiri', 'Language code: ks'), 'ks'),
+ Language(61, translate('common.languages', 'Kazakh', 'Language code: kk'), 'kk'),
+ Language(146, translate('common.languages', 'Kekchí ', 'Language code: kek'), 'kek'),
+ Language(62, translate('common.languages', 'Kinyarwanda', 'Language code: rw'), 'rw'),
+ Language(63, translate('common.languages', 'Kirghiz', 'Language code: ky'), 'ky'),
+ Language(64, translate('common.languages', 'Kirundi', 'Language code: rn'), 'rn'),
+ Language(65, translate('common.languages', 'Korean', 'Language code: ko'), 'ko'),
+ Language(66, translate('common.languages', 'Kurdish', 'Language code: ku'), 'ku'),
+ Language(67, translate('common.languages', 'Laothian', 'Language code: lo'), 'lo'),
+ Language(68, translate('common.languages', 'Latin', 'Language code: la'), 'la'),
+ Language(69, translate('common.languages', 'Latvian, Lettish', 'Language code: lv'), 'lv'),
+ Language(70, translate('common.languages', 'Lingala', 'Language code: ln'), 'ln'),
+ Language(71, translate('common.languages', 'Lithuanian', 'Language code: lt'), 'lt'),
+ Language(72, translate('common.languages', 'Macedonian', 'Language code: mk'), 'mk'),
+ Language(73, translate('common.languages', 'Malagasy', 'Language code: mg'), 'mg'),
+ Language(74, translate('common.languages', 'Malay', 'Language code: ms'), 'ms'),
+ Language(75, translate('common.languages', 'Malayalam', 'Language code: ml'), 'ml'),
+ Language(76, translate('common.languages', 'Maltese', 'Language code: mt'), 'mt'),
+ Language(148, translate('common.languages', 'Mam', 'Language code: mam'), 'mam'),
+ Language(77, translate('common.languages', 'Maori', 'Language code: mi'), 'mi'),
+ Language(147, translate('common.languages', 'Maori', 'Language code: mri'), 'mri'),
+ Language(78, translate('common.languages', 'Marathi', 'Language code: mr'), 'mr'),
+ Language(79, translate('common.languages', 'Moldavian', 'Language code: mo'), 'mo'),
+ Language(80, translate('common.languages', 'Mongolian', 'Language code: mn'), 'mn'),
+ Language(149, translate('common.languages', 'Nahuatl', 'Language code: nah'), 'nah'),
+ Language(81, translate('common.languages', 'Nauru', 'Language code: na'), 'na'),
+ Language(82, translate('common.languages', 'Nepali', 'Language code: ne'), 'ne'),
+ Language(83, translate('common.languages', 'Norwegian', 'Language code: no'), 'no'),
+ Language(84, translate('common.languages', 'Occitan', 'Language code: oc'), 'oc'),
+ Language(85, translate('common.languages', 'Oriya', 'Language code: or'), 'or'),
+ Language(86, translate('common.languages', 'Pashto, Pushto', 'Language code: ps'), 'ps'),
+ Language(87, translate('common.languages', 'Persian', 'Language code: fa'), 'fa'),
+ Language(151, translate('common.languages', 'Plautdietsch', 'Language code: pdt'), 'pdt'),
+ Language(88, translate('common.languages', 'Polish', 'Language code: pl'), 'pl'),
+ Language(89, translate('common.languages', 'Portuguese', 'Language code: pt'), 'pt'),
+ Language(90, translate('common.languages', 'Punjabi', 'Language code: pa'), 'pa'),
+ Language(91, translate('common.languages', 'Quechua', 'Language code: qu'), 'qu'),
+ Language(92, translate('common.languages', 'Rhaeto-Romance', 'Language code: rm'), 'rm'),
+ Language(93, translate('common.languages', 'Romanian', 'Language code: ro'), 'ro'),
+ Language(94, translate('common.languages', 'Russian', 'Language code: ru'), 'ru'),
+ Language(95, translate('common.languages', 'Samoan', 'Language code: sm'), 'sm'),
+ Language(96, translate('common.languages', 'Sangro', 'Language code: sg'), 'sg'),
+ Language(97, translate('common.languages', 'Sanskrit', 'Language code: sa'), 'sa'),
+ Language(98, translate('common.languages', 'Scots Gaelic', 'Language code: gd'), 'gd'),
+ Language(99, translate('common.languages', 'Serbian', 'Language code: sr'), 'sr'),
+ Language(100, translate('common.languages', 'Serbo-Croatian', 'Language code: sh'), 'sh'),
+ Language(101, translate('common.languages', 'Sesotho', 'Language code: st'), 'st'),
+ Language(102, translate('common.languages', 'Setswana', 'Language code: tn'), 'tn'),
+ Language(103, translate('common.languages', 'Shona', 'Language code: sn'), 'sn'),
+ Language(104, translate('common.languages', 'Sindhi', 'Language code: sd'), 'sd'),
+ Language(105, translate('common.languages', 'Singhalese', 'Language code: si'), 'si'),
+ Language(106, translate('common.languages', 'Siswati', 'Language code: ss'), 'ss'),
+ Language(107, translate('common.languages', 'Slovak', 'Language code: sk'), 'sk'),
+ Language(108, translate('common.languages', 'Slovenian', 'Language code: sl'), 'sl'),
+ Language(109, translate('common.languages', 'Somali', 'Language code: so'), 'so'),
+ Language(110, translate('common.languages', 'Spanish', 'Language code: es'), 'es'),
+ Language(111, translate('common.languages', 'Sudanese', 'Language code: su'), 'su'),
+ Language(112, translate('common.languages', 'Swahili', 'Language code: sw'), 'sw'),
+ Language(113, translate('common.languages', 'Swedish', 'Language code: sv'), 'sv'),
+ Language(114, translate('common.languages', 'Tagalog', 'Language code: tl'), 'tl'),
+ Language(115, translate('common.languages', 'Tajik', 'Language code: tg'), 'tg'),
+ Language(116, translate('common.languages', 'Tamil', 'Language code: ta'), 'ta'),
+ Language(117, translate('common.languages', 'Tatar', 'Language code: tt'), 'tt'),
+ Language(118, translate('common.languages', 'Tegulu', 'Language code: te'), 'te'),
+ Language(119, translate('common.languages', 'Thai', 'Language code: th'), 'th'),
+ Language(120, translate('common.languages', 'Tibetan', 'Language code: bo'), 'bo'),
+ Language(121, translate('common.languages', 'Tigrinya', 'Language code: ti'), 'ti'),
+ Language(122, translate('common.languages', 'Tonga', 'Language code: to'), 'to'),
+ Language(123, translate('common.languages', 'Tsonga', 'Language code: ts'), 'ts'),
+ Language(124, translate('common.languages', 'Turkish', 'Language code: tr'), 'tr'),
+ Language(125, translate('common.languages', 'Turkmen', 'Language code: tk'), 'tk'),
+ Language(126, translate('common.languages', 'Twi', 'Language code: tw'), 'tw'),
+ Language(127, translate('common.languages', 'Uigur', 'Language code: ug'), 'ug'),
+ Language(128, translate('common.languages', 'Ukrainian', 'Language code: uk'), 'uk'),
+ Language(129, translate('common.languages', 'Urdu', 'Language code: ur'), 'ur'),
+ Language(153, translate('common.languages', 'Uspanteco', 'Language code: usp'), 'usp'),
+ Language(130, translate('common.languages', 'Uzbek', 'Language code: uz'), 'uz'),
+ Language(131, translate('common.languages', 'Vietnamese', 'Language code: vi'), 'vi'),
+ Language(132, translate('common.languages', 'Volapuk', 'Language code: vo'), 'vo'),
+ Language(133, translate('common.languages', 'Welch', 'Language code: cy'), 'cy'),
+ Language(134, translate('common.languages', 'Wolof', 'Language code: wo'), 'wo'),
+ Language(135, translate('common.languages', 'Xhosa', 'Language code: xh'), 'xh'),
+ Language(136, translate('common.languages', 'Yiddish (former ji)', 'Language code: yi'), 'yi'),
+ Language(137, translate('common.languages', 'Yoruba', 'Language code: yo'), 'yo'),
+ Language(138, translate('common.languages', 'Zhuang', 'Language code: za'), 'za'),
+ Language(139, translate('common.languages', 'Zulu', 'Language code: zu'), 'zu')
+], key=lambda language: language.name)
+
+
+def get_language(name):
+ """
+ Find the language by its name or code.
+
+ :param name: The name or abbreviation of the language.
+ :return: The first match as a Language namedtuple or None
+ """
+ if name:
+ name_lower = name.lower()
+ name_title = name_lower[:1].upper() + name_lower[1:]
+ for language in languages:
+ if language.name == name_title or language.code == name_lower:
+ return language
+ return None
diff --git a/openlp/core/common/uistrings.py b/openlp/core/common/uistrings.py
index dccc3bdb4..e947b1362 100644
--- a/openlp/core/common/uistrings.py
+++ b/openlp/core/common/uistrings.py
@@ -23,6 +23,7 @@
The :mod:`uistrings` module provides standard strings for OpenLP.
"""
import logging
+import itertools
from openlp.core.common import translate
@@ -155,3 +156,30 @@ class UiStrings(object):
self.View = translate('OpenLP.Ui', 'View')
self.ViewMode = translate('OpenLP.Ui', 'View Mode')
self.Video = translate('OpenLP.Ui', 'Video')
+ self.BibleShortSearchTitle = translate('OpenLP.Ui', 'Search is Empty or too Short')
+ self.BibleShortSearch = translate('OpenLP.Ui', 'The search you have entered is empty or shorter '
+ 'than 3 characters long.
Please try again with '
+ 'a longer search.')
+ self.BibleNoBiblesTitle = translate('OpenLP.Ui', 'No Bibles Available')
+ self.BibleNoBibles = translate('OpenLP.Ui', 'There are no Bibles currently installed.
'
+ 'Please use the Import Wizard to install one or more Bibles.')
+ book_chapter = translate('OpenLP.Ui', 'Book Chapter')
+ chapter = translate('OpenLP.Ui', 'Chapter')
+ verse = translate('OpenLP.Ui', 'Verse')
+ gap = ' | '
+ psalm = translate('OpenLP.Ui', 'Psalm')
+ may_shorten = translate('OpenLP.Ui', 'Book names may be shortened from full names, for an example Ps 23 = '
+ 'Psalm 23')
+ bible_scripture_items = \
+ [book_chapter, gap, psalm, ' 23
',
+ book_chapter, '%(range)s', chapter, gap, psalm, ' 23%(range)s24
',
+ book_chapter, '%(verse)s', verse, '%(range)s', verse, gap, psalm, ' 23%(verse)s1%(range)s2
',
+ book_chapter, '%(verse)s', verse, '%(range)s', verse, '%(list)s', verse, '%(range)s', verse, gap, psalm,
+ ' 23%(verse)s1%(range)s2%(list)s5%(range)s6
',
+ book_chapter, '%(verse)s', verse, '%(range)s', verse, '%(list)s', chapter, '%(verse)s', verse, '%(range)s',
+ verse, gap, psalm, ' 23%(verse)s1%(range)s2%(list)s24%(verse)s1%(range)s3
',
+ book_chapter, '%(verse)s', verse, '%(range)s', chapter, '%(verse)s', verse, gap, psalm,
+ ' 23%(verse)s1%(range)s24%(verse)s1
', may_shorten]
+ itertools.chain.from_iterable(itertools.repeat(strings, 1) if isinstance(strings, str)
+ else strings for strings in bible_scripture_items)
+ self.BibleScriptureError = ''.join(str(joined) for joined in bible_scripture_items)
diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py
index 6a83f955d..2edea93cf 100644
--- a/openlp/core/lib/mediamanageritem.py
+++ b/openlp/core/lib/mediamanageritem.py
@@ -652,6 +652,20 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties):
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/screen.py b/openlp/core/lib/screen.py
index e7b4c0b97..31ff3d725 100644
--- a/openlp/core/lib/screen.py
+++ b/openlp/core/lib/screen.py
@@ -167,7 +167,7 @@ class ScreenList(object):
:param number: The screen number (int).
"""
- log.info('remove_screen {number:d}'.forma(number=number))
+ log.info('remove_screen {number:d}'.format(number=number))
for screen in self.screen_list:
if screen['number'] == number:
self.screen_list.remove(screen)
diff --git a/openlp/plugins/bibles/bibleplugin.py b/openlp/plugins/bibles/bibleplugin.py
index ccc61ba56..3d27effe4 100644
--- a/openlp/plugins/bibles/bibleplugin.py
+++ b/openlp/plugins/bibles/bibleplugin.py
@@ -41,7 +41,8 @@ __default_settings__ = {
'bibles/db password': '',
'bibles/db hostname': '',
'bibles/db database': '',
- 'bibles/last search type': BibleSearch.Reference,
+ 'bibles/last search type': BibleSearch.Combined,
+ 'bibles/reset to combined quick search': True,
'bibles/verse layout style': LayoutStyle.VersePerSlide,
'bibles/book name language': LanguageSelection.Bible,
'bibles/display brackets': DisplayStyle.NoBrackets,
@@ -59,7 +60,9 @@ __default_settings__ = {
'bibles/range separator': '',
'bibles/list separator': '',
'bibles/end separator': '',
- 'bibles/last directory import': ''
+ 'bibles/last directory import': '',
+ 'bibles/hide combined quick error': False,
+ 'bibles/is search while typing enabled': True
}
diff --git a/openlp/plugins/bibles/forms/languageform.py b/openlp/plugins/bibles/forms/languageform.py
index 461f02c74..1ba71ad6e 100644
--- a/openlp/plugins/bibles/forms/languageform.py
+++ b/openlp/plugins/bibles/forms/languageform.py
@@ -29,9 +29,9 @@ from PyQt5.QtWidgets import QDialog
from PyQt5 import QtCore
from openlp.core.common import translate
+from openlp.core.common.languages import languages
from openlp.core.lib.ui import critical_error_message_box
from openlp.plugins.bibles.forms.languagedialog import Ui_LanguageDialog
-from openlp.plugins.bibles.lib.db import BiblesResourcesDB
log = logging.getLogger(__name__)
@@ -51,11 +51,11 @@ class LanguageForm(QDialog, Ui_LanguageDialog):
self.setupUi(self)
def exec(self, bible_name):
- self.language_combo_box.addItem('')
if bible_name:
- self.bible_label.setText(str(bible_name))
- items = BiblesResourcesDB.get_languages()
- self.language_combo_box.addItems([item['name'] for item in items])
+ self.bible_label.setText(bible_name)
+ self.language_combo_box.addItem('')
+ for language in languages:
+ self.language_combo_box.addItem(language.name, language.id)
return QDialog.exec(self)
def accept(self):
diff --git a/openlp/plugins/bibles/lib/biblestab.py b/openlp/plugins/bibles/lib/biblestab.py
index 5438d07f2..c087335ae 100644
--- a/openlp/plugins/bibles/lib/biblestab.py
+++ b/openlp/plugins/bibles/lib/biblestab.py
@@ -128,6 +128,20 @@ class BiblesTab(SettingsTab):
self.language_selection_layout.addWidget(self.language_selection_label)
self.language_selection_layout.addWidget(self.language_selection_combo_box)
self.right_layout.addWidget(self.language_selection_group_box)
+ self.bible_quick_settings_group_box = QtWidgets.QGroupBox(self.right_column)
+ self.bible_quick_settings_group_box.setObjectName('bible_quick_settings_group_box')
+ self.right_layout.addWidget(self.bible_quick_settings_group_box)
+ self.search_settings_layout = QtWidgets.QFormLayout(self.bible_quick_settings_group_box)
+ self.search_settings_layout.setObjectName('search_settings_layout')
+ self.reset_to_combined_quick_search_check_box = QtWidgets.QCheckBox(self.bible_quick_settings_group_box)
+ self.reset_to_combined_quick_search_check_box.setObjectName('reset_to_combined_quick_search_check_box')
+ self.search_settings_layout.addRow(self.reset_to_combined_quick_search_check_box)
+ self.hide_combined_quick_error_check_box = QtWidgets.QCheckBox(self.bible_quick_settings_group_box)
+ self.hide_combined_quick_error_check_box.setObjectName('hide_combined_quick_error_check_box')
+ self.search_settings_layout.addRow(self.hide_combined_quick_error_check_box)
+ self.bible_search_while_typing_check_box = QtWidgets.QCheckBox(self.bible_quick_settings_group_box)
+ self.bible_search_while_typing_check_box.setObjectName('bible_search_while_typing_check_box')
+ self.search_settings_layout.addRow(self.bible_search_while_typing_check_box)
self.left_layout.addStretch()
self.right_layout.addStretch()
# Signals and slots
@@ -151,6 +165,12 @@ class BiblesTab(SettingsTab):
self.end_separator_line_edit.editingFinished.connect(self.on_end_separator_line_edit_finished)
Registry().register_function('theme_update_list', self.update_theme_list)
self.language_selection_combo_box.activated.connect(self.on_language_selection_combo_box_changed)
+ self.reset_to_combined_quick_search_check_box.stateChanged.connect(
+ self.on_reset_to_combined_quick_search_check_box_changed)
+ self.hide_combined_quick_error_check_box.stateChanged.connect(
+ self.on_hide_combined_quick_error_check_box_changed)
+ self.bible_search_while_typing_check_box.stateChanged.connect(
+ self.on_bible_search_while_typing_check_box_changed)
def retranslateUi(self):
self.verse_display_group_box.setTitle(translate('BiblesPlugin.BiblesTab', 'Verse Display'))
@@ -194,6 +214,17 @@ class BiblesTab(SettingsTab):
LanguageSelection.Application, translate('BiblesPlugin.BiblesTab', 'Application Language'))
self.language_selection_combo_box.setItemText(
LanguageSelection.English, translate('BiblesPlugin.BiblesTab', 'English'))
+ self.bible_quick_settings_group_box.setTitle(translate('BiblesPlugin.BiblesTab', 'Quick Search Settings'))
+ self.reset_to_combined_quick_search_check_box.setText(translate('BiblesPlugin.BiblesTab',
+ 'Reset search type to "Text or Scripture'
+ ' Reference" on startup'))
+ self.hide_combined_quick_error_check_box.setText(translate('BiblesPlugin.BiblesTab',
+ 'Don\'t show error if nothing is found in "Text or '
+ 'Scripture Reference"'))
+ self.bible_search_while_typing_check_box.setText(translate('BiblesPlugin.BiblesTab',
+ 'Search automatically while typing (Text search must'
+ ' contain a\nminimum of {count} characters and a '
+ 'space for performance reasons)').format(count='8'))
def on_bible_theme_combo_box_changed(self):
self.bible_theme = self.bible_theme_combo_box.currentText()
@@ -302,6 +333,24 @@ class BiblesTab(SettingsTab):
self.end_separator_line_edit.setText(get_reference_separator('sep_e_default'))
self.end_separator_line_edit.setPalette(self.get_grey_text_palette(True))
+ def on_reset_to_combined_quick_search_check_box_changed(self, check_state):
+ """
+ Event handler for the 'hide_combined_quick_error' check box
+ """
+ self.reset_to_combined_quick_search = (check_state == QtCore.Qt.Checked)
+
+ def on_hide_combined_quick_error_check_box_changed(self, check_state):
+ """
+ Event handler for the 'hide_combined_quick_error' check box
+ """
+ self.hide_combined_quick_error = (check_state == QtCore.Qt.Checked)
+
+ def on_bible_search_while_typing_check_box_changed(self, check_state):
+ """
+ Event handler for the 'hide_combined_quick_error' check box
+ """
+ self.bible_search_while_typing = (check_state == QtCore.Qt.Checked)
+
def load(self):
settings = Settings()
settings.beginGroup(self.settings_section)
@@ -355,6 +404,12 @@ class BiblesTab(SettingsTab):
self.end_separator_check_box.setChecked(True)
self.language_selection = settings.value('book name language')
self.language_selection_combo_box.setCurrentIndex(self.language_selection)
+ self.reset_to_combined_quick_search = settings.value('reset to combined quick search')
+ self.reset_to_combined_quick_search_check_box.setChecked(self.reset_to_combined_quick_search)
+ self.hide_combined_quick_error = settings.value('hide combined quick error')
+ self.hide_combined_quick_error_check_box.setChecked(self.hide_combined_quick_error)
+ self.bible_search_while_typing = settings.value('is search while typing enabled')
+ self.bible_search_while_typing_check_box.setChecked(self.bible_search_while_typing)
settings.endGroup()
def save(self):
@@ -386,6 +441,9 @@ class BiblesTab(SettingsTab):
if self.language_selection != settings.value('book name language'):
settings.setValue('book name language', self.language_selection)
self.settings_form.register_post_process('bibles_load_list')
+ settings.setValue('reset to combined quick search', self.reset_to_combined_quick_search)
+ settings.setValue('hide combined quick error', self.hide_combined_quick_error)
+ settings.setValue('is search while typing enabled', self.bible_search_while_typing)
settings.endGroup()
if self.tab_visited:
self.settings_form.register_post_process('bibles_config_updated')
diff --git a/openlp/plugins/bibles/lib/db.py b/openlp/plugins/bibles/lib/db.py
index c23ade4a7..ff305dec6 100644
--- a/openlp/plugins/bibles/lib/db.py
+++ b/openlp/plugins/bibles/lib/db.py
@@ -128,6 +128,12 @@ class BibleDB(Manager, RegistryProperties):
The name of the database. This is also used as the file name for SQLite databases.
"""
log.info('BibleDB loaded')
+ self._setup(parent, **kwargs)
+
+ def _setup(self, parent, **kwargs):
+ """
+ Run some initial setup. This method is separate from __init__ in order to mock it out in tests.
+ """
self.bible_plugin = parent
self.session = None
if 'path' not in kwargs:
@@ -465,14 +471,13 @@ class BibleDB(Manager, RegistryProperties):
"""
log.debug('BibleDB.get_language()')
from openlp.plugins.bibles.forms import LanguageForm
- language = None
+ language_id = None
language_form = LanguageForm(self.wizard)
if language_form.exec(bible_name):
- language = str(language_form.language_combo_box.currentText())
- if not language:
+ combo_box = language_form.language_combo_box
+ language_id = combo_box.itemData(combo_box.currentIndex())
+ if not language_id:
return False
- language = BiblesResourcesDB.get_language(language)
- language_id = language['id']
self.save_meta('language_id', language_id)
return language_id
@@ -767,43 +772,6 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
return book[0]
return None
- @staticmethod
- def get_language(name):
- """
- Return a dict containing the language id, name and code by name or abbreviation.
-
- :param name: The name or abbreviation of the language.
- """
- log.debug('BiblesResourcesDB.get_language("{name}")'.format(name=name))
- if not isinstance(name, str):
- name = str(name)
- language = BiblesResourcesDB.run_sql(
- 'SELECT id, name, code FROM language WHERE name = ? OR code = ?', (name, name.lower()))
- if language:
- return {
- 'id': language[0][0],
- 'name': str(language[0][1]),
- 'code': str(language[0][2])
- }
- else:
- return None
-
- @staticmethod
- def get_languages():
- """
- Return a dict containing all languages with id, name and code.
- """
- log.debug('BiblesResourcesDB.get_languages()')
- languages = BiblesResourcesDB.run_sql('SELECT id, name, code FROM language ORDER by name')
- if languages:
- return [{
- 'id': language[0],
- 'name': str(language[1]),
- 'code': str(language[2])
- } for language in languages]
- else:
- return None
-
@staticmethod
def get_testament_reference():
"""
@@ -1014,7 +982,7 @@ class OldBibleDB(QtCore.QObject, Manager):
def get_verses(self, book_id):
"""
- Returns the verses of the Bible.
+ Returns the verses of the Bible.
"""
verses = self.run_sql(
'SELECT book_id, chapter, verse, text FROM verse WHERE book_id = ? ORDER BY id', (book_id, ))
diff --git a/openlp/plugins/bibles/lib/manager.py b/openlp/plugins/bibles/lib/manager.py
index 1c55222f2..9ea4c6612 100644
--- a/openlp/plugins/bibles/lib/manager.py
+++ b/openlp/plugins/bibles/lib/manager.py
@@ -23,8 +23,8 @@
import logging
import os
-from openlp.core.common import RegistryProperties, AppLocation, Settings, translate, delete_file
-from openlp.plugins.bibles.lib import parse_reference, get_reference_separator, LanguageSelection
+from openlp.core.common import RegistryProperties, AppLocation, Settings, translate, delete_file, UiStrings
+from openlp.plugins.bibles.lib import parse_reference, LanguageSelection
from openlp.plugins.bibles.lib.db import BibleDB, BibleMeta
from .csvbible import CSVBible
from .http import HTTPBible
@@ -267,42 +267,21 @@ class BibleManager(RegistryProperties):
For second bible this is necessary.
:param show_error:
"""
+ # If no bibles are installed, message is given.
log.debug('BibleManager.get_verses("{bible}", "{verse}")'.format(bible=bible, verse=verse_text))
if not bible:
if show_error:
self.main_window.information_message(
- translate('BiblesPlugin.BibleManager', 'No Bibles Available'),
- translate('BiblesPlugin.BibleManager', 'There are no Bibles currently installed. Please use the '
- 'Import Wizard to install one or more Bibles.')
- )
+ UiStrings().BibleNoBiblesTitle,
+ UiStrings().BibleNoBibles)
return None
+ # Get the language for books.
language_selection = self.get_language_selection(bible)
ref_list = parse_reference(verse_text, self.db_cache[bible], language_selection, book_ref_id)
if ref_list:
return self.db_cache[bible].get_verses(ref_list, show_error)
+ # If nothing is found. Message is given if this is not combined search. (defined in mediaitem.py)
else:
- if show_error:
- reference_separators = {
- 'verse': get_reference_separator('sep_v_display'),
- 'range': get_reference_separator('sep_r_display'),
- 'list': get_reference_separator('sep_l_display')}
- self.main_window.information_message(
- translate('BiblesPlugin.BibleManager', 'Scripture Reference Error'),
- translate('BiblesPlugin.BibleManager', 'Your scripture reference is either not supported by '
- 'OpenLP or is invalid. Please make sure your reference '
- 'conforms to one of the following patterns or consult the manual:\n\n'
- 'Book Chapter\n'
- 'Book Chapter%(range)sChapter\n'
- 'Book Chapter%(verse)sVerse%(range)sVerse\n'
- 'Book Chapter%(verse)sVerse%(range)sVerse%(list)sVerse'
- '%(range)sVerse\n'
- 'Book Chapter%(verse)sVerse%(range)sVerse%(list)sChapter'
- '%(verse)sVerse%(range)sVerse\n'
- 'Book Chapter%(verse)sVerse%(range)sChapter%(verse)sVerse',
- 'Please pay attention to the appended "s" of the wildcards '
- 'and refrain from translating the words inside the names in the brackets.')
- % reference_separators
- )
return None
def get_language_selection(self, bible):
@@ -334,13 +313,11 @@ class BibleManager(RegistryProperties):
:param text: The text to search for (unicode).
"""
log.debug('BibleManager.verse_search("{bible}", "{text}")'.format(bible=bible, text=text))
+ # If no bibles are installed, message is given.
if not bible:
self.main_window.information_message(
- translate('BiblesPlugin.BibleManager', 'No Bibles Available'),
- translate('BiblesPlugin.BibleManager',
- 'There are no Bibles currently installed. Please use the Import Wizard to install one or '
- 'more Bibles.')
- )
+ UiStrings().BibleNoBiblesTitle,
+ UiStrings().BibleNoBibles)
return None
# Check if the bible or second_bible is a web bible.
web_bible = self.db_cache[bible].get_object(BibleMeta, 'download_source')
@@ -348,20 +325,56 @@ class BibleManager(RegistryProperties):
if second_bible:
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 message is given.
+ self.application.set_normal_cursor()
self.main_window.information_message(
- translate('BiblesPlugin.BibleManager', 'Web Bible cannot be used'),
- translate('BiblesPlugin.BibleManager', 'Text Search is not available with Web Bibles.')
+ translate('BiblesPlugin.BibleManager', 'Web Bible cannot be used in Text Search'),
+ translate('BiblesPlugin.BibleManager', 'Text Search is not available with Web Bibles.\n'
+ 'Please use the Scripture Reference Search instead.\n\n'
+ 'This means that the currently used Bible\nor Second Bible '
+ 'is installed as Web Bible.\n\n'
+ 'If you were trying to perform a Reference search\nin Combined '
+ 'Search, your reference is invalid.')
)
return None
- if text:
+ # Shorter than 3 char searches break OpenLP with very long search times, thus they are blocked.
+ if len(text) - text.count(' ') < 3:
+ return None
+ # Fetch the results from db. If no results are found, return None, no message is given for this.
+ elif text:
+ return self.db_cache[bible].verse_search(text)
+ else:
+ return None
+
+ def verse_search_while_typing(self, bible, second_bible, text):
+ """
+ Does a verse search for the given bible and text.
+ This is used during "Search while typing"
+ It's the same thing as the normal text search, but it does not show the web Bible error.
+ (It would result in the error popping every time a char is entered or removed)
+ It also does not have a minimum text len, this is set in mediaitem.py
+
+ :param bible: The bible to search in (unicode).
+ :param second_bible: The second bible (unicode). We do not search in this bible.
+ :param text: The text to search for (unicode).
+ """
+ # If no bibles are installed, message is given.
+ if not bible:
+ return None
+ # Check if the bible or second_bible is a web bible.
+ web_bible = self.db_cache[bible].get_object(BibleMeta, 'download_source')
+ second_web_bible = ''
+ if second_bible:
+ 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.
+ elif text:
return self.db_cache[bible].verse_search(text)
else:
- self.main_window.information_message(
- translate('BiblesPlugin.BibleManager', 'Scripture Reference Error'),
- translate('BiblesPlugin.BibleManager', 'You did not enter a search keyword.\nYou can separate '
- 'different keywords by a space to search for all of your keywords and you can separate '
- 'them by a comma to search for one of them.')
- )
return None
def save_meta_data(self, bible, version, copyright, permissions, book_name_language=None):
diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py
index d5304e867..debf786f7 100644
--- a/openlp/plugins/bibles/lib/mediaitem.py
+++ b/openlp/plugins/bibles/lib/mediaitem.py
@@ -35,6 +35,7 @@ from openlp.plugins.bibles.forms.editbibleform import EditBibleForm
from openlp.plugins.bibles.lib import LayoutStyle, DisplayStyle, VerseReferenceList, get_reference_separator, \
LanguageSelection, BibleStrings
from openlp.plugins.bibles.lib.db import BiblesResourcesDB
+import re
log = logging.getLogger(__name__)
@@ -45,6 +46,7 @@ class BibleSearch(object):
"""
Reference = 1
Text = 2
+ Combined = 3
class BibleMediaItem(MediaManagerItem):
@@ -56,6 +58,7 @@ class BibleMediaItem(MediaManagerItem):
log.info('Bible Media Item loaded')
def __init__(self, parent, plugin):
+ self.clear_icon = build_icon(':/bibles/bibles_search_clear.png')
self.lock_icon = build_icon(':/bibles/bibles_search_lock.png')
self.unlock_icon = build_icon(':/bibles/bibles_search_unlock.png')
MediaManagerItem.__init__(self, parent, plugin)
@@ -157,10 +160,15 @@ class BibleMediaItem(MediaManagerItem):
search_button_layout = QtWidgets.QHBoxLayout()
search_button_layout.setObjectName(prefix + 'search_button_layout')
search_button_layout.addStretch()
+ # Note: If we use QPushButton instead of the QToolButton, the icon will be larger than the Lock icon.
+ clear_button = QtWidgets.QToolButton(tab)
+ clear_button.setIcon(self.clear_icon)
+ clear_button.setObjectName(prefix + 'ClearButton')
lock_button = QtWidgets.QToolButton(tab)
lock_button.setIcon(self.unlock_icon)
lock_button.setCheckable(True)
lock_button.setObjectName(prefix + 'LockButton')
+ search_button_layout.addWidget(clear_button)
search_button_layout.addWidget(lock_button)
search_button = QtWidgets.QPushButton(tab)
search_button.setObjectName(prefix + 'SearchButton')
@@ -176,6 +184,7 @@ class BibleMediaItem(MediaManagerItem):
setattr(self, prefix + 'SecondComboBox', second_combo_box)
setattr(self, prefix + 'StyleLabel', style_label)
setattr(self, prefix + 'StyleComboBox', style_combo_box)
+ setattr(self, prefix + 'ClearButton', clear_button)
setattr(self, prefix + 'LockButton', lock_button)
setattr(self, prefix + 'SearchButtonLayout', search_button_layout)
setattr(self, prefix + 'SearchButton', search_button)
@@ -245,11 +254,14 @@ 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.advancedSearchButton.clicked.connect(self.on_advanced_search_button)
self.quickSearchButton.clicked.connect(self.on_quick_search_button)
# Other stuff
self.quick_search_edit.returnPressed.connect(self.on_quick_search_button)
self.search_tab_bar.currentChanged.connect(self.on_search_tab_bar_current_changed)
+ self.quick_search_edit.textChanged.connect(self.on_search_text_edit_changed)
def on_focus(self):
if self.quickTab.isVisible():
@@ -286,6 +298,7 @@ class BibleMediaItem(MediaManagerItem):
self.quickStyleComboBox.setItemText(LayoutStyle.VersePerSlide, UiStrings().VersePerSlide)
self.quickStyleComboBox.setItemText(LayoutStyle.VersePerLine, UiStrings().VersePerLine)
self.quickStyleComboBox.setItemText(LayoutStyle.Continuous, UiStrings().Continuous)
+ self.quickClearButton.setToolTip(translate('BiblesPlugin.MediaItem', 'Clear the search results.'))
self.quickLockButton.setToolTip(translate('BiblesPlugin.MediaItem',
'Toggle to keep or clear the previous results.'))
self.quickSearchButton.setText(UiStrings().Search)
@@ -300,6 +313,7 @@ class BibleMediaItem(MediaManagerItem):
self.advancedStyleComboBox.setItemText(LayoutStyle.VersePerSlide, UiStrings().VersePerSlide)
self.advancedStyleComboBox.setItemText(LayoutStyle.VersePerLine, UiStrings().VersePerLine)
self.advancedStyleComboBox.setItemText(LayoutStyle.Continuous, UiStrings().Continuous)
+ self.advancedClearButton.setToolTip(translate('BiblesPlugin.MediaItem', 'Clear the search results.'))
self.advancedLockButton.setToolTip(translate('BiblesPlugin.MediaItem',
'Toggle to keep or clear the previous results.'))
self.advancedSearchButton.setText(UiStrings().Search)
@@ -309,6 +323,9 @@ class BibleMediaItem(MediaManagerItem):
self.plugin.manager.media = self
self.load_bibles()
self.quick_search_edit.set_search_types([
+ (BibleSearch.Combined, ':/bibles/bibles_search_combined.png',
+ translate('BiblesPlugin.MediaItem', 'Text or Reference'),
+ translate('BiblesPlugin.MediaItem', 'Text or Reference...')),
(BibleSearch.Reference, ':/bibles/bibles_search_reference.png',
translate('BiblesPlugin.MediaItem', 'Scripture Reference'),
translate('BiblesPlugin.MediaItem', 'Search Scripture Reference...')),
@@ -424,18 +441,24 @@ class BibleMediaItem(MediaManagerItem):
def update_auto_completer(self):
"""
This updates the bible book completion list for the search field. The completion depends on the bible. It is
- only updated when we are doing a reference search, otherwise the auto completion list is removed.
+ 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.
- Settings().setValue('{section}/last search type'.format(section=self.settings_section),
- self.quick_search_edit.current_search_type())
+ # 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())
books = []
- # We have to do a 'Reference Search'.
- if self.quick_search_edit.current_search_type() == BibleSearch.Reference:
+ # We have to do a 'Reference Search' (Or as part of Combined Search).
+ if self.quick_search_edit.current_search_type() is not BibleSearch.Text:
bibles = self.plugin.manager.get_bibles()
bible = self.quickVersionComboBox.currentText()
if bible:
@@ -525,7 +548,15 @@ class BibleMediaItem(MediaManagerItem):
self.advancedTab.setVisible(True)
self.advanced_book_combo_box.setFocus()
+ def on_clear_button(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_lock_button_toggled(self, checked):
+ self.quick_search_edit.setFocus()
if checked:
self.sender().setIcon(self.lock_icon)
else:
@@ -652,10 +683,120 @@ class BibleMediaItem(MediaManagerItem):
self.check_search_result()
self.application.set_normal_cursor()
+ def on_quick_reference_search(self):
+ """
+ We are doing a 'Reference Search'.
+ This search is called on def on_quick_search_button by Quick Reference and Combined Searches.
+ """
+ # Set Bibles to use the text input from Quick search field.
+ bible = self.quickVersionComboBox.currentText()
+ second_bible = self.quickSecondComboBox.currentText()
+ """
+ Get input from field and replace 'A-Z + . ' with ''
+ This will check if field has any '.' after A-Z and removes them. Eg. Gen. 1 = Ge 1 = Genesis 1
+ If Book name has '.' after number. eg. 1. Genesis, the search fails without the dot, and vice versa.
+ A better solution would be to make '.' optional in the search results. Current solution was easier to code.
+ """
+ text = self.quick_search_edit.text()
+ text = re.sub('\D[.]\s', ' ', text)
+ # This is triggered on reference search, use the search from manager.py
+ if self.quick_search_edit.current_search_type() != BibleSearch.Text:
+ self.search_results = self.plugin.manager.get_verses(bible, text)
+ if second_bible and self.search_results:
+ self.second_search_results = \
+ self.plugin.manager.get_verses(second_bible, text, self.search_results[0].book.book_reference_id)
+
+ def on_quick_text_search(self):
+ """
+ We are doing a 'Text Search'.
+ This search is called on def on_quick_search_button by Quick Text and Combined Searches.
+ """
+ # Set Bibles to use the text input from Quick search field.
+ bible = self.quickVersionComboBox.currentText()
+ second_bible = self.quickSecondComboBox.currentText()
+ text = self.quick_search_edit.text()
+ # If Text search ends with "," OpenLP will crash, prevent this from happening by removing all ","s.
+ text = re.sub('[,]', '', text)
+ self.application.set_busy_cursor()
+ # Get Bibles list
+ bibles = self.plugin.manager.get_bibles()
+ # Add results to "search_results"
+ self.search_results = self.plugin.manager.verse_search(bible, second_bible, text)
+ if second_bible and self.search_results:
+ # new_search_results is needed to make sure 2nd bible contains all verses. (And counting them on error)
+ text = []
+ new_search_results = []
+ count = 0
+ passage_not_found = False
+ # Search second bible for results of search_results to make sure everythigns there.
+ # Count all the unfound passages.
+ for verse in self.search_results:
+ db_book = bibles[second_bible].get_book_by_book_ref_id(verse.book.book_reference_id)
+ if not db_book:
+ log.debug('Passage "{name} {chapter:d}:{verse:d}" not found in '
+ 'Second Bible'.format(name=verse.book.name, chapter=verse.chapter, verse=verse.verse))
+ passage_not_found = True
+ count += 1
+ continue
+ new_search_results.append(verse)
+ text.append((verse.book.book_reference_id, verse.chapter, verse.verse, verse.verse))
+ if passage_not_found:
+ # This is for the 2nd Bible.
+ self.main_window.information_message(
+ translate('BiblesPlugin.MediaItem', 'Information'),
+ translate('BiblesPlugin.MediaItem', 'The second Bible does not contain all the verses '
+ 'that are in the main Bible.\nOnly verses found in both Bibles'
+ ' will be shown.\n\n{count:d} verses have not been included '
+ 'in the results.').format(count=count))
+ # Join the searches so only verses that are found on both Bibles are shown.
+ self.search_results = new_search_results
+ self.second_search_results = bibles[second_bible].get_verses(text)
+
+ def on_quick_text_search_while_typing(self):
+ """
+ We are doing a 'Text Search' while typing
+ Call the verse_search_while_typing from manager.py
+ It does not show web bible errors while typing.
+ (It would result in the error popping every time a char is entered or removed)
+ """
+ # Set Bibles to use the text input from Quick search field.
+ bible = self.quickVersionComboBox.currentText()
+ second_bible = self.quickSecondComboBox.currentText()
+ text = self.quick_search_edit.text()
+ # If Text search ends with "," OpenLP will crash, prevent this from happening by removing all ","s.
+ text = re.sub('[,]', '', text)
+ self.application.set_busy_cursor()
+ # Get Bibles list
+ bibles = self.plugin.manager.get_bibles()
+ # Add results to "search_results"
+ self.search_results = self.plugin.manager.verse_search_while_typing(bible, second_bible, text)
+ if second_bible and self.search_results:
+ # new_search_results is needed to make sure 2nd bible contains all verses. (And counting them on error)
+ text = []
+ new_search_results = []
+ count = 0
+ passage_not_found = False
+ # Search second bible for results of search_results to make sure everythigns there.
+ # Count all the unfound passages. Even thou no error is shown, this needs to be done or
+ # the code breaks later on.
+ for verse in self.search_results:
+ db_book = bibles[second_bible].get_book_by_book_ref_id(verse.book.book_reference_id)
+ if not db_book:
+ log.debug('Passage ("{versebookname}","{versechapter}","{verseverse}") not found in Second Bible'
+ .format(versebookname=verse.book.name, versechapter='verse.chapter',
+ verseverse=verse.verse))
+ count += 1
+ continue
+ new_search_results.append(verse)
+ text.append((verse.book.book_reference_id, verse.chapter, verse.verse, verse.verse))
+ # Join the searches so only verses that are found on both Bibles are shown.
+ self.search_results = new_search_results
+ self.second_search_results = bibles[second_bible].get_verses(text)
+
def on_quick_search_button(self):
"""
- Does a quick search and saves the search results. Quick search can either be "Reference Search" or
- "Text Search".
+ This triggers the proper Quick search based on which search type is used.
+ "Eg. "Reference Search", "Text Search" or "Combined search".
"""
log.debug('Quick Search Button clicked')
self.quickSearchButton.setEnabled(False)
@@ -664,41 +805,68 @@ class BibleMediaItem(MediaManagerItem):
second_bible = self.quickSecondComboBox.currentText()
text = self.quick_search_edit.text()
if self.quick_search_edit.current_search_type() == BibleSearch.Reference:
- # We are doing a 'Reference Search'.
- self.search_results = self.plugin.manager.get_verses(bible, text)
- if second_bible and self.search_results:
- self.second_search_results = \
- self.plugin.manager.get_verses(second_bible, text, self.search_results[0].book.book_reference_id)
- else:
- # We are doing a 'Text Search'.
- self.application.set_busy_cursor()
- bibles = self.plugin.manager.get_bibles()
- self.search_results = self.plugin.manager.verse_search(bible, second_bible, text)
- if second_bible and self.search_results:
- text = []
- new_search_results = []
- count = 0
- passage_not_found = False
- for verse in self.search_results:
- db_book = bibles[second_bible].get_book_by_book_ref_id(verse.book.book_reference_id)
- if not db_book:
- log.debug('Passage "{name} {chapter:d}:{verse:d}" not found in '
- 'Second Bible'.format(name=verse.book.name, chapter=verse.chapter, verse=verse.verse))
- passage_not_found = True
- count += 1
- continue
- new_search_results.append(verse)
- text.append((verse.book.book_reference_id, verse.chapter, verse.verse, verse.verse))
- if passage_not_found:
- QtWidgets.QMessageBox.information(
- self, translate('BiblesPlugin.MediaItem', 'Information'),
- translate('BiblesPlugin.MediaItem',
- 'The second Bible does not contain all the verses that are in the main Bible. '
- 'Only verses found in both Bibles will be shown. {count:d} verses have not been '
- 'included in the results.').format(count=count),
- QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Ok))
- self.search_results = new_search_results
- self.second_search_results = bibles[second_bible].get_verses(text)
+ # We are doing a 'Reference Search'. (Get script from def on_quick_reference_search)
+ self.on_quick_reference_search()
+ # Get reference separators from settings.
+ if not self.search_results:
+ reference_separators = {
+ 'verse': get_reference_separator('sep_v_display'),
+ 'range': get_reference_separator('sep_r_display'),
+ 'list': get_reference_separator('sep_l_display')}
+ self.main_window.information_message(
+ translate('BiblesPlugin.BibleManager', 'Scripture Reference Error'),
+ translate('BiblesPlugin.BibleManager', 'OpenLP couldn’t find anything '
+ 'with your search.
'
+ 'Please make sure that your reference follows '
+ 'one of these patterns:
%s'
+ % UiStrings().BibleScriptureError % reference_separators))
+ elif self.quick_search_edit.current_search_type() == BibleSearch.Text:
+ # We are doing a 'Text Search'. (Get script from def on_quick_text_search)
+ self.on_quick_text_search()
+ if not self.search_results and len(text) - text.count(' ') < 3 and bible:
+ self.main_window.information_message(
+ UiStrings().BibleShortSearchTitle,
+ UiStrings().BibleShortSearch)
+ elif self.quick_search_edit.current_search_type() == BibleSearch.Combined:
+ # We are doing a 'Combined search'. Starting with reference search.
+ # Perform only if text contains any numbers
+ if (char.isdigit() for char in text):
+ self.on_quick_reference_search()
+ """
+ If results are found, search will be finalized.
+ This check needs to be here in order to avoid duplicate errors.
+ If keyword is shorter than 3 (not including spaces), message is given. It's actually possible to find
+ verses with less than 3 chars (Eg. G1 = Genesis 1) thus this error is not shown if any results are found.
+ if no Bibles are installed, this message is not shown - "No bibles" message is shown instead.
+ """
+ if not self.search_results and len(text) - text.count(' ') < 3 and bible:
+ self.main_window.information_message(
+ UiStrings().BibleShortSearchTitle,
+ UiStrings().BibleShortSearch)
+ if not self.search_results and len(text) - text.count(' ') > 2 and bible:
+ # Text search starts here if no reference was found and keyword is longer than 2.
+ # > 2 check is required in order to avoid duplicate error messages for short keywords.
+ self.on_quick_text_search()
+ if not self.search_results and not \
+ Settings().value(self.settings_section + '/hide combined quick error'):
+ self.application.set_normal_cursor()
+ # Reference separators need to be defined both, in here and on reference search,
+ # error won't work if they are left out from one.
+ reference_separators = {
+ 'verse': get_reference_separator('sep_v_display'),
+ 'range': get_reference_separator('sep_r_display'),
+ 'list': get_reference_separator('sep_l_display')}
+ self.main_window.information_message(translate('BiblesPlugin.BibleManager', 'Nothing found'),
+ translate('BiblesPlugin.BibleManager',
+ 'OpenLP couldn’t find anything with your'
+ ' search.
If you tried to search'
+ ' with Scripture Reference, please make
sure'
+ ' that your reference follows one of these'
+ ' patterns:
%s'
+ % UiStrings().BibleScriptureError %
+ reference_separators))
+ # Finalizing the search
+ # List is cleared if not locked, results are listed, button is set available, cursor is set to normal.
if not self.quickLockButton.isChecked():
self.list_view.clear()
if self.list_view.count() != 0 and self.search_results:
@@ -709,6 +877,99 @@ class BibleMediaItem(MediaManagerItem):
self.check_search_result()
self.application.set_normal_cursor()
+ def on_quick_search_while_typing(self):
+ """
+ This function is called when "Search as you type" is enabled for Bibles.
+ It is basically the same thing as "on_quick_search_search" but all the error messages are removed.
+ This also has increased min len for text search for performance reasons.
+ For commented version, please visit def on_quick_search_button.
+ """
+ bible = self.quickVersionComboBox.currentText()
+ second_bible = self.quickSecondComboBox.currentText()
+ text = self.quick_search_edit.text()
+ if self.quick_search_edit.current_search_type() == BibleSearch.Combined:
+ # If text has no numbers, auto search limit is min 8 characters for performance reasons.
+ # If you change this value, also change it in biblestab.py (Count) in enabling search while typing.
+ if (char.isdigit() for char in text) and len(text) > 2:
+ self.on_quick_reference_search()
+ if not self.search_results and len(text) > 7:
+ self.on_quick_text_search_while_typing()
+ elif self.quick_search_edit.current_search_type() == BibleSearch.Reference:
+ self.on_quick_reference_search()
+ elif self.quick_search_edit.current_search_type() == BibleSearch.Text:
+ if len(text) > 7:
+ self.on_quick_text_search_while_typing()
+ if not self.quickLockButton.isChecked():
+ self.list_view.clear()
+ if self.list_view.count() != 0 and self.search_results:
+ 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):
+ """
+ If search automatically while typing is enabled, perform the search and list results when conditions are met.
+ """
+ if Settings().value('bibles/is search while typing enabled'):
+ text = self.quick_search_edit.text()
+ """
+ Use Regex for finding space + number in reference search and space + 2 characters in text search.
+ Also search for two characters (Searches require at least two sets of two characters)
+ These are used to prevent bad search queries from starting. (Long/crashing queries)
+ """
+ space_and_digit_reference = re.compile(' \d')
+ two_chars_text = re.compile('\S\S')
+ space_and_two_chars_text = re.compile(' \S\S')
+ # Turn this into a format that may be used in if statement.
+ count_space_digit_reference = space_and_digit_reference.findall(text)
+ count_two_chars_text = two_chars_text.findall(text)
+ count_spaces_two_chars_text = space_and_two_chars_text.findall(text)
+ """
+ The Limit is required for setting the proper "No items found" message.
+ "Limit" is also hard coded to on_quick_search_while_typing, it must be there to avoid bad search
+ performance. Limit 8 = Text search, 3 = Reference search.
+ """
+ limit = 8
+ if self.quick_search_edit.current_search_type() == BibleSearch.Combined:
+ if len(count_space_digit_reference) != 0:
+ limit = 3
+ elif self.quick_search_edit.current_search_type() == BibleSearch.Reference:
+ limit = 3
+ """
+ If text is empty, clear the list.
+ else: Start by checking if the search is suitable for "Search while typing"
+ """
+ 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)):
+ if not self.quickLockButton.isChecked():
+ self.list_view.clear()
+ self.check_search_result_search_while_typing_short()
+ else:
+ """
+ Start search if no chars are entered or deleted for 0.2 s
+ If no Timer is set, Text search will break the search by sending repeative search Quaries on
+ all chars. Use the self.on_quick_search_while_typing, this does not contain any error messages.
+ """
+ self.search_timer = ()
+ if self.search_timer:
+ self.search_timer.stop()
+ self.search_timer.deleteLater()
+ self.search_timer = QtCore.QTimer()
+ self.search_timer.timeout.connect(self.on_quick_search_while_typing)
+ self.search_timer.setSingleShot(True)
+ self.search_timer.start(200)
+
def display_results(self, bible, second_bible=''):
"""
Displays the search results in the media manager. All data needed for further action is saved for/in each row.
diff --git a/openlp/plugins/bibles/lib/osis.py b/openlp/plugins/bibles/lib/osis.py
index 85aea70b3..4b217022e 100644
--- a/openlp/plugins/bibles/lib/osis.py
+++ b/openlp/plugins/bibles/lib/osis.py
@@ -23,7 +23,7 @@
import logging
from lxml import etree
-from openlp.core.common import translate, trace_error_handler
+from openlp.core.common import languages, translate, trace_error_handler
from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB
from openlp.core.lib.ui import critical_error_message_box
@@ -62,9 +62,11 @@ class OSISBible(BibleDB):
namespace = {'ns': 'http://www.bibletechnologies.net/2003/OSIS/namespace'}
# Find bible language
language_id = None
- language = osis_bible_tree.xpath("//ns:osisText/@xml:lang", namespaces=namespace)
- if language:
- language_id = BiblesResourcesDB.get_language(language[0])
+ lang = osis_bible_tree.xpath("//ns:osisText/@xml:lang", namespaces=namespace)
+ if lang:
+ language = languages.get_language(lang[0])
+ if hasattr(language, 'id'):
+ language_id = language.id
# The language couldn't be detected, ask the user
if not language_id:
language_id = self.get_language(bible_name)
diff --git a/openlp/plugins/bibles/lib/sword.py b/openlp/plugins/bibles/lib/sword.py
index 77f08cb47..b37e9b583 100644
--- a/openlp/plugins/bibles/lib/sword.py
+++ b/openlp/plugins/bibles/lib/sword.py
@@ -23,7 +23,7 @@
import logging
from pysword import modules
-from openlp.core.common import translate
+from openlp.core.common import languages, translate
from openlp.core.lib.ui import critical_error_message_box
from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB
@@ -57,9 +57,12 @@ class SwordBible(BibleDB):
pysword_modules = modules.SwordModules(self.sword_path)
pysword_module_json = pysword_modules.parse_modules()[self.sword_key]
bible = pysword_modules.get_bible_from_module(self.sword_key)
+ language_id = None
language = pysword_module_json['lang']
language = language[language.find('.') + 1:]
- language_id = BiblesResourcesDB.get_language(language)['id']
+ language = languages.get_language(language)
+ if hasattr(language, 'id'):
+ language_id = language.id
self.save_meta('language_id', language_id)
books = bible.get_structure().get_books()
# Count number of books
diff --git a/openlp/plugins/bibles/lib/zefania.py b/openlp/plugins/bibles/lib/zefania.py
index 482d98107..96ab69072 100644
--- a/openlp/plugins/bibles/lib/zefania.py
+++ b/openlp/plugins/bibles/lib/zefania.py
@@ -21,9 +21,9 @@
###############################################################################
import logging
-from lxml import etree, objectify
+from lxml import etree
-from openlp.core.common import translate
+from openlp.core.common import languages, translate
from openlp.core.lib.ui import critical_error_message_box
from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB
@@ -62,7 +62,9 @@ class ZefaniaBible(BibleDB):
language_id = None
language = zefania_bible_tree.xpath("/XMLBIBLE/INFORMATION/language/text()")
if language:
- language_id = BiblesResourcesDB.get_language(language[0])
+ language = languages.get_language(language[0])
+ if hasattr(language, 'id'):
+ language_id = language.id
# The language couldn't be detected, ask the user
if not language_id:
language_id = self.get_language(bible_name)
diff --git a/openlp/plugins/bibles/resources/bibles_resources.sqlite b/openlp/plugins/bibles/resources/bibles_resources.sqlite
index 8f1777124..0db28194e 100644
Binary files a/openlp/plugins/bibles/resources/bibles_resources.sqlite and b/openlp/plugins/bibles/resources/bibles_resources.sqlite differ
diff --git a/openlp/plugins/songs/lib/openlyricsxml.py b/openlp/plugins/songs/lib/openlyricsxml.py
index 5adffb300..e2964661d 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))
@@ -643,7 +643,7 @@ class OpenLyrics(object):
# Append text from tail and add formatting end tag.
# TODO: Verify format() with template variables
if element.tag == NSMAP % 'tag' and use_endtag:
- text += '{/{name}}}'.format(name=element.get('name'))
+ text += '{{{name}}}'.format(name=element.get('name'))
# Append text from tail.
if element.tail:
text += element.tail
diff --git a/resources/images/bibles_search_clear.png b/resources/images/bibles_search_clear.png
new file mode 100644
index 000000000..551ce7c96
Binary files /dev/null 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
new file mode 100644
index 000000000..161b3fcd6
Binary files /dev/null and b/resources/images/bibles_search_combined.png differ
diff --git a/resources/images/openlp-2.qrc b/resources/images/openlp-2.qrc
index f2619b0c7..b45cc745d 100644
--- a/resources/images/openlp-2.qrc
+++ b/resources/images/openlp-2.qrc
@@ -30,9 +30,11 @@
image_new_group.png
+ bibles_search_combined.png
bibles_search_text.png
bibles_search_reference.png
bibles_upgrade_alert.png
+ bibles_search_clear.png
bibles_search_unlock.png
bibles_search_lock.png
diff --git a/scripts/jenkins_script.py b/scripts/jenkins_script.py
index 61f74986a..0711d1257 100755
--- a/scripts/jenkins_script.py
+++ b/scripts/jenkins_script.py
@@ -63,9 +63,10 @@ class OpenLPJobs(object):
Branch_Windows_Interface = 'Branch-04b-Windows_Interface_Tests'
Branch_PEP = 'Branch-05a-Code_Analysis'
Branch_Coverage = 'Branch-05b-Test_Coverage'
+ Branch_Pylint = 'Branch-05c-Code_Analysis2'
Jobs = [Branch_Pull, Branch_Functional, Branch_Interface, Branch_Windows_Functional, Branch_Windows_Interface,
- Branch_PEP, Branch_Coverage]
+ Branch_PEP, Branch_Coverage, Branch_Pylint]
class Colour(object):
diff --git a/tests/functional/openlp_core_common/test_languages.py b/tests/functional/openlp_core_common/test_languages.py
new file mode 100644
index 000000000..fa6756d08
--- /dev/null
+++ b/tests/functional/openlp_core_common/test_languages.py
@@ -0,0 +1,109 @@
+# -*- 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 #
+###############################################################################
+"""
+Package to test the openlp.core.lib.languages package.
+"""
+from unittest import TestCase
+
+from openlp.core.common import languages
+
+
+class TestLanguages(TestCase):
+
+ def languages_type_test(self):
+ """
+ Test the languages variable type
+ """
+
+ # GIVEN: The languages module
+ # WHEN: Accessing the languages variable
+ # THEN: It should be of type list
+ self.assertIsInstance(languages.languages, list, 'languages.languages should be of type list')
+
+ def language_selection_languages_type_test(self):
+ """
+ Test the selection of a language
+ """
+
+ # GIVEN: A list of languages from the languages module
+ # WHEN: Selecting the first item
+ language = languages.languages[0]
+
+ # THEN: It should be an instance of the Language namedtuple
+ self.assertIsInstance(language, languages.Language)
+ self.assertEqual(language.id, 1)
+ self.assertEqual(language.name, '(Afan) Oromo')
+ self.assertEqual(language.code, 'om')
+
+ def get_language_name_test(self):
+ """
+ Test get_language() when supplied with a language name.
+ """
+
+ # GIVEN: A language name, in capitals
+ # WHEN: Calling get_language with it
+ language = languages.get_language('YORUBA')
+
+ # THEN: The Language found using that name should be returned
+ self.assertIsInstance(language, languages.Language)
+ self.assertEqual(language.id, 137)
+ self.assertEqual(language.name, 'Yoruba')
+ self.assertEqual(language.code, 'yo')
+
+ def get_language_code_test(self):
+ """
+ Test get_language() when supplied with a language code.
+ """
+
+ # GIVEN: A language code in capitals
+ # WHEN: Calling get_language with it
+ language = languages.get_language('IA')
+
+ # THEN: The Language found using that code should be returned
+ self.assertIsInstance(language, languages.Language)
+ self.assertEqual(language.id, 51)
+ self.assertEqual(language.name, 'Interlingua')
+ self.assertEqual(language.code, 'ia')
+
+ def get_language_invalid_test(self):
+ """
+ Test get_language() when supplied with a string which is not a valid language name or code.
+ """
+
+ # GIVEN: A language code
+ # WHEN: Calling get_language with it
+ language = languages.get_language('qwerty')
+
+ # THEN: None should be returned
+ self.assertIsNone(language)
+
+ def get_language_invalid__none_test(self):
+ """
+ Test get_language() when supplied with a string which is not a valid language name or code.
+ """
+
+ # GIVEN: A language code
+ # WHEN: Calling get_language with it
+ language = languages.get_language(None)
+
+ # THEN: None should be returned
+ self.assertIsNone(language)
diff --git a/tests/functional/openlp_plugins/bibles/test_db.py b/tests/functional/openlp_plugins/bibles/test_db.py
new file mode 100644
index 000000000..2807a8a3e
--- /dev/null
+++ b/tests/functional/openlp_plugins/bibles/test_db.py
@@ -0,0 +1,87 @@
+# -*- 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 db submodule of the Bibles plugin.
+"""
+
+from unittest import TestCase
+
+from openlp.plugins.bibles.lib.db import BibleDB
+from tests.functional import MagicMock, patch
+
+
+class TestBibleDB(TestCase):
+ """
+ Test the functions in the BibleDB class.
+ """
+
+ def test_get_language_canceled(self):
+ """
+ Test the BibleDB.get_language method when the user rejects the dialog box
+ """
+ # GIVEN: A mocked LanguageForm with an exec method which returns QtDialog.Rejected and an instance of BibleDB
+ with patch('openlp.plugins.bibles.lib.db.BibleDB._setup'),\
+ patch('openlp.plugins.bibles.forms.LanguageForm') as mocked_language_form:
+
+ # The integer value of QtDialog.Rejected is 0. Using the enumeration causes a seg fault for some reason
+ mocked_language_form_instance = MagicMock(**{'exec.return_value': 0})
+ mocked_language_form.return_value = mocked_language_form_instance
+ mocked_parent = MagicMock()
+ instance = BibleDB(mocked_parent)
+ mocked_wizard = MagicMock()
+ instance.wizard = mocked_wizard
+
+ # WHEN: Calling get_language()
+ result = instance.get_language()
+
+ # THEN: get_language() should return False
+ mocked_language_form.assert_called_once_with(mocked_wizard)
+ mocked_language_form_instance.exec.assert_called_once_with(None)
+ self.assertFalse(result, 'get_language() should return False if the user rejects the dialog box')
+
+ def test_get_language_accepted(self):
+ """
+ Test the BibleDB.get_language method when the user accepts the dialog box
+ """
+ # GIVEN: A mocked LanguageForm with an exec method which returns QtDialog.Accepted an instance of BibleDB and
+ # a combobox with the selected item data as 10
+ with patch('openlp.plugins.bibles.lib.db.BibleDB._setup'), \
+ patch('openlp.plugins.bibles.lib.db.BibleDB.save_meta'), \
+ patch('openlp.plugins.bibles.forms.LanguageForm') as mocked_language_form:
+
+ # The integer value of QtDialog.Accepted is 1. Using the enumeration causes a seg fault for some reason
+ mocked_language_form_instance = MagicMock(**{'exec.return_value': 1,
+ 'language_combo_box.itemData.return_value': 10})
+ mocked_language_form.return_value = mocked_language_form_instance
+ mocked_parent = MagicMock()
+ instance = BibleDB(mocked_parent)
+ mocked_wizard = MagicMock()
+ instance.wizard = mocked_wizard
+
+ # WHEN: Calling get_language()
+ result = instance.get_language('Bible Name')
+
+ # THEN: get_language() should return the id of the selected language in the combo box
+ mocked_language_form.assert_called_once_with(mocked_wizard)
+ mocked_language_form_instance.exec.assert_called_once_with('Bible Name')
+ self.assertEqual(result, 10, 'get_language() should return the id of the language the user has chosen when '
+ 'they accept the dialog box')
diff --git a/tests/functional/openlp_plugins/bibles/test_mediaitem.py b/tests/functional/openlp_plugins/bibles/test_mediaitem.py
index f820c7a64..05418f177 100644
--- a/tests/functional/openlp_plugins/bibles/test_mediaitem.py
+++ b/tests/functional/openlp_plugins/bibles/test_mediaitem.py
@@ -23,6 +23,7 @@
This module contains tests for the lib submodule of the Presentations plugin.
"""
from unittest import TestCase
+from openlp.core.common import Registry
from openlp.plugins.bibles.lib.mediaitem import BibleMediaItem
from tests.functional import MagicMock, patch
from tests.helpers.testmixin import TestMixin
@@ -41,6 +42,9 @@ class TestMediaItem(TestCase, TestMixin):
patch('openlp.plugins.bibles.lib.mediaitem.BibleMediaItem.setup_item'):
self.media_item = BibleMediaItem(None, MagicMock())
self.setup_application()
+ self.mocked_main_window = MagicMock()
+ Registry.create()
+ Registry().register('main_window', self.mocked_main_window)
def test_display_results_no_results(self):
"""
@@ -109,3 +113,40 @@ class TestMediaItem(TestCase, TestMixin):
mocked_list_view.selectAll.assert_called_once_with()
self.assertEqual(self.media_item.search_results, {})
self.assertEqual(self.media_item.second_search_results, {})
+
+ def on_quick_search_button_general_test(self):
+ """
+ Test that general things, which should be called on all Quick searches are called.
+ """
+
+ # GIVEN: self.application as self.app, all the required functions
+ Registry.create()
+ Registry().register('application', self.app)
+ self.media_item.quickSearchButton = MagicMock()
+ self.app.process_events = MagicMock()
+ self.media_item.quickVersionComboBox = MagicMock()
+ self.media_item.quickVersionComboBox.currentText = MagicMock()
+ self.media_item.quickSecondComboBox = MagicMock()
+ self.media_item.quickSecondComboBox.currentText = MagicMock()
+ self.media_item.quick_search_edit = MagicMock()
+ self.media_item.quick_search_edit.text = MagicMock()
+ self.media_item.quickLockButton = MagicMock()
+ 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
+ self.media_item.on_quick_search_button()
+
+ # THEN: Search should had been started and finalized properly
+ self.assertEqual(1, self.app.process_events.call_count, 'Normal cursor should had been called once')
+ self.assertEqual(1, self.media_item.quickVersionComboBox.currentText.call_count, 'Should had been called once')
+ self.assertEqual(1, self.media_item.quickSecondComboBox.currentText.call_count, 'Should had been called once')
+ self.assertEqual(1, self.media_item.quick_search_edit.text.call_count, 'Text edit Should had been called once')
+ 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')
diff --git a/tests/functional/openlp_plugins/bibles/test_swordimport.py b/tests/functional/openlp_plugins/bibles/test_swordimport.py
index ca3c03691..25b6ab355 100644
--- a/tests/functional/openlp_plugins/bibles/test_swordimport.py
+++ b/tests/functional/openlp_plugins/bibles/test_swordimport.py
@@ -70,8 +70,8 @@ class TestSwordImport(TestCase):
@patch('openlp.plugins.bibles.lib.sword.SwordBible.application')
@patch('openlp.plugins.bibles.lib.sword.modules')
- @patch('openlp.plugins.bibles.lib.db.BiblesResourcesDB')
- def test_simple_import(self, mocked_bible_res_db, mocked_pysword_modules, mocked_application):
+ @patch('openlp.core.common.languages')
+ def test_simple_import(self, mocked_languages, mocked_pysword_modules, mocked_application):
"""
Test that a simple SWORD import works
"""
@@ -88,7 +88,7 @@ class TestSwordImport(TestCase):
importer.create_verse = MagicMock()
importer.create_book = MagicMock()
importer.session = MagicMock()
- mocked_bible_res_db.get_language.return_value = 'Danish'
+ mocked_languages.get_language.return_value = 'Danish'
mocked_bible = MagicMock()
mocked_genesis = MagicMock()
mocked_genesis.name = 'Genesis'
diff --git a/tests/utils/test_pylint.py b/tests/utils/test_pylint.py
index dc6c83909..48c9e1393 100644
--- a/tests/utils/test_pylint.py
+++ b/tests/utils/test_pylint.py
@@ -23,8 +23,8 @@
Package to test for proper bzr tags.
"""
import os
-import logging
import platform
+import sys
from unittest import TestCase, SkipTest
try:
@@ -46,9 +46,18 @@ class TestPylint(TestCase):
"""
Test for pylint errors
"""
+ # Test if this file is specified in the arguments, if not skip the test.
+ in_argv = False
+ for arg in sys.argv:
+ if arg.endswith('test_pylint.py') or arg.endswith('test_pylint'):
+ in_argv = True
+ break
+ if not in_argv:
+ raise SkipTest('test_pylint.py not specified in arguments - skipping tests using pylint.')
+
# GIVEN: Some checks to disable and enable, and the pylint script
disabled_checks = 'import-error,no-member'
- enabled_checks = 'missing-format-argument-key,unused-format-string-argument'
+ enabled_checks = 'missing-format-argument-key,unused-format-string-argument,bad-format-string'
if is_win() or 'arch' in platform.dist()[0].lower():
pylint_script = 'pylint'
else:
@@ -84,6 +93,9 @@ class TestPylint(TestCase):
# Filter out PyQt related errors
elif ('no-name-in-module' in line or 'no-member' in line) and 'PyQt5' in line:
continue
+ # Filter out distutils related errors
+ elif 'distutils' in line:
+ continue
elif self._is_line_tolerated(line):
continue
else: