Merge lp:~tomasgroth/openlp/bug-fixes-2-4-3 into lp:openlp

Proposed by Tomas Groth
Status: Merged
Merged at revision: 2706
Proposed branch: lp:~tomasgroth/openlp/bug-fixes-2-4-3
Merge into: lp:openlp
Diff against target: 299 lines (+92/-89)
6 files modified
openlp/core/__init__.py (+41/-5)
openlp/core/lib/__init__.py (+15/-22)
openlp/core/ui/advancedtab.py (+0/-21)
openlp/core/ui/lib/treewidgetwithdnd.py (+6/-1)
openlp/core/ui/slidecontroller.py (+2/-0)
tests/functional/openlp_core_lib/test_lib.py (+28/-40)
To merge this branch: bzr merge lp:~tomasgroth/openlp/bug-fixes-2-4-3
Reviewer Review Type Date Requested Status
Raoul Snyman Approve
Review via email: mp+311435@code.launchpad.net

Description of the change

Continuation of lp:~suutari-olli/openlp/bug-fixes-2-4-3

This branch fixes the following bugs:
Bug #1487788: Importing photos does not give focus to OpenLP
Bug #1512040: Loop tooltip gets stuck to "Stop playing..."
Bug #1513490: List of authors uses localized "and" instead of English
Bug #1624661: Missing DB in unmounted disk results in Traceback

To post a comment you must log in.
Revision history for this message
Tomas Groth (tomasgroth) wrote :
Revision history for this message
Raoul Snyman (raoul-snyman) wrote :

Looks fine to me

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'openlp/core/__init__.py'
--- openlp/core/__init__.py 2016-04-27 12:49:55 +0000
+++ openlp/core/__init__.py 2016-11-21 21:19:11 +0000
@@ -177,6 +177,38 @@
177 self.shared_memory.create(1)177 self.shared_memory.create(1)
178 return False178 return False
179179
180 def is_data_path_missing(self):
181 """
182 Check if the data folder path exists.
183 """
184 data_folder_path = AppLocation.get_data_path()
185 if not os.path.exists(data_folder_path):
186 log.critical('Database was not found in: ' + data_folder_path)
187 status = QtWidgets.QMessageBox.critical(None, translate('OpenLP', 'Data Directory Error'),
188 translate('OpenLP', 'OpenLP data folder was not found in:\n\n{path}'
189 '\n\nThe location of the data folder was '
190 'previously changed from the OpenLP\'s '
191 'default location. If the data was stored on '
192 'removable device, that device needs to be '
193 'made available.\n\nYou may reset the data '
194 'location back to the default location, '
195 'or you can try to make the current location '
196 'available.\n\nDo you want to reset to the '
197 'default data location? If not, OpenLP will be '
198 'closed so you can try to fix the the problem.')
199 .format(path=data_folder_path),
200 QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes |
201 QtWidgets.QMessageBox.No),
202 QtWidgets.QMessageBox.No)
203 if status == QtWidgets.QMessageBox.No:
204 # If answer was "No", return "True", it will shutdown OpenLP in def main
205 log.info('User requested termination')
206 return True
207 # If answer was "Yes", remove the custom data path thus resetting the default location.
208 Settings().remove('advanced/data path')
209 log.info('Database location has been reset to the default settings.')
210 return False
211
180 def hook_exception(self, exc_type, value, traceback):212 def hook_exception(self, exc_type, value, traceback):
181 """213 """
182 Add an exception hook so that any uncaught exceptions are displayed in this window rather than somewhere where214 Add an exception hook so that any uncaught exceptions are displayed in this window rather than somewhere where
@@ -208,8 +240,8 @@
208 # If data_version is different from the current version ask if we should backup the data folder240 # If data_version is different from the current version ask if we should backup the data folder
209 elif data_version != openlp_version:241 elif data_version != openlp_version:
210 if QtWidgets.QMessageBox.question(None, translate('OpenLP', 'Backup'),242 if QtWidgets.QMessageBox.question(None, translate('OpenLP', 'Backup'),
211 translate('OpenLP', 'OpenLP has been upgraded, do you want to create '243 translate('OpenLP', 'OpenLP has been upgraded, do you want to create\n'
212 'a backup of OpenLPs data folder?'),244 'a backup of the old data folder?'),
213 QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,245 QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
214 QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.Yes:246 QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.Yes:
215 # Create copy of data folder247 # Create copy of data folder
@@ -223,8 +255,8 @@
223 translate('OpenLP', 'Backup of the data folder failed!'))255 translate('OpenLP', 'Backup of the data folder failed!'))
224 return256 return
225 message = translate('OpenLP',257 message = translate('OpenLP',
226 'A backup of the data folder has been created'258 'A backup of the data folder has been created at:\n\n'
227 'at {text}').format(text=data_folder_backup_path)259 '{text}').format(text=data_folder_backup_path)
228 QtWidgets.QMessageBox.information(None, translate('OpenLP', 'Backup'), message)260 QtWidgets.QMessageBox.information(None, translate('OpenLP', 'Backup'), message)
229261
230 # Update the version in the settings262 # Update the version in the settings
@@ -368,9 +400,13 @@
368 Registry.create()400 Registry.create()
369 Registry().register('application', application)401 Registry().register('application', application)
370 application.setApplicationVersion(get_application_version()['version'])402 application.setApplicationVersion(get_application_version()['version'])
371 # Instance check403 # Check if an instance of OpenLP is already running. Quit if there is a running instance and the user only wants one
372 if application.is_already_running():404 if application.is_already_running():
373 sys.exit()405 sys.exit()
406 # If the custom data path is missing and the user wants to restore the data path, quit OpenLP.
407 if application.is_data_path_missing():
408 application.shared_memory.detach()
409 sys.exit()
374 # Remove/convert obsolete settings.410 # Remove/convert obsolete settings.
375 Settings().remove_obsolete_settings()411 Settings().remove_obsolete_settings()
376 # First time checks in settings412 # First time checks in settings
377413
=== modified file 'openlp/core/lib/__init__.py'
--- openlp/core/lib/__init__.py 2016-10-30 08:29:22 +0000
+++ openlp/core/lib/__init__.py 2016-11-21 21:19:11 +0000
@@ -310,30 +310,23 @@
310310
311def create_separated_list(string_list):311def create_separated_list(string_list):
312 """312 """
313 Returns a string that represents a join of a list of strings with a localized separator. This function corresponds313 Returns a string that represents a join of a list of strings with a localized separator.
314314 Localized separation will be done via the translate() function by the translators.
315 to QLocale::createSeparatedList which was introduced in Qt 4.8 and implements the algorithm from315
316 http://www.unicode.org/reports/tr35/#ListPatterns316 :param string_list: List of unicode strings
317317 :return: Formatted string
318 :param string_list: List of unicode strings
319 """318 """
320 if LooseVersion(Qt.PYQT_VERSION_STR) >= LooseVersion('4.9') and LooseVersion(Qt.qVersion()) >= LooseVersion('4.8'):319 list_length = len(string_list)
321 return QtCore.QLocale().createSeparatedList(string_list)320 if list_length == 1:
322 if not string_list:321 list_to_string = string_list[0]
323 return ''322 elif list_length == 2:
324 elif len(string_list) == 1:323 list_to_string = translate('OpenLP.core.lib', '{one} and {two}').format(one=string_list[0], two=string_list[1])
325 return string_list[0]324 elif list_length > 2:
326 # TODO: Verify mocking of translate() test before conversion325 list_to_string = translate('OpenLP.core.lib', '{first} and {last}').format(first=', '.join(string_list[:-1]),
327 elif len(string_list) == 2:326 last=string_list[-1])
328 return translate('OpenLP.core.lib', '%s and %s',
329 'Locale list separator: 2 items') % (string_list[0], string_list[1])
330 else:327 else:
331 merged = translate('OpenLP.core.lib', '%s, and %s',328 list_to_string = ''
332 'Locale list separator: end') % (string_list[-2], string_list[-1])329 return list_to_string
333 for index in reversed(list(range(1, len(string_list) - 2))):
334 merged = translate('OpenLP.core.lib', '%s, %s',
335 'Locale list separator: middle') % (string_list[index], merged)
336 return translate('OpenLP.core.lib', '%s, %s', 'Locale list separator: start') % (string_list[0], merged)
337330
338331
339from .exceptions import ValidationError332from .exceptions import ValidationError
340333
=== modified file 'openlp/core/ui/advancedtab.py'
--- openlp/core/ui/advancedtab.py 2016-08-11 22:12:50 +0000
+++ openlp/core/ui/advancedtab.py 2016-11-21 21:19:11 +0000
@@ -397,27 +397,6 @@
397 self.data_directory_cancel_button.hide()397 self.data_directory_cancel_button.hide()
398 # Since data location can be changed, make sure the path is present.398 # Since data location can be changed, make sure the path is present.
399 self.current_data_path = AppLocation.get_data_path()399 self.current_data_path = AppLocation.get_data_path()
400 if not os.path.exists(self.current_data_path):
401 log.error('Data path not found {path}'.format(path=self.current_data_path))
402 answer = QtWidgets.QMessageBox.critical(
403 self, translate('OpenLP.AdvancedTab', 'Data Directory Error'),
404 translate('OpenLP.AdvancedTab', 'OpenLP data directory was not found\n\n{path}\n\n'
405 'This data directory was previously changed from the OpenLP '
406 'default location. If the new location was on removable '
407 'media, that media needs to be made available.\n\n'
408 'Click "No" to stop loading OpenLP. allowing you to fix the the problem.\n\n'
409 'Click "Yes" to reset the data directory to the default '
410 'location.').format(path=self.current_data_path),
411 QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No),
412 QtWidgets.QMessageBox.No)
413 if answer == QtWidgets.QMessageBox.No:
414 log.info('User requested termination')
415 self.main_window.clean_up()
416 sys.exit()
417 # Set data location to default.
418 settings.remove('advanced/data path')
419 self.current_data_path = AppLocation.get_data_path()
420 log.warning('User requested data path set to default {path}'.format(path=self.current_data_path))
421 self.data_directory_label.setText(os.path.abspath(self.current_data_path))400 self.data_directory_label.setText(os.path.abspath(self.current_data_path))
422 # Don't allow data directory move if running portable.401 # Don't allow data directory move if running portable.
423 if settings.value('advanced/is portable'):402 if settings.value('advanced/is portable'):
424403
=== modified file 'openlp/core/ui/lib/treewidgetwithdnd.py'
--- openlp/core/ui/lib/treewidgetwithdnd.py 2016-04-17 19:32:15 +0000
+++ openlp/core/ui/lib/treewidgetwithdnd.py 2016-11-21 21:19:11 +0000
@@ -26,7 +26,7 @@
2626
27from PyQt5 import QtCore, QtGui, QtWidgets27from PyQt5 import QtCore, QtGui, QtWidgets
2828
29from openlp.core.common import Registry29from openlp.core.common import Registry, is_win
3030
3131
32class TreeWidgetWithDnD(QtWidgets.QTreeWidget):32class TreeWidgetWithDnD(QtWidgets.QTreeWidget):
@@ -108,6 +108,11 @@
108108
109 :param event: Handle of the event pint passed109 :param event: Handle of the event pint passed
110 """110 """
111 # If we are on Windows, OpenLP window will not be set on top. For example, user can drag images to Library and
112 # the folder stays on top of the group creation box. This piece of code fixes this issue.
113 if is_win():
114 self.setWindowState(self.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive)
115 self.setWindowState(QtCore.Qt.WindowNoState)
111 if event.mimeData().hasUrls():116 if event.mimeData().hasUrls():
112 event.setDropAction(QtCore.Qt.CopyAction)117 event.setDropAction(QtCore.Qt.CopyAction)
113 event.accept()118 event.accept()
114119
=== modified file 'openlp/core/ui/slidecontroller.py'
--- openlp/core/ui/slidecontroller.py 2016-09-12 12:11:59 +0000
+++ openlp/core/ui/slidecontroller.py 2016-11-21 21:19:11 +0000
@@ -722,8 +722,10 @@
722 # Reset the button722 # Reset the button
723 self.play_slides_once.setChecked(False)723 self.play_slides_once.setChecked(False)
724 self.play_slides_once.setIcon(build_icon(':/media/media_time.png'))724 self.play_slides_once.setIcon(build_icon(':/media/media_time.png'))
725 self.play_slides_once.setText(UiStrings().PlaySlidesToEnd)
725 self.play_slides_loop.setChecked(False)726 self.play_slides_loop.setChecked(False)
726 self.play_slides_loop.setIcon(build_icon(':/media/media_time.png'))727 self.play_slides_loop.setIcon(build_icon(':/media/media_time.png'))
728 self.play_slides_loop.setText(UiStrings().PlaySlidesInLoop)
727 if item.is_text():729 if item.is_text():
728 if (Settings().value(self.main_window.songs_settings_section + '/display songbar') and730 if (Settings().value(self.main_window.songs_settings_section + '/display songbar') and
729 not self.song_menu.menu().isEmpty()):731 not self.song_menu.menu().isEmpty()):
730732
=== modified file 'tests/functional/openlp_core_lib/test_lib.py'
--- tests/functional/openlp_core_lib/test_lib.py 2016-10-30 08:29:22 +0000
+++ tests/functional/openlp_core_lib/test_lib.py 2016-11-21 21:19:11 +0000
@@ -688,8 +688,8 @@
688 string_result = create_separated_list(string_list)688 string_result = create_separated_list(string_list)
689689
690 # THEN: We should have "Author 1, Author 2, and Author 3"690 # THEN: We should have "Author 1, Author 2, and Author 3"
691 assert string_result == 'Author 1, Author 2, and Author 3', 'The string should be u\'Author 1, ' \691 self.assertEqual(string_result, 'Author 1, Author 2 and Author 3', 'The string should be "Author 1, '
692 'Author 2, and Author 3\'.'692 'Author 2, and Author 3".')
693693
694 def test_create_separated_list_empty_list(self):694 def test_create_separated_list_empty_list(self):
695 """695 """
@@ -705,56 +705,44 @@
705 string_result = create_separated_list(string_list)705 string_result = create_separated_list(string_list)
706706
707 # THEN: We shoud have an emptry string.707 # THEN: We shoud have an emptry string.
708 assert string_result == '', 'The string sould be empty.'708 self.assertEqual(string_result, '', 'The string sould be empty.')
709709
710 def test_create_separated_list_with_one_item(self):710 def test_create_separated_list_with_one_item(self):
711 """711 """
712 Test the create_separated_list function with a list consisting of only one entry712 Test the create_separated_list function with a list consisting of only one entry
713 """713 """
714 with patch('openlp.core.lib.Qt') as mocked_qt:714 # GIVEN: A list with a string.
715 # GIVEN: A list with a string and the mocked Qt module.715 string_list = ['Author 1']
716 mocked_qt.PYQT_VERSION_STR = '4.8'716
717 mocked_qt.qVersion.return_value = '4.7'717 # WHEN: We get a string build from the entries it the list and a separator.
718 string_list = ['Author 1']718 string_result = create_separated_list(string_list)
719719
720 # WHEN: We get a string build from the entries it the list and a separator.720 # THEN: We should have "Author 1"
721 string_result = create_separated_list(string_list)721 self.assertEqual(string_result, 'Author 1', 'The string should be "Author 1".')
722
723 # THEN: We should have "Author 1"
724 assert string_result == 'Author 1', 'The string should be u\'Author 1\'.'
725722
726 def test_create_separated_list_with_two_items(self):723 def test_create_separated_list_with_two_items(self):
727 """724 """
728 Test the create_separated_list function with a list of two entries725 Test the create_separated_list function with a list of two entries
729 """726 """
730 with patch('openlp.core.lib.Qt') as mocked_qt, patch('openlp.core.lib.translate') as mocked_translate:727 # GIVEN: A list with two strings.
731 # GIVEN: A list of strings and the mocked Qt module.728 string_list = ['Author 1', 'Author 2']
732 mocked_qt.PYQT_VERSION_STR = '4.8'729
733 mocked_qt.qVersion.return_value = '4.7'730 # WHEN: We get a string build from the entries it the list and a seperator.
734 mocked_translate.return_value = '%s and %s'731 string_result = create_separated_list(string_list)
735 string_list = ['Author 1', 'Author 2']732
736733 # THEN: We should have "Author 1 and Author 2"
737 # WHEN: We get a string build from the entries it the list and a seperator.734 self.assertEqual(string_result, 'Author 1 and Author 2', 'The string should be "Author 1 and Author 2".')
738 string_result = create_separated_list(string_list)
739
740 # THEN: We should have "Author 1 and Author 2"
741 assert string_result == 'Author 1 and Author 2', 'The string should be u\'Author 1 and Author 2\'.'
742735
743 def test_create_separated_list_with_three_items(self):736 def test_create_separated_list_with_three_items(self):
744 """737 """
745 Test the create_separated_list function with a list of three items738 Test the create_separated_list function with a list of three items
746 """739 """
747 with patch('openlp.core.lib.Qt') as mocked_qt, patch('openlp.core.lib.translate') as mocked_translate:740 # GIVEN: A list with three strings.
748 # GIVEN: A list with a string and the mocked Qt module.741 string_list = ['Author 1', 'Author 2', 'Author 3']
749 mocked_qt.PYQT_VERSION_STR = '4.8'742
750 mocked_qt.qVersion.return_value = '4.7'743 # WHEN: We get a string build from the entries it the list and a seperator.
751 # Always return the untranslated string.744 string_result = create_separated_list(string_list)
752 mocked_translate.side_effect = lambda module, string_to_translate, comment: string_to_translate745
753 string_list = ['Author 1', 'Author 2', 'Author 3']746 # THEN: We should have "Author 1, Author 2 and Author 3"
754747 self.assertEqual(string_result, 'Author 1, Author 2 and Author 3', 'The string should be "Author 1, '
755 # WHEN: We get a string build from the entries it the list and a seperator.748 'Author 2, and Author 3".')
756 string_result = create_separated_list(string_list)
757
758 # THEN: We should have "Author 1, Author 2, and Author 3"
759 assert string_result == 'Author 1, Author 2, and Author 3', 'The string should be u\'Author 1, ' \
760 'Author 2, and Author 3\'.'