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

Proposed by Tomas Groth on 2016-11-21
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 2016-11-21 Approve on 2016-11-21
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

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'openlp/core/__init__.py'
2--- openlp/core/__init__.py 2016-04-27 12:49:55 +0000
3+++ openlp/core/__init__.py 2016-11-21 21:19:11 +0000
4@@ -177,6 +177,38 @@
5 self.shared_memory.create(1)
6 return False
7
8+ def is_data_path_missing(self):
9+ """
10+ Check if the data folder path exists.
11+ """
12+ data_folder_path = AppLocation.get_data_path()
13+ if not os.path.exists(data_folder_path):
14+ log.critical('Database was not found in: ' + data_folder_path)
15+ status = QtWidgets.QMessageBox.critical(None, translate('OpenLP', 'Data Directory Error'),
16+ translate('OpenLP', 'OpenLP data folder was not found in:\n\n{path}'
17+ '\n\nThe location of the data folder was '
18+ 'previously changed from the OpenLP\'s '
19+ 'default location. If the data was stored on '
20+ 'removable device, that device needs to be '
21+ 'made available.\n\nYou may reset the data '
22+ 'location back to the default location, '
23+ 'or you can try to make the current location '
24+ 'available.\n\nDo you want to reset to the '
25+ 'default data location? If not, OpenLP will be '
26+ 'closed so you can try to fix the the problem.')
27+ .format(path=data_folder_path),
28+ QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes |
29+ QtWidgets.QMessageBox.No),
30+ QtWidgets.QMessageBox.No)
31+ if status == QtWidgets.QMessageBox.No:
32+ # If answer was "No", return "True", it will shutdown OpenLP in def main
33+ log.info('User requested termination')
34+ return True
35+ # If answer was "Yes", remove the custom data path thus resetting the default location.
36+ Settings().remove('advanced/data path')
37+ log.info('Database location has been reset to the default settings.')
38+ return False
39+
40 def hook_exception(self, exc_type, value, traceback):
41 """
42 Add an exception hook so that any uncaught exceptions are displayed in this window rather than somewhere where
43@@ -208,8 +240,8 @@
44 # If data_version is different from the current version ask if we should backup the data folder
45 elif data_version != openlp_version:
46 if QtWidgets.QMessageBox.question(None, translate('OpenLP', 'Backup'),
47- translate('OpenLP', 'OpenLP has been upgraded, do you want to create '
48- 'a backup of OpenLPs data folder?'),
49+ translate('OpenLP', 'OpenLP has been upgraded, do you want to create\n'
50+ 'a backup of the old data folder?'),
51 QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
52 QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.Yes:
53 # Create copy of data folder
54@@ -223,8 +255,8 @@
55 translate('OpenLP', 'Backup of the data folder failed!'))
56 return
57 message = translate('OpenLP',
58- 'A backup of the data folder has been created'
59- 'at {text}').format(text=data_folder_backup_path)
60+ 'A backup of the data folder has been created at:\n\n'
61+ '{text}').format(text=data_folder_backup_path)
62 QtWidgets.QMessageBox.information(None, translate('OpenLP', 'Backup'), message)
63
64 # Update the version in the settings
65@@ -368,9 +400,13 @@
66 Registry.create()
67 Registry().register('application', application)
68 application.setApplicationVersion(get_application_version()['version'])
69- # Instance check
70+ # Check if an instance of OpenLP is already running. Quit if there is a running instance and the user only wants one
71 if application.is_already_running():
72 sys.exit()
73+ # If the custom data path is missing and the user wants to restore the data path, quit OpenLP.
74+ if application.is_data_path_missing():
75+ application.shared_memory.detach()
76+ sys.exit()
77 # Remove/convert obsolete settings.
78 Settings().remove_obsolete_settings()
79 # First time checks in settings
80
81=== modified file 'openlp/core/lib/__init__.py'
82--- openlp/core/lib/__init__.py 2016-10-30 08:29:22 +0000
83+++ openlp/core/lib/__init__.py 2016-11-21 21:19:11 +0000
84@@ -310,30 +310,23 @@
85
86 def create_separated_list(string_list):
87 """
88- Returns a string that represents a join of a list of strings with a localized separator. This function corresponds
89-
90- to QLocale::createSeparatedList which was introduced in Qt 4.8 and implements the algorithm from
91- http://www.unicode.org/reports/tr35/#ListPatterns
92-
93- :param string_list: List of unicode strings
94+ Returns a string that represents a join of a list of strings with a localized separator.
95+ Localized separation will be done via the translate() function by the translators.
96+
97+ :param string_list: List of unicode strings
98+ :return: Formatted string
99 """
100- if LooseVersion(Qt.PYQT_VERSION_STR) >= LooseVersion('4.9') and LooseVersion(Qt.qVersion()) >= LooseVersion('4.8'):
101- return QtCore.QLocale().createSeparatedList(string_list)
102- if not string_list:
103- return ''
104- elif len(string_list) == 1:
105- return string_list[0]
106- # TODO: Verify mocking of translate() test before conversion
107- elif len(string_list) == 2:
108- return translate('OpenLP.core.lib', '%s and %s',
109- 'Locale list separator: 2 items') % (string_list[0], string_list[1])
110+ list_length = len(string_list)
111+ if list_length == 1:
112+ list_to_string = string_list[0]
113+ elif list_length == 2:
114+ list_to_string = translate('OpenLP.core.lib', '{one} and {two}').format(one=string_list[0], two=string_list[1])
115+ elif list_length > 2:
116+ list_to_string = translate('OpenLP.core.lib', '{first} and {last}').format(first=', '.join(string_list[:-1]),
117+ last=string_list[-1])
118 else:
119- merged = translate('OpenLP.core.lib', '%s, and %s',
120- 'Locale list separator: end') % (string_list[-2], string_list[-1])
121- for index in reversed(list(range(1, len(string_list) - 2))):
122- merged = translate('OpenLP.core.lib', '%s, %s',
123- 'Locale list separator: middle') % (string_list[index], merged)
124- return translate('OpenLP.core.lib', '%s, %s', 'Locale list separator: start') % (string_list[0], merged)
125+ list_to_string = ''
126+ return list_to_string
127
128
129 from .exceptions import ValidationError
130
131=== modified file 'openlp/core/ui/advancedtab.py'
132--- openlp/core/ui/advancedtab.py 2016-08-11 22:12:50 +0000
133+++ openlp/core/ui/advancedtab.py 2016-11-21 21:19:11 +0000
134@@ -397,27 +397,6 @@
135 self.data_directory_cancel_button.hide()
136 # Since data location can be changed, make sure the path is present.
137 self.current_data_path = AppLocation.get_data_path()
138- if not os.path.exists(self.current_data_path):
139- log.error('Data path not found {path}'.format(path=self.current_data_path))
140- answer = QtWidgets.QMessageBox.critical(
141- self, translate('OpenLP.AdvancedTab', 'Data Directory Error'),
142- translate('OpenLP.AdvancedTab', 'OpenLP data directory was not found\n\n{path}\n\n'
143- 'This data directory was previously changed from the OpenLP '
144- 'default location. If the new location was on removable '
145- 'media, that media needs to be made available.\n\n'
146- 'Click "No" to stop loading OpenLP. allowing you to fix the the problem.\n\n'
147- 'Click "Yes" to reset the data directory to the default '
148- 'location.').format(path=self.current_data_path),
149- QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No),
150- QtWidgets.QMessageBox.No)
151- if answer == QtWidgets.QMessageBox.No:
152- log.info('User requested termination')
153- self.main_window.clean_up()
154- sys.exit()
155- # Set data location to default.
156- settings.remove('advanced/data path')
157- self.current_data_path = AppLocation.get_data_path()
158- log.warning('User requested data path set to default {path}'.format(path=self.current_data_path))
159 self.data_directory_label.setText(os.path.abspath(self.current_data_path))
160 # Don't allow data directory move if running portable.
161 if settings.value('advanced/is portable'):
162
163=== modified file 'openlp/core/ui/lib/treewidgetwithdnd.py'
164--- openlp/core/ui/lib/treewidgetwithdnd.py 2016-04-17 19:32:15 +0000
165+++ openlp/core/ui/lib/treewidgetwithdnd.py 2016-11-21 21:19:11 +0000
166@@ -26,7 +26,7 @@
167
168 from PyQt5 import QtCore, QtGui, QtWidgets
169
170-from openlp.core.common import Registry
171+from openlp.core.common import Registry, is_win
172
173
174 class TreeWidgetWithDnD(QtWidgets.QTreeWidget):
175@@ -108,6 +108,11 @@
176
177 :param event: Handle of the event pint passed
178 """
179+ # If we are on Windows, OpenLP window will not be set on top. For example, user can drag images to Library and
180+ # the folder stays on top of the group creation box. This piece of code fixes this issue.
181+ if is_win():
182+ self.setWindowState(self.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive)
183+ self.setWindowState(QtCore.Qt.WindowNoState)
184 if event.mimeData().hasUrls():
185 event.setDropAction(QtCore.Qt.CopyAction)
186 event.accept()
187
188=== modified file 'openlp/core/ui/slidecontroller.py'
189--- openlp/core/ui/slidecontroller.py 2016-09-12 12:11:59 +0000
190+++ openlp/core/ui/slidecontroller.py 2016-11-21 21:19:11 +0000
191@@ -722,8 +722,10 @@
192 # Reset the button
193 self.play_slides_once.setChecked(False)
194 self.play_slides_once.setIcon(build_icon(':/media/media_time.png'))
195+ self.play_slides_once.setText(UiStrings().PlaySlidesToEnd)
196 self.play_slides_loop.setChecked(False)
197 self.play_slides_loop.setIcon(build_icon(':/media/media_time.png'))
198+ self.play_slides_loop.setText(UiStrings().PlaySlidesInLoop)
199 if item.is_text():
200 if (Settings().value(self.main_window.songs_settings_section + '/display songbar') and
201 not self.song_menu.menu().isEmpty()):
202
203=== modified file 'tests/functional/openlp_core_lib/test_lib.py'
204--- tests/functional/openlp_core_lib/test_lib.py 2016-10-30 08:29:22 +0000
205+++ tests/functional/openlp_core_lib/test_lib.py 2016-11-21 21:19:11 +0000
206@@ -688,8 +688,8 @@
207 string_result = create_separated_list(string_list)
208
209 # THEN: We should have "Author 1, Author 2, and Author 3"
210- assert string_result == 'Author 1, Author 2, and Author 3', 'The string should be u\'Author 1, ' \
211- 'Author 2, and Author 3\'.'
212+ self.assertEqual(string_result, 'Author 1, Author 2 and Author 3', 'The string should be "Author 1, '
213+ 'Author 2, and Author 3".')
214
215 def test_create_separated_list_empty_list(self):
216 """
217@@ -705,56 +705,44 @@
218 string_result = create_separated_list(string_list)
219
220 # THEN: We shoud have an emptry string.
221- assert string_result == '', 'The string sould be empty.'
222+ self.assertEqual(string_result, '', 'The string sould be empty.')
223
224 def test_create_separated_list_with_one_item(self):
225 """
226 Test the create_separated_list function with a list consisting of only one entry
227 """
228- with patch('openlp.core.lib.Qt') as mocked_qt:
229- # GIVEN: A list with a string and the mocked Qt module.
230- mocked_qt.PYQT_VERSION_STR = '4.8'
231- mocked_qt.qVersion.return_value = '4.7'
232- string_list = ['Author 1']
233-
234- # WHEN: We get a string build from the entries it the list and a separator.
235- string_result = create_separated_list(string_list)
236-
237- # THEN: We should have "Author 1"
238- assert string_result == 'Author 1', 'The string should be u\'Author 1\'.'
239+ # GIVEN: A list with a string.
240+ string_list = ['Author 1']
241+
242+ # WHEN: We get a string build from the entries it the list and a separator.
243+ string_result = create_separated_list(string_list)
244+
245+ # THEN: We should have "Author 1"
246+ self.assertEqual(string_result, 'Author 1', 'The string should be "Author 1".')
247
248 def test_create_separated_list_with_two_items(self):
249 """
250 Test the create_separated_list function with a list of two entries
251 """
252- with patch('openlp.core.lib.Qt') as mocked_qt, patch('openlp.core.lib.translate') as mocked_translate:
253- # GIVEN: A list of strings and the mocked Qt module.
254- mocked_qt.PYQT_VERSION_STR = '4.8'
255- mocked_qt.qVersion.return_value = '4.7'
256- mocked_translate.return_value = '%s and %s'
257- string_list = ['Author 1', 'Author 2']
258-
259- # WHEN: We get a string build from the entries it the list and a seperator.
260- string_result = create_separated_list(string_list)
261-
262- # THEN: We should have "Author 1 and Author 2"
263- assert string_result == 'Author 1 and Author 2', 'The string should be u\'Author 1 and Author 2\'.'
264+ # GIVEN: A list with two strings.
265+ string_list = ['Author 1', 'Author 2']
266+
267+ # WHEN: We get a string build from the entries it the list and a seperator.
268+ string_result = create_separated_list(string_list)
269+
270+ # THEN: We should have "Author 1 and Author 2"
271+ self.assertEqual(string_result, 'Author 1 and Author 2', 'The string should be "Author 1 and Author 2".')
272
273 def test_create_separated_list_with_three_items(self):
274 """
275 Test the create_separated_list function with a list of three items
276 """
277- with patch('openlp.core.lib.Qt') as mocked_qt, patch('openlp.core.lib.translate') as mocked_translate:
278- # GIVEN: A list with a string and the mocked Qt module.
279- mocked_qt.PYQT_VERSION_STR = '4.8'
280- mocked_qt.qVersion.return_value = '4.7'
281- # Always return the untranslated string.
282- mocked_translate.side_effect = lambda module, string_to_translate, comment: string_to_translate
283- string_list = ['Author 1', 'Author 2', 'Author 3']
284-
285- # WHEN: We get a string build from the entries it the list and a seperator.
286- string_result = create_separated_list(string_list)
287-
288- # THEN: We should have "Author 1, Author 2, and Author 3"
289- assert string_result == 'Author 1, Author 2, and Author 3', 'The string should be u\'Author 1, ' \
290- 'Author 2, and Author 3\'.'
291+ # GIVEN: A list with three strings.
292+ string_list = ['Author 1', 'Author 2', 'Author 3']
293+
294+ # WHEN: We get a string build from the entries it the list and a seperator.
295+ string_result = create_separated_list(string_list)
296+
297+ # THEN: We should have "Author 1, Author 2 and Author 3"
298+ self.assertEqual(string_result, 'Author 1, Author 2 and Author 3', 'The string should be "Author 1, '
299+ 'Author 2, and Author 3".')