Merge lp:~phill-ridout/openlp/saved_bible_verses into lp:openlp

Proposed by Phill
Status: Merged
Merged at revision: 2743
Proposed branch: lp:~phill-ridout/openlp/saved_bible_verses
Merge into: lp:openlp
Diff against target: 1076 lines (+324/-202)
12 files modified
openlp/core/ui/lib/listwidgetwithdnd.py (+10/-4)
openlp/core/ui/lib/pathedit.py (+4/-4)
openlp/plugins/bibles/lib/__init__.py (+3/-3)
openlp/plugins/bibles/lib/db.py (+13/-0)
openlp/plugins/bibles/lib/manager.py (+15/-43)
openlp/plugins/bibles/lib/mediaitem.py (+178/-63)
openlp/plugins/presentations/presentationplugin.py (+1/-1)
openlp/plugins/songusage/forms/songusagedetaildialog.py (+1/-1)
resources/images/openlp-2.qrc (+2/-4)
tests/functional/openlp_core_ui_lib/test_listwidgetwithdnd.py (+33/-31)
tests/functional/openlp_plugins/bibles/test_mediaitem.py (+63/-47)
tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py (+1/-1)
To merge this branch: bzr merge lp:~phill-ridout/openlp/saved_bible_verses
Reviewer Review Type Date Requested Status
Tomas Groth Approve
Tim Bentley Pending
Review via email: mp+324674@code.launchpad.net

This proposal supersedes a proposal from 2017-05-07.

Description of the change

To post a comment you must log in.
Revision history for this message
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal

Please can you remove the resource file as this makes it difficult to see the changes.
The resource file can be merged in the next request,

review: Needs Fixing
Revision history for this message
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal

Traceback (most recent call last):
  File "/home/tim/Projects/OpenLP/openlp/saved_bible_verses/openlp/plugins/bibles/lib/mediaitem.py", line 789, in on_search_timer_timeout
    self.text_search()
  File "/home/tim/Projects/OpenLP/openlp/saved_bible_verses/openlp/plugins/bibles/lib/mediaitem.py", line 765, in text_search
    self.display_results()
  File "/home/tim/Projects/OpenLP/openlp/saved_bible_verses/openlp/plugins/bibles/lib/mediaitem.py", line 798, in display_results
    self.current_results = self.build_display_results(self.bible, self.second_bible, self.search_results)
  File "/home/tim/Projects/OpenLP/openlp/saved_bible_verses/openlp/plugins/bibles/lib/mediaitem.py", line 814, in build_display_results
    version = self.plugin.manager.get_meta_data(self.bible.name, 'name').value
AttributeError: 'NoneType' object has no attribute 'name'

downloaded and started to no bibles loaded !!!!!!!

Loaded a new bible and started to type. Search as type fired and downloaded the chapter and gave me a message saying search as you type is not available!

Then a traceback
Traceback (most recent call last):
  File "/home/tim/Projects/OpenLP/openlp/saved_bible_verses/openlp/plugins/bibles/lib/mediaitem.py", line 789, in on_search_timer_timeout
    self.text_search()
  File "/home/tim/Projects/OpenLP/openlp/saved_bible_verses/openlp/plugins/bibles/lib/mediaitem.py", line 768, in text_search
    self.on_text_search(text)
  File "/home/tim/Projects/OpenLP/openlp/saved_bible_verses/openlp/plugins/bibles/lib/mediaitem.py", line 732, in on_text_search
    self.display_results()
  File "/home/tim/Projects/OpenLP/openlp/saved_bible_verses/openlp/plugins/bibles/lib/mediaitem.py", line 798, in display_results
    self.current_results = self.build_display_results(self.bible, self.second_bible, self.search_results)
  File "/home/tim/Projects/OpenLP/openlp/saved_bible_verses/openlp/plugins/bibles/lib/mediaitem.py", line 828, in build_display_results
    for count, verse in enumerate(search_results):
TypeError: 'NoneType' object is not iterable

On the select UI I only have 2 lines of search as the options is large and takes up most the the space.

review: Needs Fixing
Revision history for this message
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal

Traceback (most recent call last):
  File "./openlp.py", line 44, in <module>
    main()
  File "/home/tim/Projects/OpenLP/openlp/saved_bible_verses/openlp/core/__init__.py", line 439, in main
    sys.exit(application.run(qt_args))
  File "/home/tim/Projects/OpenLP/openlp/saved_bible_verses/openlp/core/__init__.py", line 145, in run
    self.main_window.show()
  File "/home/tim/Projects/OpenLP/openlp/saved_bible_verses/openlp/core/ui/mainwindow.py", line 626, in show
    self.service_manager_contents.load_last_file()
  File "/home/tim/Projects/OpenLP/openlp/saved_bible_verses/openlp/core/ui/servicemanager.py", line 841, in load_last_file
    self.load_file(file_name)
  File "/home/tim/Projects/OpenLP/openlp/saved_bible_verses/openlp/core/ui/servicemanager.py", line 770, in load_file
    self.process_service_items(items)
  File "/home/tim/Projects/OpenLP/openlp/saved_bible_verses/openlp/core/ui/servicemanager.py", line 829, in process_service_items
    new_item = Registry().get(service_item.name).service_load(service_item)
  File "/home/tim/Projects/OpenLP/openlp/saved_bible_verses/openlp/plugins/songs/lib/mediaitem.py", line 712, in service_load
    self.generate_footer(item, song)
  File "/home/tim/Projects/OpenLP/openlp/saved_bible_verses/openlp/plugins/songs/lib/mediaitem.py", line 663, in generate_footer
    if self.display_copyright_symbol:
AttributeError: 'SongMediaItem' object has no attribute 'display_copyright_symbol'

on start up!

review: Needs Fixing
Revision history for this message
Tim Bentley (trb143) : Posted in a previous version of this proposal
review: Approve
Revision history for this message
Tomas Groth (tomasgroth) wrote :

I just did a quick test, where I using ESV:
 * Searched for "wrath", saved the verses.
 * Searched for "wrath of" and again saved the verses.

This causes the a lot of duplicates in the saved verse lists since the second list of saved verses seems to be appended to the existing list, without any duplicate checks. Is this intended behavior? Maybe removing duplicates and inserting new saved verses into list so that they are chronologically ordered, should be considered?

Revision history for this message
Phill (phill-ridout) wrote :

> I just did a quick test, where I using ESV:
> * Searched for "wrath", saved the verses.
> * Searched for "wrath of" and again saved the verses.
>
> This causes the a lot of duplicates in the saved verse lists since the second
> list of saved verses seems to be appended to the existing list, without any
> duplicate checks. Is this intended behavior? Maybe removing duplicates and
> inserting new saved verses into list so that they are chronologically ordered,
> should be considered?

Only verses that are selected should be saved. The idea being is that you could build up a list of verses, possibly from different places in the bible, and present them as one item.

I think it should be down to the user to decided if they want to add duplicate verses. How often would you add all the search results as a service item?

Revision history for this message
Tomas Groth (tomasgroth) wrote :

Ok, I'm convinced :)

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'openlp/core/ui/lib/listwidgetwithdnd.py' (properties changed: -x to +x)
--- openlp/core/ui/lib/listwidgetwithdnd.py 2017-02-18 07:23:15 +0000
+++ openlp/core/ui/lib/listwidgetwithdnd.py 2017-05-26 13:55:01 +0000
@@ -44,7 +44,6 @@
44 self.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)44 self.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
45 self.setAlternatingRowColors(True)45 self.setAlternatingRowColors(True)
46 self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)46 self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
47 self.locked = False
4847
49 def activateDnD(self):48 def activateDnD(self):
50 """49 """
@@ -54,15 +53,13 @@
54 self.setDragDropMode(QtWidgets.QAbstractItemView.DragDrop)53 self.setDragDropMode(QtWidgets.QAbstractItemView.DragDrop)
55 Registry().register_function(('%s_dnd' % self.mime_data_text), self.parent().load_file)54 Registry().register_function(('%s_dnd' % self.mime_data_text), self.parent().load_file)
5655
57 def clear(self, search_while_typing=False, override_lock=False):56 def clear(self, search_while_typing=False):
58 """57 """
59 Re-implement clear, so that we can customise feedback when using 'Search as you type'58 Re-implement clear, so that we can customise feedback when using 'Search as you type'
6059
61 :param search_while_typing: True if we want to display the customised message60 :param search_while_typing: True if we want to display the customised message
62 :return: None61 :return: None
63 """62 """
64 if self.locked and not override_lock:
65 return
66 if search_while_typing:63 if search_while_typing:
67 self.no_results_text = UiStrings().ShortResults64 self.no_results_text = UiStrings().ShortResults
68 else:65 else:
@@ -128,6 +125,15 @@
128 else:125 else:
129 event.ignore()126 event.ignore()
130127
128 def allItems(self):
129 """
130 An generator to list all the items in the widget
131
132 :return: a generator
133 """
134 for row in range(self.count()):
135 yield self.item(row)
136
131 def paintEvent(self, event):137 def paintEvent(self, event):
132 """138 """
133 Re-implement paintEvent so that we can add 'No Results' text when the listWidget is empty.139 Re-implement paintEvent so that we can add 'No Results' text when the listWidget is empty.
134140
=== modified file 'openlp/core/ui/lib/pathedit.py'
--- openlp/core/ui/lib/pathedit.py 2017-05-22 18:22:43 +0000
+++ openlp/core/ui/lib/pathedit.py 2017-05-26 13:55:01 +0000
@@ -46,16 +46,16 @@
4646
47 :param parent: The parent of the widget. This is just passed to the super method.47 :param parent: The parent of the widget. This is just passed to the super method.
48 :type parent: QWidget or None48 :type parent: QWidget or None
49 49
50 :param dialog_caption: Used to customise the caption in the QFileDialog.50 :param dialog_caption: Used to customise the caption in the QFileDialog.
51 :param dialog_caption: str51 :param dialog_caption: str
52 52
53 :param default_path: The default path. This is set as the path when the revert button is clicked53 :param default_path: The default path. This is set as the path when the revert button is clicked
54 :type default_path: str54 :type default_path: str
5555
56 :param show_revert: Used to determin if the 'revert button' should be visible.56 :param show_revert: Used to determin if the 'revert button' should be visible.
57 :type show_revert: bool57 :type show_revert: bool
58 58
59 :return: None59 :return: None
60 :rtype: None60 :rtype: None
61 """61 """
@@ -72,7 +72,7 @@
72 Set up the widget72 Set up the widget
73 :param show_revert: Show or hide the revert button73 :param show_revert: Show or hide the revert button
74 :type show_revert: bool74 :type show_revert: bool
75 75
76 :return: None76 :return: None
77 :rtype: None77 :rtype: None
78 """78 """
7979
=== modified file 'openlp/plugins/bibles/lib/__init__.py'
--- openlp/plugins/bibles/lib/__init__.py 2017-02-18 07:23:15 +0000
+++ openlp/plugins/bibles/lib/__init__.py 2017-05-26 13:55:01 +0000
@@ -341,10 +341,10 @@
341 if not book_ref_id:341 if not book_ref_id:
342 book_ref_id = bible.get_book_ref_id_by_localised_name(book, language_selection)342 book_ref_id = bible.get_book_ref_id_by_localised_name(book, language_selection)
343 elif not bible.get_book_by_book_ref_id(book_ref_id):343 elif not bible.get_book_by_book_ref_id(book_ref_id):
344 return False344 return []
345 # We have not found the book so do not continue345 # We have not found the book so do not continue
346 if not book_ref_id:346 if not book_ref_id:
347 return False347 return []
348 ranges = match.group('ranges')348 ranges = match.group('ranges')
349 range_list = get_reference_match('range_separator').split(ranges)349 range_list = get_reference_match('range_separator').split(ranges)
350 ref_list = []350 ref_list = []
@@ -403,7 +403,7 @@
403 return ref_list403 return ref_list
404 else:404 else:
405 log.debug('Invalid reference: {text}'.format(text=reference))405 log.debug('Invalid reference: {text}'.format(text=reference))
406 return None406 return []
407407
408408
409class SearchResults(object):409class SearchResults(object):
410410
=== modified file 'openlp/plugins/bibles/lib/db.py'
--- openlp/plugins/bibles/lib/db.py 2017-02-18 07:23:15 +0000
+++ openlp/plugins/bibles/lib/db.py 2017-05-26 13:55:01 +0000
@@ -158,6 +158,7 @@
158 self.get_name()158 self.get_name()
159 if 'path' in kwargs:159 if 'path' in kwargs:
160 self.path = kwargs['path']160 self.path = kwargs['path']
161 self._is_web_bible = None
161162
162 def get_name(self):163 def get_name(self):
163 """164 """
@@ -426,6 +427,18 @@
426 return 0427 return 0
427 return count428 return count
428429
430 @property
431 def is_web_bible(self):
432 """
433 A read only property indicating if the bible is a 'web bible'
434
435 :return: If the bible is a web bible.
436 :rtype: bool
437 """
438 if self._is_web_bible is None:
439 self._is_web_bible = bool(self.get_object(BibleMeta, 'download_source'))
440 return self._is_web_bible
441
429 def dump_bible(self):442 def dump_bible(self):
430 """443 """
431 Utility debugging method to dump the contents of a bible.444 Utility debugging method to dump the contents of a bible.
432445
=== modified file 'openlp/plugins/bibles/lib/manager.py'
--- openlp/plugins/bibles/lib/manager.py 2017-03-15 19:51:10 +0000
+++ openlp/plugins/bibles/lib/manager.py 2017-05-26 13:55:01 +0000
@@ -142,8 +142,8 @@
142 log.debug('Bible Name: "{name}"'.format(name=name))142 log.debug('Bible Name: "{name}"'.format(name=name))
143 self.db_cache[name] = bible143 self.db_cache[name] = bible
144 # Look to see if lazy load bible exists and get create getter.144 # Look to see if lazy load bible exists and get create getter.
145 source = self.db_cache[name].get_object(BibleMeta, 'download_source')145 if self.db_cache[name].is_web_bible:
146 if source:146 source = self.db_cache[name].get_object(BibleMeta, 'download_source')
147 download_name = self.db_cache[name].get_object(BibleMeta, 'download_name').value147 download_name = self.db_cache[name].get_object(BibleMeta, 'download_name').value
148 meta_proxy = self.db_cache[name].get_object(BibleMeta, 'proxy_server')148 meta_proxy = self.db_cache[name].get_object(BibleMeta, 'proxy_server')
149 web_bible = HTTPBible(self.parent, path=self.path, file=filename, download_source=source.value,149 web_bible = HTTPBible(self.parent, path=self.path, file=filename, download_source=source.value,
@@ -278,7 +278,7 @@
278 :param show_error:278 :param show_error:
279 """279 """
280 if not bible or not ref_list:280 if not bible or not ref_list:
281 return None281 return []
282 return self.db_cache[bible].get_verses(ref_list, show_error)282 return self.db_cache[bible].get_verses(ref_list, show_error)
283283
284 def get_language_selection(self, bible):284 def get_language_selection(self, bible):
@@ -305,11 +305,17 @@
305 """305 """
306 Does a verse search for the given bible and text.306 Does a verse search for the given bible and text.
307307
308 :param bible: The bible to search in (unicode).308 :param bible: The bible to search
309 :param second_bible: The second bible (unicode). We do not search in this bible.309 :type bible: str
310 :param text: The text to search for (unicode).310 :param text: The text to search for
311 :type text: str
312
313 :return: The search results if valid, or None if the search is invalid.
314 :rtype: None, list
311 """315 """
312 log.debug('BibleManager.verse_search("{bible}", "{text}")'.format(bible=bible, text=text))316 log.debug('BibleManager.verse_search("{bible}", "{text}")'.format(bible=bible, text=text))
317 if not text:
318 return None
313 # If no bibles are installed, message is given.319 # If no bibles are installed, message is given.
314 if not bible:320 if not bible:
315 self.main_window.information_message(321 self.main_window.information_message(
@@ -317,8 +323,7 @@
317 UiStrings().BibleNoBibles)323 UiStrings().BibleNoBibles)
318 return None324 return None
319 # Check if the bible or second_bible is a web bible.325 # Check if the bible or second_bible is a web bible.
320 web_bible = self.db_cache[bible].get_object(BibleMeta, 'download_source')326 if self.db_cache[bible].is_web_bible:
321 if web_bible:
322 # If either Bible is Web, cursor is reset to normal and message is given.327 # If either Bible is Web, cursor is reset to normal and message is given.
323 self.application.set_normal_cursor()328 self.application.set_normal_cursor()
324 self.main_window.information_message(329 self.main_window.information_message(
@@ -328,41 +333,8 @@
328 'This means that the currently selected Bible is a Web Bible.')333 'This means that the currently selected Bible is a Web Bible.')
329 )334 )
330 return None335 return None
331 # Shorter than 3 char searches break OpenLP with very long search times, thus they are blocked.336 # Fetch the results from db. If no results are found, return None, no message is given for this.
332 if len(text) - text.count(' ') < 3:337 if text:
333 return None
334 # Fetch the results from db. If no results are found, return None, no message is given for this.
335 elif text:
336 return self.db_cache[bible].verse_search(text)
337 else:
338 return None
339
340 def verse_search_while_typing(self, bible, second_bible, text):
341 """
342 Does a verse search for the given bible and text.
343 This is used during "Search while typing"
344 It's the same thing as the normal text search, but it does not show the web Bible error.
345 (It would result in the error popping every time a char is entered or removed)
346 It also does not have a minimum text len, this is set in mediaitem.py
347
348 :param bible: The bible to search in (unicode).
349 :param second_bible: The second bible (unicode). We do not search in this bible.
350 :param text: The text to search for (unicode).
351 """
352 # If no bibles are installed, message is given.
353 if not bible:
354 return None
355 # Check if the bible or second_bible is a web bible.
356 web_bible = self.db_cache[bible].get_object(BibleMeta, 'download_source')
357 second_web_bible = ''
358 if second_bible:
359 second_web_bible = self.db_cache[second_bible].get_object(BibleMeta, 'download_source')
360 if web_bible or second_web_bible:
361 # If either Bible is Web, cursor is reset to normal and search ends w/o any message.
362 self.application.set_normal_cursor()
363 return None
364 # Fetch the results from db. If no results are found, return None, no message is given for this.
365 elif text:
366 return self.db_cache[bible].verse_search(text)338 return self.db_cache[bible].verse_search(text)
367 else:339 else:
368 return None340 return None
369341
=== modified file 'openlp/plugins/bibles/lib/mediaitem.py' (properties changed: -x to +x)
--- openlp/plugins/bibles/lib/mediaitem.py 2017-02-18 20:22:47 +0000
+++ openlp/plugins/bibles/lib/mediaitem.py 2017-05-26 13:55:01 +0000
@@ -22,6 +22,7 @@
2222
23import logging23import logging
24import re24import re
25from enum import IntEnum, unique
2526
26from PyQt5 import QtCore, QtWidgets27from PyQt5 import QtCore, QtWidgets
2728
@@ -48,15 +49,45 @@
48 'list': get_reference_separator('sep_l_display')}49 'list': get_reference_separator('sep_l_display')}
4950
5051
51class BibleSearch(object):52@unique
53class BibleSearch(IntEnum):
52 """54 """
53 Enumeration class for the different search methods for the "Search" tab.55 Enumeration class for the different search types for the "Search" tab.
54 """56 """
55 Reference = 157 Reference = 1
56 Text = 258 Text = 2
57 Combined = 359 Combined = 3
5860
5961
62@unique
63class ResultsTab(IntEnum):
64 """
65 Enumeration class for the different tabs for the results list.
66 """
67 Saved = 0
68 Search = 1
69
70
71@unique
72class SearchStatus(IntEnum):
73 """
74 Enumeration class for the different search methods.
75 """
76 SearchButton = 0
77 SearchAsYouType = 1
78 NotEnoughText = 2
79
80
81@unique
82class SearchTabs(IntEnum):
83 """
84 Enumeration class for the tabs on the media item.
85 """
86 Search = 0
87 Select = 1
88 Options = 2
89
90
60class BibleMediaItem(MediaManagerItem):91class BibleMediaItem(MediaManagerItem):
61 """92 """
62 This is the custom media manager item for Bibles.93 This is the custom media manager item for Bibles.
@@ -73,11 +104,13 @@
73 :param kwargs: Keyword arguments to pass to the super method. (dict)104 :param kwargs: Keyword arguments to pass to the super method. (dict)
74 """105 """
75 self.clear_icon = build_icon(':/bibles/bibles_search_clear.png')106 self.clear_icon = build_icon(':/bibles/bibles_search_clear.png')
76 self.lock_icon = build_icon(':/bibles/bibles_search_lock.png')107 self.save_results_icon = build_icon(':/bibles/bibles_save_results.png')
77 self.unlock_icon = build_icon(':/bibles/bibles_search_unlock.png')
78 self.sort_icon = build_icon(':/bibles/bibles_book_sort.png')108 self.sort_icon = build_icon(':/bibles/bibles_book_sort.png')
79 self.bible = None109 self.bible = None
80 self.second_bible = None110 self.second_bible = None
111 self.saved_results = []
112 self.current_results = []
113 self.search_status = SearchStatus.SearchButton
81 # TODO: Make more central and clean up after!114 # TODO: Make more central and clean up after!
82 self.search_timer = QtCore.QTimer()115 self.search_timer = QtCore.QTimer()
83 self.search_timer.setInterval(200)116 self.search_timer.setInterval(200)
@@ -162,8 +195,10 @@
162 self.select_tab.setVisible(False)195 self.select_tab.setVisible(False)
163 self.page_layout.addWidget(self.select_tab)196 self.page_layout.addWidget(self.select_tab)
164 # General Search Opions197 # General Search Opions
165 self.options_widget = QtWidgets.QGroupBox(translate('BiblesPlugin.MediaItem', 'Options'), self)198 self.options_tab = QtWidgets.QWidget()
166 self.general_bible_layout = QtWidgets.QFormLayout(self.options_widget)199 self.options_tab.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)
200 self.search_tab_bar.addTab(translate('BiblesPlugin.MediaItem', 'Options'))
201 self.general_bible_layout = QtWidgets.QFormLayout(self.options_tab)
167 self.version_combo_box = create_horizontal_adjusting_combo_box(self, 'version_combo_box')202 self.version_combo_box = create_horizontal_adjusting_combo_box(self, 'version_combo_box')
168 self.general_bible_layout.addRow('{version}:'.format(version=UiStrings().Version), self.version_combo_box)203 self.general_bible_layout.addRow('{version}:'.format(version=UiStrings().Version), self.version_combo_box)
169 self.second_combo_box = create_horizontal_adjusting_combo_box(self, 'second_combo_box')204 self.second_combo_box = create_horizontal_adjusting_combo_box(self, 'second_combo_box')
@@ -171,20 +206,28 @@
171 self.style_combo_box = create_horizontal_adjusting_combo_box(self, 'style_combo_box')206 self.style_combo_box = create_horizontal_adjusting_combo_box(self, 'style_combo_box')
172 self.style_combo_box.addItems(['', '', ''])207 self.style_combo_box.addItems(['', '', ''])
173 self.general_bible_layout.addRow(UiStrings().LayoutStyle, self.style_combo_box)208 self.general_bible_layout.addRow(UiStrings().LayoutStyle, self.style_combo_box)
174 self.search_button_layout = QtWidgets.QHBoxLayout()209 self.options_tab.setVisible(False)
210 self.page_layout.addWidget(self.options_tab)
211 # This widget is the easier way to reset the spacing of search_button_layout. (Because page_layout has had its
212 # spacing set to 0)
213 self.search_button_widget = QtWidgets.QWidget()
214 self.search_button_layout = QtWidgets.QHBoxLayout(self.search_button_widget)
175 self.search_button_layout.addStretch()215 self.search_button_layout.addStretch()
176 # Note: If we use QPushButton instead of the QToolButton, the icon will be larger than the Lock icon.216 # Note: If we use QPushButton instead of the QToolButton, the icon will be larger than the Lock icon.
177 self.clear_button = QtWidgets.QToolButton(self)217 self.clear_button = QtWidgets.QPushButton()
178 self.clear_button.setIcon(self.clear_icon)218 self.clear_button.setIcon(self.clear_icon)
179 self.lock_button = QtWidgets.QToolButton(self)219 self.save_results_button = QtWidgets.QPushButton()
180 self.lock_button.setIcon(self.unlock_icon)220 self.save_results_button.setIcon(self.save_results_icon)
181 self.lock_button.setCheckable(True)
182 self.search_button_layout.addWidget(self.clear_button)221 self.search_button_layout.addWidget(self.clear_button)
183 self.search_button_layout.addWidget(self.lock_button)222 self.search_button_layout.addWidget(self.save_results_button)
184 self.search_button = QtWidgets.QPushButton(self)223 self.search_button = QtWidgets.QPushButton(self)
185 self.search_button_layout.addWidget(self.search_button)224 self.search_button_layout.addWidget(self.search_button)
186 self.general_bible_layout.addRow(self.search_button_layout)225 self.page_layout.addWidget(self.search_button_widget)
187 self.page_layout.addWidget(self.options_widget)226 self.results_view_tab = QtWidgets.QTabBar(self)
227 self.results_view_tab.addTab('')
228 self.results_view_tab.addTab('')
229 self.results_view_tab.setCurrentIndex(ResultsTab.Search)
230 self.page_layout.addWidget(self.results_view_tab)
188231
189 def setupUi(self):232 def setupUi(self):
190 super().setupUi()233 super().setupUi()
@@ -211,12 +254,15 @@
211 # Buttons254 # Buttons
212 self.book_order_button.toggled.connect(self.on_book_order_button_toggled)255 self.book_order_button.toggled.connect(self.on_book_order_button_toggled)
213 self.clear_button.clicked.connect(self.on_clear_button_clicked)256 self.clear_button.clicked.connect(self.on_clear_button_clicked)
214 self.lock_button.toggled.connect(self.on_lock_button_toggled)257 self.save_results_button.clicked.connect(self.on_save_results_button_clicked)
215 self.search_button.clicked.connect(self.on_search_button_clicked)258 self.search_button.clicked.connect(self.on_search_button_clicked)
216 # Other stuff259 # Other stuff
217 self.search_edit.returnPressed.connect(self.on_search_button_clicked)260 self.search_edit.returnPressed.connect(self.on_search_button_clicked)
218 self.search_tab_bar.currentChanged.connect(self.on_search_tab_bar_current_changed)261 self.search_tab_bar.currentChanged.connect(self.on_search_tab_bar_current_changed)
262 self.results_view_tab.currentChanged.connect(self.on_results_view_tab_current_changed)
219 self.search_edit.textChanged.connect(self.on_search_edit_text_changed)263 self.search_edit.textChanged.connect(self.on_search_edit_text_changed)
264 self.on_results_view_tab_total_update(ResultsTab.Saved)
265 self.on_results_view_tab_total_update(ResultsTab.Search)
220266
221 def retranslateUi(self):267 def retranslateUi(self):
222 log.debug('retranslateUi')268 log.debug('retranslateUi')
@@ -225,9 +271,9 @@
225 self.style_combo_box.setItemText(LayoutStyle.VersePerSlide, UiStrings().VersePerSlide)271 self.style_combo_box.setItemText(LayoutStyle.VersePerSlide, UiStrings().VersePerSlide)
226 self.style_combo_box.setItemText(LayoutStyle.VersePerLine, UiStrings().VersePerLine)272 self.style_combo_box.setItemText(LayoutStyle.VersePerLine, UiStrings().VersePerLine)
227 self.style_combo_box.setItemText(LayoutStyle.Continuous, UiStrings().Continuous)273 self.style_combo_box.setItemText(LayoutStyle.Continuous, UiStrings().Continuous)
228 self.clear_button.setToolTip(translate('BiblesPlugin.MediaItem', 'Clear the search results.'))274 self.clear_button.setToolTip(translate('BiblesPlugin.MediaItem', 'Clear the results on the current tab.'))
229 self.lock_button.setToolTip(275 self.save_results_button.setToolTip(
230 translate('BiblesPlugin.MediaItem', 'Toggle to keep or clear the previous results.'))276 translate('BiblesPlugin.MediaItem', 'Add the search results to the saved list.'))
231 self.search_button.setText(UiStrings().Search)277 self.search_button.setText(UiStrings().Search)
232278
233 def on_focus(self):279 def on_focus(self):
@@ -241,8 +287,10 @@
241 if self.search_tab.isVisible():287 if self.search_tab.isVisible():
242 self.search_edit.setFocus()288 self.search_edit.setFocus()
243 self.search_edit.selectAll()289 self.search_edit.selectAll()
244 else:290 if self.select_tab.isVisible():
245 self.select_book_combo_box.setFocus()291 self.select_book_combo_box.setFocus()
292 if self.options_tab.isVisible():
293 self.version_combo_box.setFocus()
246294
247 def config_update(self):295 def config_update(self):
248 """296 """
@@ -415,14 +463,48 @@
415 """463 """
416 Show the selected tab and set focus to it464 Show the selected tab and set focus to it
417465
418 :param index: The tab selected (int)466 :param index: The tab selected
467 :type index: int
419 :return: None468 :return: None
420 """469 """
421 search_tab = index == 0470 if index == SearchTabs.Search or index == SearchTabs.Select:
422 self.search_tab.setVisible(search_tab)471 self.search_button.setEnabled(True)
423 self.select_tab.setVisible(not search_tab)472 else:
473 self.search_button.setEnabled(False)
474 self.search_tab.setVisible(index == SearchTabs.Search)
475 self.select_tab.setVisible(index == SearchTabs.Select)
476 self.options_tab.setVisible(index == SearchTabs.Options)
424 self.on_focus()477 self.on_focus()
425478
479 def on_results_view_tab_current_changed(self, index):
480 """
481 Update list_widget with the contents of the selected list
482
483 :param index: The index of the tab that has been changed to. (int)
484 :return: None
485 """
486 if index == ResultsTab.Saved:
487 self.add_built_results_to_list_widget(self.saved_results)
488 elif index == ResultsTab.Search:
489 self.add_built_results_to_list_widget(self.current_results)
490
491 def on_results_view_tab_total_update(self, index):
492 """
493 Update the result total count on the tab with the given index.
494
495 :param index: Index of the tab to update (int)
496 :return: None
497 """
498 string = ''
499 count = 0
500 if index == ResultsTab.Saved:
501 string = translate('BiblesPlugin.MediaItem', 'Saved ({result_count})')
502 count = len(self.saved_results)
503 elif index == ResultsTab.Search:
504 string = translate('BiblesPlugin.MediaItem', 'Results ({result_count})')
505 count = len(self.current_results)
506 self.results_view_tab.setTabText(index, string.format(result_count=count))
507
426 def on_book_order_button_toggled(self, checked):508 def on_book_order_button_toggled(self, checked):
427 """509 """
428 Change the sort order of the book names510 Change the sort order of the book names
@@ -442,22 +524,25 @@
442524
443 :return: None525 :return: None
444 """526 """
445 self.list_view.clear()527 current_index = self.results_view_tab.currentIndex()
446 self.search_edit.clear()528 for item in self.list_view.selectedItems():
447 self.on_focus()529 self.list_view.takeItem(self.list_view.row(item))
530 results = [item.data(QtCore.Qt.UserRole) for item in self.list_view.allItems()]
531 if current_index == ResultsTab.Saved:
532 self.saved_results = results
533 elif current_index == ResultsTab.Search:
534 self.current_results = results
535 self.on_results_view_tab_total_update(current_index)
448536
449 def on_lock_button_toggled(self, checked):537 def on_save_results_button_clicked(self):
450 """538 """
451 Toggle the lock button, if Search tab is used, set focus to search field.539 Add the selected verses to the saved_results list.
452540
453 :param checked: The state of the toggle button. (bool)
454 :return: None541 :return: None
455 """542 """
456 self.list_view.locked = checked543 for verse in self.list_view.selectedItems():
457 if checked:544 self.saved_results.append(verse.data(QtCore.Qt.UserRole))
458 self.sender().setIcon(self.lock_icon)545 self.on_results_view_tab_total_update(ResultsTab.Saved)
459 else:
460 self.sender().setIcon(self.unlock_icon)
461546
462 def on_style_combo_box_index_changed(self, index):547 def on_style_combo_box_index_changed(self, index):
463 """548 """
@@ -490,16 +575,17 @@
490 :return: None575 :return: None
491 """576 """
492 new_selection = self.second_combo_box.currentData()577 new_selection = self.second_combo_box.currentData()
493 if self.list_view.count():578 if self.saved_results:
494 # Exclusive or (^) the new and previous selections to detect if the user has switched between single and579 # Exclusive or (^) the new and previous selections to detect if the user has switched between single and
495 # dual bible mode580 # dual bible mode
496 if (new_selection is None) ^ (self.second_bible is None):581 if (new_selection is None) ^ (self.second_bible is None):
497 if critical_error_message_box(582 if critical_error_message_box(
498 message=translate('BiblesPlugin.MediaItem',583 message=translate('BiblesPlugin.MediaItem',
499 'OpenLP cannot combine single and dual Bible verse search results. '584 'OpenLP cannot combine single and dual Bible verse search results. '
500 'Do you want to clear your search results and start a new search?'),585 'Do you want to clear your saved results?'),
501 parent=self, question=True) == QtWidgets.QMessageBox.Yes:586 parent=self, question=True) == QtWidgets.QMessageBox.Yes:
502 self.list_view.clear(override_lock=True)587 self.saved_results = []
588 self.on_results_view_tab_total_update(ResultsTab.Saved)
503 else:589 else:
504 self.second_combo_box.setCurrentIndex(self.second_combo_box.findData(self.second_bible))590 self.second_combo_box.setCurrentIndex(self.second_combo_box.findData(self.second_bible))
505 return591 return
@@ -525,7 +611,8 @@
525 log.warning('Not enough chapters in %s', book_ref_id)611 log.warning('Not enough chapters in %s', book_ref_id)
526 critical_error_message_box(message=translate('BiblesPlugin.MediaItem', 'Bible not fully loaded.'))612 critical_error_message_box(message=translate('BiblesPlugin.MediaItem', 'Bible not fully loaded.'))
527 else:613 else:
528 self.search_button.setEnabled(True)614 if self.select_tab.isVisible():
615 self.search_button.setEnabled(True)
529 self.adjust_combo_box(1, self.chapter_count, self.from_chapter)616 self.adjust_combo_box(1, self.chapter_count, self.from_chapter)
530 self.adjust_combo_box(1, self.chapter_count, self.to_chapter)617 self.adjust_combo_box(1, self.chapter_count, self.to_chapter)
531 self.adjust_combo_box(1, verse_count, self.from_verse)618 self.adjust_combo_box(1, verse_count, self.from_verse)
@@ -602,6 +689,8 @@
602689
603 :return: None690 :return: None
604 """691 """
692 self.search_timer.stop()
693 self.search_status = SearchStatus.SearchButton
605 if not self.bible:694 if not self.bible:
606 self.main_window.information_message(UiStrings().BibleNoBiblesTitle, UiStrings().BibleNoBibles)695 self.main_window.information_message(UiStrings().BibleNoBiblesTitle, UiStrings().BibleNoBibles)
607 return696 return
@@ -613,6 +702,7 @@
613 elif self.select_tab.isVisible():702 elif self.select_tab.isVisible():
614 self.select_search()703 self.select_search()
615 self.search_button.setEnabled(True)704 self.search_button.setEnabled(True)
705 self.results_view_tab.setCurrentIndex(ResultsTab.Search)
616 self.application.set_normal_cursor()706 self.application.set_normal_cursor()
617707
618 def select_search(self):708 def select_search(self):
@@ -636,18 +726,21 @@
636726
637 :return: None727 :return: None
638 """728 """
729 self.search_results = []
639 verse_refs = self.plugin.manager.parse_ref(self.bible.name, search_text)730 verse_refs = self.plugin.manager.parse_ref(self.bible.name, search_text)
640 self.search_results = self.plugin.manager.get_verses(self.bible.name, verse_refs, True)731 self.search_results = self.plugin.manager.get_verses(self.bible.name, verse_refs, True)
641 if self.second_bible and self.search_results:732 if self.second_bible and self.search_results:
642 self.search_results = self.plugin.manager.get_verses(self.second_bible.name, verse_refs, True)733 self.search_results = self.plugin.manager.get_verses(self.second_bible.name, verse_refs, True)
643 self.display_results()734 self.display_results()
644735
645 def on_text_search(self, text, search_while_type=False):736 def on_text_search(self, text):
646 """737 """
647 We are doing a 'Text Search'.738 We are doing a 'Text Search'.
648 This search is called on def text_search by 'Search' Text and Combined Searches.739 This search is called on def text_search by 'Search' Text and Combined Searches.
649 """740 """
650 self.search_results = self.plugin.manager.verse_search(self.bible.name, text)741 self.search_results = self.plugin.manager.verse_search(self.bible.name, text)
742 if self.search_results is None:
743 return
651 if self.second_bible and self.search_results:744 if self.second_bible and self.search_results:
652 filtered_search_results = []745 filtered_search_results = []
653 not_found_count = 0746 not_found_count = 0
@@ -663,7 +756,7 @@
663 verse=verse.verse, bible_name=self.second_bible.name))756 verse=verse.verse, bible_name=self.second_bible.name))
664 not_found_count += 1757 not_found_count += 1
665 self.search_results = filtered_search_results758 self.search_results = filtered_search_results
666 if not_found_count != 0 and not search_while_type:759 if not_found_count != 0 and self.search_status == SearchStatus.SearchButton:
667 self.main_window.information_message(760 self.main_window.information_message(
668 translate('BiblesPlugin.MediaItem', 'Verses not found'),761 translate('BiblesPlugin.MediaItem', 'Verses not found'),
669 translate('BiblesPlugin.MediaItem',762 translate('BiblesPlugin.MediaItem',
@@ -673,22 +766,23 @@
673 ).format(second_name=self.second_bible.name, name=self.bible.name, count=not_found_count))766 ).format(second_name=self.second_bible.name, name=self.bible.name, count=not_found_count))
674 self.display_results()767 self.display_results()
675768
676 def text_search(self, search_while_type=False):769 def text_search(self):
677 """770 """
678 This triggers the proper 'Search' search based on which search type is used.771 This triggers the proper 'Search' search based on which search type is used.
679 "Eg. "Reference Search", "Text Search" or "Combined search".772 "Eg. "Reference Search", "Text Search" or "Combined search".
680 """773 """
774 self.search_results = []
681 log.debug('text_search called')775 log.debug('text_search called')
682 text = self.search_edit.text()776 text = self.search_edit.text()
683 if text == '':777 if text == '':
684 self.list_view.clear()778 self.display_results()
685 return779 return
686 self.list_view.clear(search_while_typing=search_while_type)780 self.on_results_view_tab_total_update(ResultsTab.Search)
687 if self.search_edit.current_search_type() == BibleSearch.Reference:781 if self.search_edit.current_search_type() == BibleSearch.Reference:
688 if get_reference_match('full').match(text):782 if get_reference_match('full').match(text):
689 # Valid reference found. Do reference search.783 # Valid reference found. Do reference search.
690 self.text_reference_search(text)784 self.text_reference_search(text)
691 elif not search_while_type:785 elif self.search_status == SearchStatus.SearchButton:
692 self.main_window.information_message(786 self.main_window.information_message(
693 translate('BiblesPlugin.BibleManager', 'Scripture Reference Error'),787 translate('BiblesPlugin.BibleManager', 'Scripture Reference Error'),
694 translate('BiblesPlugin.BibleManager',788 translate('BiblesPlugin.BibleManager',
@@ -700,10 +794,12 @@
700 self.text_reference_search(text)794 self.text_reference_search(text)
701 else:795 else:
702 # It can only be a 'Combined' search without a valid reference, or a 'Text' search796 # It can only be a 'Combined' search without a valid reference, or a 'Text' search
703 if search_while_type:797 if self.search_status == SearchStatus.SearchAsYouType:
704 if len(text) > 8 and VALID_TEXT_SEARCH.search(text):798 if len(text) <= 8:
705 self.on_text_search(text, True)799 self.search_status = SearchStatus.NotEnoughText
706 elif VALID_TEXT_SEARCH.search(text):800 self.display_results()
801 return
802 if VALID_TEXT_SEARCH.search(text):
707 self.on_text_search(text)803 self.on_text_search(text)
708804
709 def on_search_edit_text_changed(self):805 def on_search_edit_text_changed(self):
@@ -713,9 +809,12 @@
713809
714 :return: None810 :return: None
715 """811 """
716 if Settings().value('bibles/is search while typing enabled'):812 if not Settings().value('bibles/is search while typing enabled') or \
717 if not self.search_timer.isActive():813 not self.bible or self.bible.is_web_bible or \
718 self.search_timer.start()814 (self.second_bible and self.bible.is_web_bible):
815 return
816 if not self.search_timer.isActive():
817 self.search_timer.start()
719818
720 def on_search_timer_timeout(self):819 def on_search_timer_timeout(self):
721 """820 """
@@ -724,7 +823,9 @@
724823
725 :return: None824 :return: None
726 """825 """
727 self.text_search(True)826 self.search_status = SearchStatus.SearchAsYouType
827 self.text_search()
828 self.results_view_tab.setCurrentIndex(ResultsTab.Search)
728829
729 def display_results(self):830 def display_results(self):
730 """831 """
@@ -732,14 +833,16 @@
732833
733 :return: None834 :return: None
734 """835 """
735 self.list_view.clear()836 self.current_results = self.build_display_results(self.bible, self.second_bible, self.search_results)
736 if self.search_results:
737 items = self.build_display_results(self.bible, self.second_bible, self.search_results)
738 for item in items:
739 self.list_view.addItem(item)
740 self.list_view.selectAll()
741 self.search_results = []837 self.search_results = []
742 self.second_search_results = []838 self.add_built_results_to_list_widget(self.current_results)
839
840 def add_built_results_to_list_widget(self, results):
841 self.list_view.clear(self.search_status == SearchStatus.NotEnoughText)
842 for item in self.build_list_widget_items(results):
843 self.list_view.addItem(item)
844 self.list_view.selectAll()
845 self.on_results_view_tab_total_update(ResultsTab.Search)
743846
744 def build_display_results(self, bible, second_bible, search_results):847 def build_display_results(self, bible, second_bible, search_results):
745 """848 """
@@ -789,10 +892,17 @@
789 bible_text = '{book} {chapter:d}{sep}{verse:d} ({version}, {second_version})'892 bible_text = '{book} {chapter:d}{sep}{verse:d} ({version}, {second_version})'
790 else:893 else:
791 bible_text = '{book} {chapter:d}{sep}{verse:d} ({version})'894 bible_text = '{book} {chapter:d}{sep}{verse:d} ({version})'
792 bible_verse = QtWidgets.QListWidgetItem(bible_text.format(sep=verse_separator, **data))895 data['item_title'] = bible_text.format(sep=verse_separator, **data)
896 items.append(data)
897 return items
898
899 def build_list_widget_items(self, items):
900 list_widget_items = []
901 for data in items:
902 bible_verse = QtWidgets.QListWidgetItem(data['item_title'])
793 bible_verse.setData(QtCore.Qt.UserRole, data)903 bible_verse.setData(QtCore.Qt.UserRole, data)
794 items.append(bible_verse)904 list_widget_items.append(bible_verse)
795 return items905 return list_widget_items
796906
797 def generate_slide_data(self, service_item, item=None, xml_version=False, remote=False,907 def generate_slide_data(self, service_item, item=None, xml_version=False, remote=False,
798 context=ServiceItemContext.Service):908 context=ServiceItemContext.Service):
@@ -897,6 +1007,8 @@
897 """1007 """
898 Search for some Bible verses (by reference).1008 Search for some Bible verses (by reference).
899 """1009 """
1010 if self.bible is None:
1011 return []
900 reference = self.plugin.manager.parse_ref(self.bible.name, string)1012 reference = self.plugin.manager.parse_ref(self.bible.name, string)
901 search_results = self.plugin.manager.get_verses(self.bible.name, reference, showError)1013 search_results = self.plugin.manager.get_verses(self.bible.name, reference, showError)
902 if search_results:1014 if search_results:
@@ -908,6 +1020,9 @@
908 """1020 """
909 Create a media item from an item id.1021 Create a media item from an item id.
910 """1022 """
1023 if self.bible is None:
1024 return []
911 reference = self.plugin.manager.parse_ref(self.bible.name, item_id)1025 reference = self.plugin.manager.parse_ref(self.bible.name, item_id)
912 search_results = self.plugin.manager.get_verses(self.bible.name, reference, False)1026 search_results = self.plugin.manager.get_verses(self.bible.name, reference, False)
913 return self.build_display_results(self.bible, None, search_results)1027 items = self.build_display_results(self.bible, None, search_results)
1028 return self.build_list_widget_items(items)
9141029
=== modified file 'openlp/plugins/presentations/presentationplugin.py'
--- openlp/plugins/presentations/presentationplugin.py 2017-05-22 18:27:40 +0000
+++ openlp/plugins/presentations/presentationplugin.py 2017-05-26 13:55:01 +0000
@@ -1,4 +1,4 @@
1 # -*- coding: utf-8 -*-1# -*- coding: utf-8 -*-
2# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=42# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
33
4###############################################################################4###############################################################################
55
=== modified file 'openlp/plugins/songusage/forms/songusagedetaildialog.py'
--- openlp/plugins/songusage/forms/songusagedetaildialog.py 2017-05-22 18:22:43 +0000
+++ openlp/plugins/songusage/forms/songusagedetaildialog.py 2017-05-26 13:55:01 +0000
@@ -69,7 +69,7 @@
69 self.file_horizontal_layout.setSpacing(8)69 self.file_horizontal_layout.setSpacing(8)
70 self.file_horizontal_layout.setContentsMargins(8, 8, 8, 8)70 self.file_horizontal_layout.setContentsMargins(8, 8, 8, 8)
71 self.file_horizontal_layout.setObjectName('file_horizontal_layout')71 self.file_horizontal_layout.setObjectName('file_horizontal_layout')
72 self.report_path_edit = PathEdit(self.file_group_box, path_type = PathType.Directories, show_revert=False)72 self.report_path_edit = PathEdit(self.file_group_box, path_type=PathType.Directories, show_revert=False)
73 self.file_horizontal_layout.addWidget(self.report_path_edit)73 self.file_horizontal_layout.addWidget(self.report_path_edit)
74 self.vertical_layout.addWidget(self.file_group_box)74 self.vertical_layout.addWidget(self.file_group_box)
75 self.button_box = create_button_box(song_usage_detail_dialog, 'button_box', ['cancel', 'ok'])75 self.button_box = create_button_box(song_usage_detail_dialog, 'button_box', ['cancel', 'ok'])
7676
=== added file 'resources/images/bibles_save_results.png'
77Binary files resources/images/bibles_save_results.png 1970-01-01 00:00:00 +0000 and resources/images/bibles_save_results.png 2017-05-26 13:55:01 +0000 differ77Binary files resources/images/bibles_save_results.png 1970-01-01 00:00:00 +0000 and resources/images/bibles_save_results.png 2017-05-26 13:55:01 +0000 differ
=== removed file 'resources/images/bibles_search_lock.png'
78Binary files resources/images/bibles_search_lock.png 2011-05-11 23:59:37 +0000 and resources/images/bibles_search_lock.png 1970-01-01 00:00:00 +0000 differ78Binary files resources/images/bibles_search_lock.png 2011-05-11 23:59:37 +0000 and resources/images/bibles_search_lock.png 1970-01-01 00:00:00 +0000 differ
=== removed file 'resources/images/bibles_search_unlock.png'
79Binary files resources/images/bibles_search_unlock.png 2011-05-11 23:59:37 +0000 and resources/images/bibles_search_unlock.png 1970-01-01 00:00:00 +0000 differ79Binary files resources/images/bibles_search_unlock.png 2011-05-11 23:59:37 +0000 and resources/images/bibles_search_unlock.png 1970-01-01 00:00:00 +0000 differ
=== removed file 'resources/images/network_ssl.png'
80Binary files resources/images/network_ssl.png 2014-04-14 18:09:47 +0000 and resources/images/network_ssl.png 1970-01-01 00:00:00 +0000 differ80Binary files resources/images/network_ssl.png 2014-04-14 18:09:47 +0000 and resources/images/network_ssl.png 1970-01-01 00:00:00 +0000 differ
=== modified file 'resources/images/openlp-2.qrc'
--- resources/images/openlp-2.qrc 2016-12-18 14:11:31 +0000
+++ resources/images/openlp-2.qrc 2017-05-26 13:55:01 +0000
@@ -34,8 +34,7 @@
34 <file>bibles_search_text.png</file>34 <file>bibles_search_text.png</file>
35 <file>bibles_search_reference.png</file>35 <file>bibles_search_reference.png</file>
36 <file>bibles_search_clear.png</file>36 <file>bibles_search_clear.png</file>
37 <file>bibles_search_unlock.png</file>37 <file>bibles_save_results.png</file>
38 <file>bibles_search_lock.png</file>
39 </qresource>38 </qresource>
40 <qresource prefix="plugins">39 <qresource prefix="plugins">
41 <file>plugin_alerts.png</file>40 <file>plugin_alerts.png</file>
@@ -144,7 +143,6 @@
144 </qresource>143 </qresource>
145 <qresource prefix="remote">144 <qresource prefix="remote">
146 <file>network_server.png</file>145 <file>network_server.png</file>
147 <file>network_ssl.png</file>
148 <file>network_auth.png</file>146 <file>network_auth.png</file>
149 </qresource>147 </qresource>
150 <qresource prefix="songusage">148 <qresource prefix="songusage">
@@ -188,4 +186,4 @@
188 <file>android_app_qr.png</file>186 <file>android_app_qr.png</file>
189 <file>ios_app_qr.png</file>187 <file>ios_app_qr.png</file>
190 </qresource>188 </qresource>
191</RCC>
192\ No newline at end of file189\ No newline at end of file
190</RCC>
193191
=== modified file 'tests/functional/openlp_core_ui_lib/test_listwidgetwithdnd.py' (properties changed: -x to +x)
--- tests/functional/openlp_core_ui_lib/test_listwidgetwithdnd.py 2017-02-18 07:23:15 +0000
+++ tests/functional/openlp_core_ui_lib/test_listwidgetwithdnd.py 2017-05-26 13:55:01 +0000
@@ -23,6 +23,7 @@
23This module contains tests for the openlp.core.lib.listwidgetwithdnd module23This module contains tests for the openlp.core.lib.listwidgetwithdnd module
24"""24"""
25from unittest import TestCase25from unittest import TestCase
26from types import GeneratorType
2627
27from openlp.core.common.uistrings import UiStrings28from openlp.core.common.uistrings import UiStrings
28from openlp.core.ui.lib.listwidgetwithdnd import ListWidgetWithDnD29from openlp.core.ui.lib.listwidgetwithdnd import ListWidgetWithDnD
@@ -33,37 +34,6 @@
33 """34 """
34 Test the :class:`~openlp.core.lib.listwidgetwithdnd.ListWidgetWithDnD` class35 Test the :class:`~openlp.core.lib.listwidgetwithdnd.ListWidgetWithDnD` class
35 """36 """
36 def test_clear_locked(self):
37 """
38 Test the clear method the list is 'locked'
39 """
40 with patch('openlp.core.ui.lib.listwidgetwithdnd.QtWidgets.QListWidget.clear') as mocked_clear_super_method:
41 # GIVEN: An instance of ListWidgetWithDnD
42 widget = ListWidgetWithDnD()
43
44 # WHEN: The list is 'locked' and clear has been called
45 widget.locked = True
46 widget.clear()
47
48 # THEN: The super method should not have been called (i.e. The list not cleared)
49 self.assertFalse(mocked_clear_super_method.called)
50
51 def test_clear_overide_locked(self):
52 """
53 Test the clear method the list is 'locked', but clear is called with 'override_lock' set to True
54 """
55 with patch('openlp.core.ui.lib.listwidgetwithdnd.QtWidgets.QListWidget.clear') as mocked_clear_super_method:
56 # GIVEN: An instance of ListWidgetWithDnD
57 widget = ListWidgetWithDnD()
58
59 # WHEN: The list is 'locked' and clear has been called with override_lock se to True
60 widget.locked = True
61 widget.clear(override_lock=True)
62
63 # THEN: The super method should have been called (i.e. The list is cleared regardless whether it is locked
64 # or not)
65 mocked_clear_super_method.assert_called_once_with()
66
67 def test_clear(self):37 def test_clear(self):
68 """38 """
69 Test the clear method when called without any arguments.39 Test the clear method when called without any arguments.
@@ -90,6 +60,38 @@
90 # THEN: The results text should be the 'short results' text.60 # THEN: The results text should be the 'short results' text.
91 self.assertEqual(widget.no_results_text, UiStrings().ShortResults)61 self.assertEqual(widget.no_results_text, UiStrings().ShortResults)
9262
63 def test_all_items_no_list_items(self):
64 """
65 Test allItems when there are no items in the list widget
66 """
67 # GIVEN: An instance of ListWidgetWithDnD
68 widget = ListWidgetWithDnD()
69 with patch.object(widget, 'count', return_value=0), \
70 patch.object(widget, 'item', side_effect=lambda x: [][x]):
71
72 # WHEN: Calling allItems
73 result = widget.allItems()
74
75 # THEN: An instance of a Generator object should be returned. The generator should not yeild any results
76 self.assertIsInstance(result, GeneratorType)
77 self.assertEqual(list(result), [])
78
79 def test_all_items_list_items(self):
80 """
81 Test allItems when the list widget contains some items.
82 """
83 # GIVEN: An instance of ListWidgetWithDnD
84 widget = ListWidgetWithDnD()
85 with patch.object(widget, 'count', return_value=2), \
86 patch.object(widget, 'item', side_effect=lambda x: [5, 3][x]):
87
88 # WHEN: Calling allItems
89 result = widget.allItems()
90
91 # THEN: An instance of a Generator object should be returned. The generator should not yeild any results
92 self.assertIsInstance(result, GeneratorType)
93 self.assertEqual(list(result), [5, 3])
94
93 def test_paint_event(self):95 def test_paint_event(self):
94 """96 """
95 Test the paintEvent method when the list is not empty97 Test the paintEvent method when the list is not empty
9698
=== modified file 'tests/functional/openlp_plugins/bibles/test_mediaitem.py' (properties changed: -x to +x)
--- tests/functional/openlp_plugins/bibles/test_mediaitem.py 2017-02-18 07:23:15 +0000
+++ tests/functional/openlp_plugins/bibles/test_mediaitem.py 2017-05-26 13:55:01 +0000
@@ -31,7 +31,8 @@
3131
32from openlp.core.common import Registry32from openlp.core.common import Registry
33from openlp.core.lib import MediaManagerItem33from openlp.core.lib import MediaManagerItem
34from openlp.plugins.bibles.lib.mediaitem import BibleMediaItem, BibleSearch, get_reference_separators, VALID_TEXT_SEARCH34from openlp.plugins.bibles.lib.mediaitem import BibleMediaItem, BibleSearch, ResultsTab, SearchStatus, \
35 get_reference_separators, VALID_TEXT_SEARCH
3536
3637
37class TestBibleMediaItemModulefunctions(TestCase):38class TestBibleMediaItemModulefunctions(TestCase):
@@ -143,6 +144,7 @@
143 self.media_item = BibleMediaItem(None, self.mocked_plugin)144 self.media_item = BibleMediaItem(None, self.mocked_plugin)
144145
145 self.media_item.settings_section = 'bibles'146 self.media_item.settings_section = 'bibles'
147 self.media_item.results_view_tab = MagicMock()
146148
147 self.mocked_book_1 = MagicMock(**{'get_name.return_value': 'Book 1', 'book_reference_id': 1})149 self.mocked_book_1 = MagicMock(**{'get_name.return_value': 'Book 1', 'book_reference_id': 1})
148 self.mocked_book_2 = MagicMock(**{'get_name.return_value': 'Book 2', 'book_reference_id': 2})150 self.mocked_book_2 = MagicMock(**{'get_name.return_value': 'Book 2', 'book_reference_id': 2})
@@ -658,56 +660,65 @@
658 # THEN: The select_book_combo_box model sort should have been reset660 # THEN: The select_book_combo_box model sort should have been reset
659 self.media_item.select_book_combo_box.model().sort.assert_called_once_with(-1)661 self.media_item.select_book_combo_box.model().sort.assert_called_once_with(-1)
660662
661 def test_on_clear_button_clicked(self):663 def test_on_clear_button_clicked_saved_tab(self):
662 """664 """
663 Test on_clear_button_clicked665 Test on_clear_button_clicked when the saved tab is selected
666 """
667 # GIVEN: An instance of :class:`MediaManagerItem` and mocked out saved_tab and select_tab and a mocked out
668 # list_view and search_edit
669 self.media_item.list_view = MagicMock()
670 self.media_item.search_edit = MagicMock()
671 self.media_item.results_view_tab = MagicMock(**{'currentIndex.return_value': ResultsTab.Saved})
672 self.media_item.saved_results = ['Some', 'Results']
673 with patch.object(self.media_item, 'on_focus'):
674
675 # WHEN: Calling on_clear_button_clicked
676 self.media_item.on_clear_button_clicked()
677
678 # THEN: The list_view should be cleared
679 self.assertEqual(self.media_item.saved_results, [])
680 self.media_item.list_view.clear.assert_called_once_with()
681
682 def test_on_clear_button_clicked_search_tab(self):
683 """
684 Test on_clear_button_clicked when the search tab is selected
664 """685 """
665 # GIVEN: An instance of :class:`MediaManagerItem` and mocked out search_tab and select_tab and a mocked out686 # GIVEN: An instance of :class:`MediaManagerItem` and mocked out search_tab and select_tab and a mocked out
666 # list_view and search_edit687 # list_view and search_edit
667 self.media_item.list_view = MagicMock()688 self.media_item.list_view = MagicMock()
668 self.media_item.search_edit = MagicMock()689 self.media_item.search_edit = MagicMock()
690 self.media_item.results_view_tab = MagicMock(**{'currentIndex.return_value': ResultsTab.Search})
691 self.media_item.current_results = ['Some', 'Results']
669 with patch.object(self.media_item, 'on_focus'):692 with patch.object(self.media_item, 'on_focus'):
670693
671 # WHEN: Calling on_clear_button_clicked694 # WHEN: Calling on_clear_button_clicked
672 self.media_item.on_clear_button_clicked()695 self.media_item.on_clear_button_clicked()
673696
674 # THEN: The list_view and the search_edit should be cleared697 # THEN: The list_view and the search_edit should be cleared
698 self.assertEqual(self.media_item.current_results, [])
675 self.media_item.list_view.clear.assert_called_once_with()699 self.media_item.list_view.clear.assert_called_once_with()
676 self.media_item.search_edit.clear.assert_called_once_with()700 self.media_item.search_edit.clear.assert_called_once_with()
677701
678 def test_on_lock_button_toggled_search_tab_lock_icon(self):702 def test_on_save_results_button_clicked(self):
679 """703 """
680 Test that "on_lock_button_toggled" toggles the lock properly.704 Test that "on_save_results_button_clicked" saves the results.
681 """705 """
682 # GIVEN: An instance of :class:`MediaManagerItem` a mocked sender and list_view706 # GIVEN: An instance of :class:`MediaManagerItem` and a mocked list_view
683 self.media_item.list_view = MagicMock()707 result_1 = MagicMock(**{'data.return_value': 'R1'})
684 self.media_item.lock_icon = 'lock icon'708 result_2 = MagicMock(**{'data.return_value': 'R2'})
685 mocked_sender_instance = MagicMock()709 result_3 = MagicMock(**{'data.return_value': 'R3'})
686 with patch.object(self.media_item, 'sender', return_value=mocked_sender_instance):710 self.media_item.list_view = MagicMock(**{'selectedItems.return_value': [result_1, result_2, result_3]})
687711
688 # WHEN: When the lock_button is checked712 with patch.object(self.media_item, 'on_results_view_tab_total_update') as \
689 self.media_item.on_lock_button_toggled(True)713 mocked_on_results_view_tab_total_update:
690714
691 # THEN: list_view should be 'locked' and the lock icon set715 # WHEN: When the save_results_button is clicked
692 self.assertTrue(self.media_item.list_view.locked)716 self.media_item.on_save_results_button_clicked()
693 mocked_sender_instance.setIcon.assert_called_once_with('lock icon')717
694718 # THEN: The selected results in the list_view should be added to the 'saved_results' list. And the saved_tab
695 def test_on_lock_button_toggled_unlock_icon(self):719 # total should be updated.
696 """720 self.assertEqual(self.media_item.saved_results, ['R1', 'R2', 'R3'])
697 Test that "on_lock_button_toggled" toggles the lock properly.721 mocked_on_results_view_tab_total_update.assert_called_once_with(ResultsTab.Saved)
698 """
699 # GIVEN: An instance of :class:`MediaManagerItem` a mocked sender and list_view
700 self.media_item.list_view = MagicMock()
701 self.media_item.unlock_icon = 'unlock icon'
702 mocked_sender_instance = MagicMock()
703 with patch.object(self.media_item, 'sender', return_value=mocked_sender_instance):
704
705 # WHEN: When the lock_button is unchecked
706 self.media_item.on_lock_button_toggled(False)
707
708 # THEN: list_view should be 'unlocked' and the unlock icon set
709 self.assertFalse(self.media_item.list_view.locked)
710 mocked_sender_instance.setIcon.assert_called_once_with('unlock icon')
711722
712 def test_on_style_combo_box_changed(self):723 def test_on_style_combo_box_changed(self):
713 """724 """
@@ -815,7 +826,9 @@
815 self.media_item.list_view = MagicMock(**{'count.return_value': 5})826 self.media_item.list_view = MagicMock(**{'count.return_value': 5})
816 self.media_item.style_combo_box = MagicMock()827 self.media_item.style_combo_box = MagicMock()
817 self.media_item.select_book_combo_box = MagicMock()828 self.media_item.select_book_combo_box = MagicMock()
829 self.media_item.search_results = ['list', 'of', 'results']
818 with patch.object(self.media_item, 'initialise_advanced_bible') as mocked_initialise_advanced_bible, \830 with patch.object(self.media_item, 'initialise_advanced_bible') as mocked_initialise_advanced_bible, \
831 patch.object(self.media_item, 'display_results'), \
819 patch('openlp.plugins.bibles.lib.mediaitem.critical_error_message_box',832 patch('openlp.plugins.bibles.lib.mediaitem.critical_error_message_box',
820 return_value=QtWidgets.QMessageBox.Yes) as mocked_critical_error_message_box:833 return_value=QtWidgets.QMessageBox.Yes) as mocked_critical_error_message_box:
821834
@@ -825,9 +838,8 @@
825 self.media_item.second_combo_box = MagicMock(**{'currentData.return_value': self.mocked_bible_1})838 self.media_item.second_combo_box = MagicMock(**{'currentData.return_value': self.mocked_bible_1})
826 self.media_item.on_second_combo_box_index_changed(5)839 self.media_item.on_second_combo_box_index_changed(5)
827840
828 # THEN: The list_view should be cleared and the selected bible should be set as the current bible841 # THEN: The selected bible should be set as the current bible
829 self.assertTrue(mocked_critical_error_message_box.called)842 self.assertTrue(mocked_critical_error_message_box.called)
830 self.media_item.list_view.clear.assert_called_once_with(override_lock=True)
831 self.media_item.style_combo_box.setEnabled.assert_called_once_with(False)843 self.media_item.style_combo_box.setEnabled.assert_called_once_with(False)
832 self.assertTrue(mocked_initialise_advanced_bible.called)844 self.assertTrue(mocked_initialise_advanced_bible.called)
833 self.assertEqual(self.media_item.second_bible, self.mocked_bible_1)845 self.assertEqual(self.media_item.second_bible, self.mocked_bible_1)
@@ -841,7 +853,9 @@
841 self.media_item.list_view = MagicMock(**{'count.return_value': 5})853 self.media_item.list_view = MagicMock(**{'count.return_value': 5})
842 self.media_item.style_combo_box = MagicMock()854 self.media_item.style_combo_box = MagicMock()
843 self.media_item.select_book_combo_box = MagicMock()855 self.media_item.select_book_combo_box = MagicMock()
856 self.media_item.search_results = ['list', 'of', 'results']
844 with patch.object(self.media_item, 'initialise_advanced_bible') as mocked_initialise_advanced_bible, \857 with patch.object(self.media_item, 'initialise_advanced_bible') as mocked_initialise_advanced_bible, \
858 patch.object(self.media_item, 'display_results'), \
845 patch('openlp.plugins.bibles.lib.mediaitem.critical_error_message_box',859 patch('openlp.plugins.bibles.lib.mediaitem.critical_error_message_box',
846 return_value=QtWidgets.QMessageBox.Yes) as mocked_critical_error_message_box:860 return_value=QtWidgets.QMessageBox.Yes) as mocked_critical_error_message_box:
847 # WHEN: The previously is a bible new selection is None and the user selects yes861 # WHEN: The previously is a bible new selection is None and the user selects yes
@@ -850,9 +864,8 @@
850 self.media_item.second_combo_box = MagicMock(**{'currentData.return_value': None})864 self.media_item.second_combo_box = MagicMock(**{'currentData.return_value': None})
851 self.media_item.on_second_combo_box_index_changed(0)865 self.media_item.on_second_combo_box_index_changed(0)
852866
853 # THEN: The list_view should be cleared and the selected bible should be set as the current bible867 # THEN: The selected bible should be set as the current bible
854 self.assertTrue(mocked_critical_error_message_box.called)868 self.assertTrue(mocked_critical_error_message_box.called)
855 self.media_item.list_view.clear.assert_called_once_with(override_lock=True)
856 self.media_item.style_combo_box.setEnabled.assert_called_once_with(True)869 self.media_item.style_combo_box.setEnabled.assert_called_once_with(True)
857 self.assertFalse(mocked_initialise_advanced_bible.called)870 self.assertFalse(mocked_initialise_advanced_bible.called)
858 self.assertEqual(self.media_item.second_bible, None)871 self.assertEqual(self.media_item.second_bible, None)
@@ -1388,8 +1401,9 @@
1388 # WHEN: Calling on_search_timer_timeout1401 # WHEN: Calling on_search_timer_timeout
1389 self.media_item.on_search_timer_timeout()1402 self.media_item.on_search_timer_timeout()
13901403
1391 # THEN: The text_search method should have been called with True1404 # THEN: The search_status should be set to SearchAsYouType and text_search should have been called
1392 mocked_text_search.assert_called_once_with(True)1405 self.assertEqual(self.media_item.search_status, SearchStatus().SearchAsYouType)
1406 mocked_text_search.assert_called_once_with()
13931407
1394 def test_display_results_no_results(self):1408 def test_display_results_no_results(self):
1395 """1409 """
@@ -1407,7 +1421,6 @@
1407 self.media_item.display_results()1421 self.media_item.display_results()
14081422
1409 # THEN: No items should be added to the list1423 # THEN: No items should be added to the list
1410 self.media_item.list_view.clear.assert_called_once_with()
1411 self.assertFalse(self.media_item.list_view.addItem.called)1424 self.assertFalse(self.media_item.list_view.addItem.called)
14121425
1413 def test_display_results_results(self):1426 def test_display_results_results(self):
@@ -1415,7 +1428,10 @@
1415 Test the display_results method when there are items to display1428 Test the display_results method when there are items to display
1416 """1429 """
1417 # GIVEN: An instance of BibleMediaItem and a mocked build_display_results which returns a list of results1430 # GIVEN: An instance of BibleMediaItem and a mocked build_display_results which returns a list of results
1418 with patch.object(self.media_item, 'build_display_results', return_value=['list', 'items']):1431 with patch.object(self.media_item, 'build_display_results', return_value=[
1432 {'item_title': 'Title 1'}, {'item_title': 'Title 2'}]), \
1433 patch.object(self.media_item, 'add_built_results_to_list_widget') as \
1434 mocked_add_built_results_to_list_widget:
1419 self.media_item.search_results = ['results']1435 self.media_item.search_results = ['results']
1420 self.media_item.list_view = MagicMock()1436 self.media_item.list_view = MagicMock()
14211437
@@ -1423,5 +1439,5 @@
1423 self.media_item.display_results()1439 self.media_item.display_results()
14241440
1425 # THEN: addItem should have been with the display items1441 # THEN: addItem should have been with the display items
1426 self.media_item.list_view.clear.assert_called_once_with()1442 mocked_add_built_results_to_list_widget.assert_called_once_with(
1427 self.assertEqual(self.media_item.list_view.addItem.call_args_list, [call('list'), call('items')])1443 [{'item_title': 'Title 1'}, {'item_title': 'Title 2'}])
14281444
=== modified file 'tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py'
--- tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py 2017-04-24 05:17:55 +0000
+++ tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py 2017-05-26 13:55:01 +0000
@@ -108,7 +108,7 @@
108 # WHEN asking to parse the bible reference108 # WHEN asking to parse the bible reference
109 results = parse_reference('Raoul 1', self.manager.db_cache['tests'], MagicMock())109 results = parse_reference('Raoul 1', self.manager.db_cache['tests'], MagicMock())
110 # THEN a verse array should be returned110 # THEN a verse array should be returned
111 self.assertEqual(False, results, "The bible Search should return False")111 self.assertEqual([], results, "The bible Search should return an empty list")
112112
113 def test_parse_reference_five(self):113 def test_parse_reference_five(self):
114 """114 """