Merge lp:~nataliabidart/ubuntuone-control-panel/better-folder-mgmt into lp:ubuntuone-control-panel
- better-folder-mgmt
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | dobey | ||||
Approved revision: | 174 | ||||
Merged at revision: | 166 | ||||
Proposed branch: | lp:~nataliabidart/ubuntuone-control-panel/better-folder-mgmt | ||||
Merge into: | lp:ubuntuone-control-panel | ||||
Diff against target: |
1452 lines (+641/-246) 25 files modified
data/qt/controlpanel.ui (+3/-3) data/qt/device.ui (+1/-1) data/qt/devices.ui (+5/-12) data/qt/filesyncstatus.ui (+1/-1) data/qt/folders.ui (+1/-1) data/qt/preferences.ui (+82/-88) data/qt/profile.ui (+3/-3) data/qt/services.ui (+3/-26) data/qt/ubuntuonebin.ui (+64/-0) ubuntuone/controlpanel/gui/__init__.py (+5/-0) ubuntuone/controlpanel/gui/qt/devices.py (+22/-8) ubuntuone/controlpanel/gui/qt/folders.py (+32/-14) ubuntuone/controlpanel/gui/qt/main/linux.py (+1/-1) ubuntuone/controlpanel/gui/qt/preferences.py (+10/-17) ubuntuone/controlpanel/gui/qt/profile.py (+10/-6) ubuntuone/controlpanel/gui/qt/services.py (+13/-6) ubuntuone/controlpanel/gui/qt/tests/__init__.py (+3/-4) ubuntuone/controlpanel/gui/qt/tests/test_devices.py (+26/-5) ubuntuone/controlpanel/gui/qt/tests/test_folders.py (+120/-31) ubuntuone/controlpanel/gui/qt/tests/test_preferences.py (+19/-9) ubuntuone/controlpanel/gui/qt/tests/test_profile.py (+19/-5) ubuntuone/controlpanel/gui/qt/tests/test_services.py (+19/-5) ubuntuone/controlpanel/gui/qt/tests/test_ubuntuonebin.py (+92/-0) ubuntuone/controlpanel/gui/qt/ubuntuonebin.py (+85/-0) ubuntuone/controlpanel/sd_client/linux.py (+2/-0) |
||||
To merge this branch: | bzr merge lp:~nataliabidart/ubuntuone-control-panel/better-folder-mgmt | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Roberto Alsina (community) | Approve | ||
Eric Casteleijn (community) | Approve | ||
Review via email: mp+65281@code.launchpad.net |
Commit message
- Implementing a common ancestor for Control Panel's tabs to gracefully handle info processing and delays from the backend.
- Cloud folder creation process is now improved: new UDFs can not be nested, but can be a prefix of an existent folder (LP: #798964).
- Going back to using the system's SDTool, seems like DBus is not longer segfaulting (finger cross!).
Description of the change
DISCLAIMER: please ignore all the warnings that look like:
QLayout: Attempting to add QLayout "" to SomePanel "Form", which already has a layout
I know what casues them, I'm not sure how to fix them, I need our QT guru to teach me how to nest Ui_Forms better.
To test IRL, run:
DEBUG=True PYTHONPATH=. ./bin/ubuntuone
and go to the Cloud Folders tab. You should your REAL folder info (be careful when playing around!). You should try to add new folders inside an UDF, and also trying with a folder which has in its name the same prefix as an existent UDF (this last operation should succeed).
Eric Casteleijn (thisfred) wrote : | # |
Eric Casteleijn (thisfred) wrote : | # |
+ 'Please choose a folder outside the existent cloud '
467 + 'folder "%(existent_
s/existent/
Eric Casteleijn (thisfred) wrote : | # |
in FoldersPanel.
folder = unicode(folder)
This will take any 8-bit ascii string and make it into a unicode string with the same content, or return unmodified anything that is already a unicode string. Anything that is an encoded string with non-ascii characters will break it though.
Of course showing a path with an unknown encoding to the user will present challenges, but at least we should not error. So maybe use: unicode(folder, 'utf-8', 'replace'), but I have no idea if utf-8 is actually the default on Windows.
Eric Casteleijn (thisfred) wrote : | # |
I see you test for unicode paths, but the trick is to test for encoded paths instead (or at least I think so, does the QT ui do the encoding for you? In that case, the unicode() call is still unnecessary.)
Eric Casteleijn (thisfred) : | # |
Eric Casteleijn (thisfred) wrote : | # |
I will set this to approve, to not hold this up in case I'm not around to re-review, but please fix the last two concerns (or explain why I'm wrong about them ;)
Natalia Bidart (nataliabidart) wrote : | # |
> I see you test for unicode paths, but the trick is to test for encoded paths
> instead (or at least I think so, does the QT ui do the encoding for you? In
> that case, the unicode() call is still unnecessary.)
Qt returns a Qstring, which does not support any of the string/unicode methods from the python standard lib. So we need to cast explicitly to unicode() in order to operate with them.
But since you pointed this out, I went to the doc again and I realized I should do a better string management (http://
Natalia Bidart (nataliabidart) wrote : | # |
> + 'Please choose a folder outside the existent cloud '
> 467 + 'folder "%(existent_
>
> s/existent/
Fixed!
Natalia Bidart (nataliabidart) wrote : | # |
> - Adding folder A/B/C and then adding folder A/B/C/D fails as it should and
> gives a nice error dialog.
> - Adding folder A/B/C/D and then adding folder A/B/C fails, and shows this on
> the command line:
>
> Failure: ubuntuone.
> (dbus.Dictionar
> dbus.String(
> signature=
>
> But after this everything on the Files tab remains greyed out for a long time,
> and then becomes reponsive again with no changes.
>
> We should at least show a similar error message. (For a future version we
> could think about just adding the new UDF and removing all previous once that
> it contains, but that may be harder to explain than just disallowing it.)
>
> I will approve this branch, (unless I find other problems) since it is quite
> big already, so the other case can be solved in a new one.
Thanks. I opened bug #800161 to track this.
> I've tested with folders sharing a prefix, but I don't see that being very
> useful, since all UDFs share a common prefix already (/home/$USER/), so I
> would assume this has always worked? Maybe I misunderstand the instruction.
Is very common to make the mistake of not adding the path separator to a path when checking if one oath is prefix of the other, so what I meant was:
having a UDF called A/B, try to create a new one in A/B1. The first version of this branch will no allow it ;-) (but this final version does, and has tests for that).
Natalia Bidart (nataliabidart) wrote : | # |
> > I see you test for unicode paths, but the trick is to test for encoded paths
> > instead (or at least I think so, does the QT ui do the encoding for you? In
> > that case, the unicode() call is still unnecessary.)
>
> Qt returns a Qstring, which does not support any of the string/unicode methods
> from the python standard lib. So we need to cast explicitly to unicode() in
> order to operate with them.
>
> But since you pointed this out, I went to the doc again and I realized I
> should do a better string management
> (http://
> and-unicode-
Reading the doc in detail, seems like a QString is already handling all the needed encoding/decoding. As you can see from a run:
PyQt4.QtCore.
but I do need to extract the unicode inside the QString, since that is what the backend expects. Does it make sense?
Eric Casteleijn (thisfred) wrote : | # |
It does, but here you're passing in a unicode string, what happens if you pass in an encoded string (which I'm pretty sure you can get from the filesystem.)
PyQt4.QtCore.
Ubuntu One Auto Pilot (otto-pilot) wrote : | # |
The attempt to merge lp:~nataliabidart/ubuntuone-control-panel/better-folder-mgmt into lp:ubuntuone-control-panel failed. Below is the output from the failed tests.
running build
Compiled data/qt/device.ui into ubuntuone/
Compiled data/qt/
Compiled data/qt/
Compiled data/qt/folders.ui into ubuntuone/
Compiled data/qt/devices.ui into ubuntuone/
Compiled data/qt/
compiled data/qt/images.qrc into ubuntuone/
Compiled data/qt/
Compiled data/qt/services.ui into ubuntuone/
Compiled data/qt/
Compiled data/qt/profile.ui into ubuntuone/
running build_py
creating build
creating build/lib.
creating build/lib.
copying ubuntuone/
creating build/lib.
copying ubuntuone/
copying ubuntuone/
copying ubuntuone/
copying ubuntuone/
copying ubuntuone/
copying ubuntuone/
copying ubuntuone/
creating build/lib.
copying ubuntuone/
creating build/lib.
copying ubuntuone/
copying ubuntuone/
copying ubuntuone/
copying ubuntuone/
creating build/lib.
copying ubuntuone/
copying ubuntuone/
copying ubuntuone/
copying ubuntuone/
copying ubuntuone/co...
- 174. By Natalia Bidart
-
Why lint why!
Preview Diff
1 | === modified file 'data/qt/controlpanel.ui' |
2 | --- data/qt/controlpanel.ui 2011-06-02 15:49:20 +0000 |
3 | +++ data/qt/controlpanel.ui 2011-06-21 16:31:15 +0000 |
4 | @@ -6,8 +6,8 @@ |
5 | <rect> |
6 | <x>0</x> |
7 | <y>0</y> |
8 | - <width>646</width> |
9 | - <height>487</height> |
10 | + <width>374</width> |
11 | + <height>172</height> |
12 | </rect> |
13 | </property> |
14 | <property name="sizePolicy"> |
15 | @@ -17,7 +17,7 @@ |
16 | </sizepolicy> |
17 | </property> |
18 | <property name="windowTitle"> |
19 | - <string>Form</string> |
20 | + <string notr="true">Form</string> |
21 | </property> |
22 | <layout class="QVBoxLayout" name="verticalLayout"> |
23 | <item> |
24 | |
25 | === modified file 'data/qt/device.ui' |
26 | --- data/qt/device.ui 2011-06-02 15:48:25 +0000 |
27 | +++ data/qt/device.ui 2011-06-21 16:31:15 +0000 |
28 | @@ -11,7 +11,7 @@ |
29 | </rect> |
30 | </property> |
31 | <property name="windowTitle"> |
32 | - <string>Form</string> |
33 | + <string notr="true">Form</string> |
34 | </property> |
35 | <layout class="QHBoxLayout" name="horizontalLayout"> |
36 | <item> |
37 | |
38 | === modified file 'data/qt/devices.ui' |
39 | --- data/qt/devices.ui 2011-06-02 15:30:51 +0000 |
40 | +++ data/qt/devices.ui 2011-06-21 16:31:15 +0000 |
41 | @@ -6,22 +6,15 @@ |
42 | <rect> |
43 | <x>0</x> |
44 | <y>0</y> |
45 | - <width>702</width> |
46 | - <height>434</height> |
47 | + <width>558</width> |
48 | + <height>156</height> |
49 | </rect> |
50 | </property> |
51 | <property name="windowTitle"> |
52 | - <string>Form</string> |
53 | + <string notr="true">Form</string> |
54 | </property> |
55 | <layout class="QVBoxLayout" name="verticalLayout"> |
56 | <item> |
57 | - <widget class="QLabel" name="tab_header_label"> |
58 | - <property name="text"> |
59 | - <string>All cloud connected devices</string> |
60 | - </property> |
61 | - </widget> |
62 | - </item> |
63 | - <item> |
64 | <widget class="QScrollArea" name="scrollArea"> |
65 | <property name="verticalScrollBarPolicy"> |
66 | <enum>Qt::ScrollBarAlwaysOn</enum> |
67 | @@ -37,8 +30,8 @@ |
68 | <rect> |
69 | <x>0</x> |
70 | <y>0</y> |
71 | - <width>669</width> |
72 | - <height>391</height> |
73 | + <width>535</width> |
74 | + <height>136</height> |
75 | </rect> |
76 | </property> |
77 | <layout class="QGridLayout" name="gridLayout_2"> |
78 | |
79 | === modified file 'data/qt/filesyncstatus.ui' |
80 | --- data/qt/filesyncstatus.ui 2011-05-26 22:18:04 +0000 |
81 | +++ data/qt/filesyncstatus.ui 2011-06-21 16:31:15 +0000 |
82 | @@ -11,7 +11,7 @@ |
83 | </rect> |
84 | </property> |
85 | <property name="windowTitle"> |
86 | - <string>Form</string> |
87 | + <string notr="true">Form</string> |
88 | </property> |
89 | <layout class="QHBoxLayout" name="horizontalLayout_2"> |
90 | <item> |
91 | |
92 | === modified file 'data/qt/folders.ui' |
93 | --- data/qt/folders.ui 2011-06-01 18:10:22 +0000 |
94 | +++ data/qt/folders.ui 2011-06-21 16:31:15 +0000 |
95 | @@ -11,7 +11,7 @@ |
96 | </rect> |
97 | </property> |
98 | <property name="windowTitle"> |
99 | - <string>Form</string> |
100 | + <string notr="true">Form</string> |
101 | </property> |
102 | <layout class="QVBoxLayout" name="verticalLayout"> |
103 | <item> |
104 | |
105 | === modified file 'data/qt/preferences.ui' |
106 | --- data/qt/preferences.ui 2011-06-16 18:18:23 +0000 |
107 | +++ data/qt/preferences.ui 2011-06-21 16:31:15 +0000 |
108 | @@ -6,98 +6,92 @@ |
109 | <rect> |
110 | <x>0</x> |
111 | <y>0</y> |
112 | - <width>737</width> |
113 | - <height>481</height> |
114 | + <width>469</width> |
115 | + <height>352</height> |
116 | </rect> |
117 | </property> |
118 | <property name="windowTitle"> |
119 | - <string>Form</string> |
120 | + <string notr="true">Form</string> |
121 | </property> |
122 | - <layout class="QVBoxLayout" name="verticalLayout_2"> |
123 | + <layout class="QVBoxLayout" name="verticalLayout"> |
124 | <item> |
125 | <widget class="QGroupBox" name="verticalGroupBox"> |
126 | - <layout class="QVBoxLayout" name="verticalLayout_4"> |
127 | - <item> |
128 | - <widget class="QGroupBox" name="groupBox"> |
129 | - <property name="title"> |
130 | - <string>Bandwidth settings</string> |
131 | - </property> |
132 | - <layout class="QGridLayout" name="gridLayout"> |
133 | - <item row="0" column="0"> |
134 | - <widget class="QCheckBox" name="limit_uploads_checkbox"> |
135 | - <property name="text"> |
136 | - <string>Limit upload speed to</string> |
137 | - </property> |
138 | - </widget> |
139 | - </item> |
140 | - <item row="0" column="1"> |
141 | - <widget class="QSpinBox" name="upload_speed_spinbox"> |
142 | - <property name="minimum"> |
143 | - <number>-1</number> |
144 | - </property> |
145 | - <property name="maximum"> |
146 | - <number>999999999</number> |
147 | - </property> |
148 | - </widget> |
149 | - </item> |
150 | - <item row="0" column="2"> |
151 | - <widget class="QLabel" name="kbps_label_1"> |
152 | - <property name="text"> |
153 | - <string>Kilobits per second</string> |
154 | - </property> |
155 | - </widget> |
156 | - </item> |
157 | - <item row="2" column="0"> |
158 | - <widget class="QCheckBox" name="limit_downloads_checkbox"> |
159 | - <property name="text"> |
160 | - <string>Limit download speed to</string> |
161 | - </property> |
162 | - </widget> |
163 | - </item> |
164 | - <item row="2" column="1"> |
165 | - <widget class="QSpinBox" name="download_speed_spinbox"> |
166 | - <property name="minimum"> |
167 | - <number>-1</number> |
168 | - </property> |
169 | - <property name="maximum"> |
170 | - <number>999999999</number> |
171 | - </property> |
172 | - </widget> |
173 | - </item> |
174 | - <item row="2" column="2"> |
175 | - <widget class="QLabel" name="kbps_label_2"> |
176 | - <property name="text"> |
177 | - <string>Kilobits per second</string> |
178 | - </property> |
179 | - </widget> |
180 | - </item> |
181 | - <item row="0" column="3"> |
182 | - <spacer name="horizontalSpacer_2"> |
183 | - <property name="orientation"> |
184 | - <enum>Qt::Horizontal</enum> |
185 | - </property> |
186 | - <property name="sizeHint" stdset="0"> |
187 | - <size> |
188 | - <width>40</width> |
189 | - <height>20</height> |
190 | - </size> |
191 | - </property> |
192 | - </spacer> |
193 | - </item> |
194 | - <item row="3" column="0" colspan="3"> |
195 | - <widget class="QLabel" name="label_2"> |
196 | - <property name="text"> |
197 | - <string>Please note that your files will not sync if you set bandwidth to 0</string> |
198 | - </property> |
199 | - <property name="scaledContents"> |
200 | - <bool>false</bool> |
201 | - </property> |
202 | - <property name="wordWrap"> |
203 | - <bool>false</bool> |
204 | - </property> |
205 | - </widget> |
206 | - </item> |
207 | - </layout> |
208 | + <property name="title"> |
209 | + <string>Bandwidth settings</string> |
210 | + </property> |
211 | + <layout class="QGridLayout" name="gridLayout"> |
212 | + <item row="0" column="0"> |
213 | + <widget class="QCheckBox" name="limit_uploads_checkbox"> |
214 | + <property name="text"> |
215 | + <string>Limit upload speed to</string> |
216 | + </property> |
217 | + </widget> |
218 | + </item> |
219 | + <item row="0" column="1"> |
220 | + <widget class="QSpinBox" name="upload_speed_spinbox"> |
221 | + <property name="minimum"> |
222 | + <number>-1</number> |
223 | + </property> |
224 | + <property name="maximum"> |
225 | + <number>999999999</number> |
226 | + </property> |
227 | + </widget> |
228 | + </item> |
229 | + <item row="0" column="2"> |
230 | + <widget class="QLabel" name="kbps_label_1"> |
231 | + <property name="text"> |
232 | + <string>Kilobits per second</string> |
233 | + </property> |
234 | + </widget> |
235 | + </item> |
236 | + <item row="2" column="0"> |
237 | + <widget class="QCheckBox" name="limit_downloads_checkbox"> |
238 | + <property name="text"> |
239 | + <string>Limit download speed to</string> |
240 | + </property> |
241 | + </widget> |
242 | + </item> |
243 | + <item row="2" column="1"> |
244 | + <widget class="QSpinBox" name="download_speed_spinbox"> |
245 | + <property name="minimum"> |
246 | + <number>-1</number> |
247 | + </property> |
248 | + <property name="maximum"> |
249 | + <number>999999999</number> |
250 | + </property> |
251 | + </widget> |
252 | + </item> |
253 | + <item row="2" column="2"> |
254 | + <widget class="QLabel" name="kbps_label_2"> |
255 | + <property name="text"> |
256 | + <string>Kilobits per second</string> |
257 | + </property> |
258 | + </widget> |
259 | + </item> |
260 | + <item row="0" column="3"> |
261 | + <spacer name="horizontalSpacer_2"> |
262 | + <property name="orientation"> |
263 | + <enum>Qt::Horizontal</enum> |
264 | + </property> |
265 | + <property name="sizeHint" stdset="0"> |
266 | + <size> |
267 | + <width>40</width> |
268 | + <height>20</height> |
269 | + </size> |
270 | + </property> |
271 | + </spacer> |
272 | + </item> |
273 | + <item row="3" column="0" colspan="3"> |
274 | + <widget class="QLabel" name="label_2"> |
275 | + <property name="text"> |
276 | + <string>Please note that your files will not sync if you set bandwidth to 0</string> |
277 | + </property> |
278 | + <property name="scaledContents"> |
279 | + <bool>false</bool> |
280 | + </property> |
281 | + <property name="wordWrap"> |
282 | + <bool>false</bool> |
283 | + </property> |
284 | </widget> |
285 | </item> |
286 | </layout> |
287 | @@ -108,7 +102,7 @@ |
288 | <property name="title"> |
289 | <string>File Sync Settings</string> |
290 | </property> |
291 | - <layout class="QVBoxLayout" name="verticalLayout"> |
292 | + <layout class="QVBoxLayout" name="verticalLayout_1"> |
293 | <item> |
294 | <widget class="QCheckBox" name="autoconnect_checkbox"> |
295 | <property name="text"> |
296 | @@ -179,7 +173,7 @@ |
297 | <property name="sizeHint" stdset="0"> |
298 | <size> |
299 | <width>20</width> |
300 | - <height>152</height> |
301 | + <height>40</height> |
302 | </size> |
303 | </property> |
304 | </spacer> |
305 | |
306 | === modified file 'data/qt/profile.ui' |
307 | --- data/qt/profile.ui 2011-06-16 19:49:43 +0000 |
308 | +++ data/qt/profile.ui 2011-06-21 16:31:15 +0000 |
309 | @@ -6,12 +6,12 @@ |
310 | <rect> |
311 | <x>0</x> |
312 | <y>0</y> |
313 | - <width>704</width> |
314 | - <height>449</height> |
315 | + <width>456</width> |
316 | + <height>189</height> |
317 | </rect> |
318 | </property> |
319 | <property name="windowTitle"> |
320 | - <string>Form</string> |
321 | + <string notr="true">Form</string> |
322 | </property> |
323 | <layout class="QVBoxLayout" name="verticalLayout"> |
324 | <item> |
325 | |
326 | === modified file 'data/qt/services.ui' |
327 | --- data/qt/services.ui 2011-05-28 06:16:06 +0000 |
328 | +++ data/qt/services.ui 2011-06-21 16:31:15 +0000 |
329 | @@ -6,22 +6,15 @@ |
330 | <rect> |
331 | <x>0</x> |
332 | <y>0</y> |
333 | - <width>636</width> |
334 | - <height>478</height> |
335 | + <width>341</width> |
336 | + <height>248</height> |
337 | </rect> |
338 | </property> |
339 | <property name="windowTitle"> |
340 | - <string>Form</string> |
341 | + <string notr="true">Form</string> |
342 | </property> |
343 | <layout class="QVBoxLayout" name="verticalLayout"> |
344 | <item> |
345 | - <widget class="QLabel" name="tab_header_label"> |
346 | - <property name="text"> |
347 | - <string>Selected plans and storage</string> |
348 | - </property> |
349 | - </widget> |
350 | - </item> |
351 | - <item> |
352 | <layout class="QVBoxLayout" name="mobile_plan_verticallayout"> |
353 | <item> |
354 | <layout class="QHBoxLayout" name="horizontalLayout"> |
355 | @@ -108,22 +101,6 @@ |
356 | </layout> |
357 | </item> |
358 | <item> |
359 | - <spacer name="verticalSpacer"> |
360 | - <property name="orientation"> |
361 | - <enum>Qt::Vertical</enum> |
362 | - </property> |
363 | - <property name="sizeType"> |
364 | - <enum>QSizePolicy::Preferred</enum> |
365 | - </property> |
366 | - <property name="sizeHint" stdset="0"> |
367 | - <size> |
368 | - <width>20</width> |
369 | - <height>20</height> |
370 | - </size> |
371 | - </property> |
372 | - </spacer> |
373 | - </item> |
374 | - <item> |
375 | <layout class="QVBoxLayout" name="storage_plan_verticallayout"> |
376 | <item> |
377 | <layout class="QHBoxLayout" name="horizontalLayout_3"> |
378 | |
379 | === added file 'data/qt/ubuntuonebin.ui' |
380 | --- data/qt/ubuntuonebin.ui 1970-01-01 00:00:00 +0000 |
381 | +++ data/qt/ubuntuonebin.ui 2011-06-21 16:31:15 +0000 |
382 | @@ -0,0 +1,64 @@ |
383 | +<?xml version="1.0" encoding="UTF-8"?> |
384 | +<ui version="4.0"> |
385 | + <class>Form</class> |
386 | + <widget class="QWidget" name="Form"> |
387 | + <property name="geometry"> |
388 | + <rect> |
389 | + <x>0</x> |
390 | + <y>0</y> |
391 | + <width>248</width> |
392 | + <height>59</height> |
393 | + </rect> |
394 | + </property> |
395 | + <property name="windowTitle"> |
396 | + <string notr="true">Form</string> |
397 | + </property> |
398 | + <layout class="QVBoxLayout" name="verticalLayout"> |
399 | + <item> |
400 | + <layout class="QHBoxLayout" name="horizontalLayout"> |
401 | + <item> |
402 | + <widget class="QLabel" name="title_label"> |
403 | + <property name="text"> |
404 | + <string notr="true">The Panel Title</string> |
405 | + </property> |
406 | + </widget> |
407 | + </item> |
408 | + <item> |
409 | + <spacer name="horizontalSpacer"> |
410 | + <property name="orientation"> |
411 | + <enum>Qt::Horizontal</enum> |
412 | + </property> |
413 | + <property name="sizeHint" stdset="0"> |
414 | + <size> |
415 | + <width>40</width> |
416 | + <height>20</height> |
417 | + </size> |
418 | + </property> |
419 | + </spacer> |
420 | + </item> |
421 | + <item> |
422 | + <widget class="QProgressBar" name="spinner"> |
423 | + <property name="sizePolicy"> |
424 | + <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> |
425 | + <horstretch>0</horstretch> |
426 | + <verstretch>0</verstretch> |
427 | + </sizepolicy> |
428 | + </property> |
429 | + <property name="maximum"> |
430 | + <number>0</number> |
431 | + </property> |
432 | + <property name="value"> |
433 | + <number>-1</number> |
434 | + </property> |
435 | + </widget> |
436 | + </item> |
437 | + </layout> |
438 | + </item> |
439 | + <item> |
440 | + <widget class="QWidget" name="container" native="true"/> |
441 | + </item> |
442 | + </layout> |
443 | + </widget> |
444 | + <resources/> |
445 | + <connections/> |
446 | +</ui> |
447 | |
448 | === modified file 'ubuntuone/controlpanel/gui/__init__.py' |
449 | --- ubuntuone/controlpanel/gui/__init__.py 2011-06-17 20:08:02 +0000 |
450 | +++ ubuntuone/controlpanel/gui/__init__.py 2011-06-21 16:31:15 +0000 |
451 | @@ -59,6 +59,7 @@ |
452 | |
453 | CONTACTS_LINK = UBUNTUONE_LINK |
454 | EDIT_ACCOUNT_LINK = UBUNTUONE_LINK + 'account/' |
455 | +EDIT_DEVICES_LINK = EDIT_ACCOUNT_LINK + 'machines/' |
456 | EDIT_PROFILE_LINK = 'https://login.ubuntu.com/' |
457 | LEARN_MORE_LINK = UBUNTUONE_LINK |
458 | MANAGE_FILES_LINK = UBUNTUONE_LINK + 'files/' |
459 | @@ -89,6 +90,10 @@ |
460 | '\n\n' |
461 | 'Please choose a folder inside your "%(home_folder)s" ' |
462 | 'directory.') |
463 | +FOLDER_NESTED_PATH = _('The chosen directory "%(folder_path)s" is not valid. ' |
464 | + '\n\n' |
465 | + 'Please choose a folder outside the existing cloud ' |
466 | + 'folder "%(existent_folder_path)s".') |
467 | FOLDERS_CONFIRM_MERGE = _('The contents of your cloud folder will be merged ' |
468 | 'with your local folder "%(folder_path)s" when ' |
469 | 'subscribing.\nDo you want to subscribe to this ' |
470 | |
471 | === modified file 'ubuntuone/controlpanel/gui/qt/devices.py' |
472 | --- ubuntuone/controlpanel/gui/qt/devices.py 2011-06-02 19:55:45 +0000 |
473 | +++ ubuntuone/controlpanel/gui/qt/devices.py 2011-06-21 16:31:15 +0000 |
474 | @@ -18,20 +18,29 @@ |
475 | |
476 | """The user interface for the control panel for Ubuntu One.""" |
477 | |
478 | -from PyQt4 import QtGui |
479 | - |
480 | +from gettext import gettext as _ |
481 | + |
482 | +# Unused import QtGui |
483 | +# pylint: disable=W0611 |
484 | +from PyQt4 import QtGui, QtCore |
485 | +# pylint: enable=W0611 |
486 | + |
487 | +from ubuntuone.controlpanel.gui import EDIT_DEVICES_LINK |
488 | +from ubuntuone.controlpanel.gui.qt import uri_hook |
489 | from ubuntuone.controlpanel.gui.qt.ui import devices_ui |
490 | +from ubuntuone.controlpanel.gui.qt.ubuntuonebin import UbuntuOneBin |
491 | from ubuntuone.controlpanel.gui.qt.device import DeviceWidget |
492 | |
493 | |
494 | -class DevicesPanel(QtGui.QWidget): |
495 | +class DevicesPanel(UbuntuOneBin): |
496 | """The DevicesFolders Tab Panel widget""" |
497 | |
498 | - def __init__(self, *args): |
499 | - """Initialize the UI of the widget.""" |
500 | - QtGui.QWidget.__init__(self, *args) |
501 | - self.ui = devices_ui.Ui_Form() |
502 | - self.ui.setupUi(self) |
503 | + title = _('All cloud-connected devices') |
504 | + ui_class = devices_ui |
505 | + |
506 | + def load(self): |
507 | + """Load info.""" |
508 | + # request devices info |
509 | |
510 | def update_local_device(self, device_info): |
511 | """Update the info for the local device.""" |
512 | @@ -54,3 +63,8 @@ |
513 | """Update the list of devices in this panel.""" |
514 | for device_info in devices_info: |
515 | self.update_device_info(device_info) |
516 | + |
517 | + @QtCore.pyqtSlot() |
518 | + def on_manage_devices_button_clicked(self): |
519 | + """The storage plan modify button was clicked.""" |
520 | + uri_hook(EDIT_DEVICES_LINK) |
521 | |
522 | === modified file 'ubuntuone/controlpanel/gui/qt/folders.py' |
523 | --- ubuntuone/controlpanel/gui/qt/folders.py 2011-06-17 21:11:45 +0000 |
524 | +++ ubuntuone/controlpanel/gui/qt/folders.py 2011-06-21 16:31:15 +0000 |
525 | @@ -25,7 +25,6 @@ |
526 | from PyQt4 import QtGui, QtCore |
527 | from twisted.internet import defer |
528 | |
529 | -from ubuntuone.controlpanel import backend |
530 | from ubuntuone.controlpanel.logger import setup_logging, log_call |
531 | from ubuntuone.controlpanel.gui import ( |
532 | ALWAYS_SUBSCRIBED, |
533 | @@ -34,6 +33,7 @@ |
534 | FILE_URI_PREFIX, |
535 | FOLDER_ICON_NAME, |
536 | FOLDER_INVALID_PATH, |
537 | + FOLDER_NESTED_PATH, |
538 | FOLDERS_CONFIRM_MERGE, |
539 | KILOBYTES, |
540 | MANAGE_FILES_LINK, |
541 | @@ -47,6 +47,7 @@ |
542 | SHARES_MIN_SIZE_FULL, |
543 | ) |
544 | from ubuntuone.controlpanel.gui.qt import uri_hook |
545 | +from ubuntuone.controlpanel.gui.qt.ubuntuonebin import UbuntuOneBin |
546 | from ubuntuone.controlpanel.gui.qt.ui import folders_ui |
547 | |
548 | |
549 | @@ -68,17 +69,22 @@ |
550 | YES = QtGui.QMessageBox.Yes |
551 | |
552 | |
553 | -class FoldersPanel(QtGui.QWidget): |
554 | +def append_path_sep(path): |
555 | + """If 'path' does not end with the path separator, append it.""" |
556 | + if not path.endswith(os.path.sep): |
557 | + path += os.path.sep |
558 | + return path |
559 | + |
560 | + |
561 | +class FoldersPanel(UbuntuOneBin): |
562 | """The Folders Tab Panel widget""" |
563 | |
564 | - def __init__(self, *args): |
565 | + ui_class = folders_ui |
566 | + |
567 | + def __init__(self, *args, **kwargs): |
568 | """Initialize the UI of the widget.""" |
569 | - QtGui.QWidget.__init__(self, *args) |
570 | - self.ui = folders_ui.Ui_Form() |
571 | - self.ui.setupUi(self) |
572 | - |
573 | - self.is_processing = False |
574 | - self.backend = backend.ControlBackend() |
575 | + super(FoldersPanel, self).__init__(*args, **kwargs) |
576 | + self._backend_folders = None |
577 | logger.debug('%s: started.', self.__class__.__name__) |
578 | |
579 | # Invalid name "showEvent" |
580 | @@ -89,9 +95,7 @@ |
581 | headers = self.ui.folders.header() |
582 | headers.setResizeMode(FOLDER_NAME_COL, headers.Stretch) |
583 | headers.setResizeMode(SUBSCRIPTION_COL, headers.ResizeToContents) |
584 | - |
585 | - self.load() |
586 | - event.accept() |
587 | + super(FoldersPanel, self).showEvent(event) |
588 | |
589 | @defer.inlineCallbacks |
590 | def load(self): |
591 | @@ -127,6 +131,7 @@ |
592 | def process_folders(self, info): |
593 | """Load folders info into the tree view.""" |
594 | self.ui.folders.clear() |
595 | + self._backend_folders = [] |
596 | self.is_processing = False |
597 | |
598 | if not info: |
599 | @@ -170,6 +175,7 @@ |
600 | child = QtGui.QTreeWidgetItem() |
601 | child.volume_path = volume['path'] |
602 | child.volume_id = volume['volume_id'] |
603 | + self._backend_folders.append(append_path_sep(volume['path'])) |
604 | |
605 | name = self._process_name(volume[u'display_name']) |
606 | icon_name = FOLDER_ICON_NAME |
607 | @@ -267,19 +273,31 @@ |
608 | @defer.inlineCallbacks |
609 | def on_add_folder_button_clicked(self): |
610 | """The 'Sync another folder' button was clicked.""" |
611 | - user_home = os.path.expanduser('~') |
612 | + user_home = append_path_sep(os.path.expanduser('~')) |
613 | folder = QtGui.QFileDialog.getExistingDirectory(parent=self, |
614 | directory=user_home) |
615 | + folder = unicode(folder) |
616 | logger.debug('on_add_folder_button_clicked: user requested folder ' |
617 | 'creation for path %r', folder) |
618 | if folder == '': |
619 | return |
620 | |
621 | + folder = append_path_sep(folder) |
622 | + |
623 | # handle folder path not within '~' |
624 | - if not folder.startsWith(user_home): # folder is a QString |
625 | + if not folder.startswith(user_home): |
626 | text = FOLDER_INVALID_PATH % {'folder_path': folder, |
627 | 'home_folder': user_home} |
628 | QtGui.QMessageBox.warning(self, '', text, CLOSE) |
629 | + return |
630 | + |
631 | + # handle folder path inside an existent cloud folder |
632 | + inside_udf = filter(folder.startswith, self._backend_folders) |
633 | + if inside_udf: |
634 | + text = FOLDER_NESTED_PATH % {'folder_path': folder, |
635 | + 'existent_folder_path': inside_udf[0]} |
636 | + QtGui.QMessageBox.warning(self, '', text, CLOSE) |
637 | + return |
638 | |
639 | self.is_processing = True |
640 | yield self.backend.create_folder(folder_path=folder) |
641 | |
642 | === modified file 'ubuntuone/controlpanel/gui/qt/main/linux.py' |
643 | --- ubuntuone/controlpanel/gui/qt/main/linux.py 2011-06-20 01:46:29 +0000 |
644 | +++ ubuntuone/controlpanel/gui/qt/main/linux.py 2011-06-21 16:31:15 +0000 |
645 | @@ -149,7 +149,7 @@ |
646 | subprocess.Popen(('u1sdtool', '--unsubscribe-folder', folder_id)) |
647 | |
648 | from ubuntuone.platform.linux import tools |
649 | - tools.SyncDaemonTool = SDTool |
650 | + #tools.SyncDaemonTool = SDTool |
651 | |
652 | # pylint believes that reactor has no run nor stop methods. Silence it. |
653 | # pylint: disable=E1101 |
654 | |
655 | === modified file 'ubuntuone/controlpanel/gui/qt/preferences.py' |
656 | --- ubuntuone/controlpanel/gui/qt/preferences.py 2011-06-17 20:08:02 +0000 |
657 | +++ ubuntuone/controlpanel/gui/qt/preferences.py 2011-06-21 16:31:15 +0000 |
658 | @@ -18,14 +18,19 @@ |
659 | |
660 | """The user interface for the control panel for Ubuntu One.""" |
661 | |
662 | +# Unused import QtGui |
663 | +# pylint: disable=W0611 |
664 | from PyQt4 import QtGui, QtCore |
665 | +# pylint: enable=W0611 |
666 | from twisted.internet import defer |
667 | |
668 | from ubuntuone.controlpanel import backend |
669 | from ubuntuone.controlpanel.logger import setup_logging, log_call |
670 | from ubuntuone.controlpanel.gui import KILOBYTES |
671 | +from ubuntuone.controlpanel.gui.qt.ubuntuonebin import UbuntuOneBin |
672 | from ubuntuone.controlpanel.gui.qt.ui import preferences_ui |
673 | |
674 | + |
675 | logger = setup_logging('qt.preferences') |
676 | |
677 | CHECKED = QtCore.Qt.Checked |
678 | @@ -37,25 +42,10 @@ |
679 | return CHECKED if checked else UNCHECKED |
680 | |
681 | |
682 | -class PreferencesPanel(QtGui.QWidget): |
683 | +class PreferencesPanel(UbuntuOneBin): |
684 | """The Preferences Tab Panel widget""" |
685 | |
686 | - def __init__(self, *args): |
687 | - """Initialize the UI of the widget.""" |
688 | - QtGui.QWidget.__init__(self, *args) |
689 | - self.ui = preferences_ui.Ui_Form() |
690 | - self.ui.setupUi(self) |
691 | - |
692 | - self.backend = backend.ControlBackend() |
693 | - logger.debug('%s: started.', self.__class__.__name__) |
694 | - |
695 | - # Invalid name "showEvent" |
696 | - # pylint: disable=C0103 |
697 | - |
698 | - def showEvent(self, event): |
699 | - """Load info.""" |
700 | - self.load() |
701 | - event.accept() |
702 | + ui_class = preferences_ui |
703 | |
704 | @defer.inlineCallbacks |
705 | def load(self): |
706 | @@ -93,6 +83,9 @@ |
707 | else: |
708 | checkbox.setCheckState(CHECKED) |
709 | |
710 | + # Invalid name "on_{down, up}load_speed_spinbox_valueChanged" |
711 | + # pylint: disable=C0103 |
712 | + |
713 | @QtCore.pyqtSlot(int) |
714 | def on_download_speed_spinbox_valueChanged(self, new_value): |
715 | """A new download speed was set.""" |
716 | |
717 | === modified file 'ubuntuone/controlpanel/gui/qt/profile.py' |
718 | --- ubuntuone/controlpanel/gui/qt/profile.py 2011-06-16 19:49:43 +0000 |
719 | +++ ubuntuone/controlpanel/gui/qt/profile.py 2011-06-21 16:31:15 +0000 |
720 | @@ -18,21 +18,25 @@ |
721 | |
722 | """The user interface for the control panel for Ubuntu One.""" |
723 | |
724 | +# Unused import QtGui |
725 | +# pylint: disable=W0611 |
726 | from PyQt4 import QtGui, QtCore |
727 | +# pylint: enable=W0611 |
728 | |
729 | from ubuntuone.controlpanel.gui import EDIT_PROFILE_LINK |
730 | from ubuntuone.controlpanel.gui.qt import uri_hook |
731 | +from ubuntuone.controlpanel.gui.qt.ubuntuonebin import UbuntuOneBin |
732 | from ubuntuone.controlpanel.gui.qt.ui import profile_ui |
733 | |
734 | |
735 | -class ProfilePanel(QtGui.QWidget): |
736 | +class ProfilePanel(UbuntuOneBin): |
737 | """The Profile Tab Panel widget""" |
738 | |
739 | - def __init__(self, *args): |
740 | - """Initialize the UI of the widget.""" |
741 | - QtGui.QWidget.__init__(self, *args) |
742 | - self.ui = profile_ui.Ui_Form() |
743 | - self.ui.setupUi(self) |
744 | + ui_class = profile_ui |
745 | + |
746 | + def load(self): |
747 | + """Load info.""" |
748 | + # request account info |
749 | |
750 | def update_account_info(self, account_info): |
751 | """Update the widgets with the account info.""" |
752 | |
753 | === modified file 'ubuntuone/controlpanel/gui/qt/services.py' |
754 | --- ubuntuone/controlpanel/gui/qt/services.py 2011-06-16 19:49:43 +0000 |
755 | +++ ubuntuone/controlpanel/gui/qt/services.py 2011-06-21 16:31:15 +0000 |
756 | @@ -18,21 +18,28 @@ |
757 | |
758 | """The user interface for the control panel for Ubuntu One.""" |
759 | |
760 | +from gettext import gettext as _ |
761 | + |
762 | +# Unused import QtGui |
763 | +# pylint: disable=W0611 |
764 | from PyQt4 import QtGui, QtCore |
765 | +# pylint: enable=W0611 |
766 | |
767 | from ubuntuone.controlpanel.gui import EDIT_ACCOUNT_LINK |
768 | from ubuntuone.controlpanel.gui.qt import uri_hook |
769 | +from ubuntuone.controlpanel.gui.qt.ubuntuonebin import UbuntuOneBin |
770 | from ubuntuone.controlpanel.gui.qt.ui import services_ui |
771 | |
772 | |
773 | -class ServicesPanel(QtGui.QWidget): |
774 | +class ServicesPanel(UbuntuOneBin): |
775 | """The ServicesFolders Tab Panel widget""" |
776 | |
777 | - def __init__(self, *args): |
778 | - """Initialize the UI of the widget.""" |
779 | - QtGui.QWidget.__init__(self, *args) |
780 | - self.ui = services_ui.Ui_Form() |
781 | - self.ui.setupUi(self) |
782 | + title = _('Selected plans and storage') |
783 | + ui_class = services_ui |
784 | + |
785 | + def load(self): |
786 | + """Load info.""" |
787 | + # request account info |
788 | |
789 | def update_account_info(self, account_info): |
790 | """Update the widgets with the account info.""" |
791 | |
792 | === modified file 'ubuntuone/controlpanel/gui/qt/tests/__init__.py' |
793 | --- ubuntuone/controlpanel/gui/qt/tests/__init__.py 2011-06-17 20:08:02 +0000 |
794 | +++ ubuntuone/controlpanel/gui/qt/tests/__init__.py 2011-06-21 16:31:15 +0000 |
795 | @@ -67,9 +67,7 @@ |
796 | |
797 | def inner(instance): |
798 | """Skip a test if is an abstract class.""" |
799 | - abstract = any(i is None for i in (instance.innerclass_ui, |
800 | - instance.innerclass_name, |
801 | - instance.class_ui)) |
802 | + abstract = instance.class_ui is None |
803 | result = None |
804 | if not abstract: |
805 | result = test(instance) |
806 | @@ -172,9 +170,10 @@ |
807 | self.assertIn(method_name, self.ui.backend._called) |
808 | self.assertEqual(self.ui.backend._called[method_name], (args, kwargs)) |
809 | |
810 | - @skip_if_abstract_class |
811 | def test_init_loads_ui(self): |
812 | """The __init__ method loads the ui.""" |
813 | + if self.innerclass_ui is None: |
814 | + return |
815 | self.patch(self.innerclass_ui, self.innerclass_name, FakeUi) |
816 | # pylint: disable=E1102 |
817 | self.ui = self.class_ui(**self.kwargs) |
818 | |
819 | === modified file 'ubuntuone/controlpanel/gui/qt/tests/test_devices.py' |
820 | --- ubuntuone/controlpanel/gui/qt/tests/test_devices.py 2011-06-02 19:55:45 +0000 |
821 | +++ ubuntuone/controlpanel/gui/qt/tests/test_devices.py 2011-06-21 16:31:15 +0000 |
822 | @@ -19,17 +19,31 @@ |
823 | """Tests for the devices tab.""" |
824 | |
825 | from ubuntuone.controlpanel.gui.qt import devices as gui |
826 | -from ubuntuone.controlpanel.gui.qt.tests import (BaseTestCase, |
827 | - SAMPLE_DEVICES_INFO) |
828 | - |
829 | - |
830 | -class DevicesPanelTestCase(BaseTestCase): |
831 | +from ubuntuone.controlpanel.gui.qt.tests import ( |
832 | + SAMPLE_DEVICES_INFO, |
833 | +) |
834 | +from ubuntuone.controlpanel.gui.qt.tests.test_ubuntuonebin import ( |
835 | + UbuntuOneBinTestCase, |
836 | +) |
837 | + |
838 | + |
839 | +class DevicesPanelTestCase(UbuntuOneBinTestCase): |
840 | """Test the qt control panel.""" |
841 | |
842 | innerclass_ui = gui.devices_ui |
843 | innerclass_name = "Ui_Form" |
844 | class_ui = gui.DevicesPanel |
845 | |
846 | + def test_is_processing_while_asking_info(self): |
847 | + """The ui is processing while the contents are loaded.""" |
848 | + def check(): |
849 | + """The ui must be is_processing.""" |
850 | + self.assertTrue(self.ui.is_processing, 'ui must be processing') |
851 | + |
852 | + self.patch(self.ui.backend, 'devices_info', check) |
853 | + |
854 | + return self.ui.load() # trigger the info request |
855 | + |
856 | def test_update_devices_info(self): |
857 | """The widget is updated with the info.""" |
858 | container = self.ui.ui.devices_list_verticallayout |
859 | @@ -38,3 +52,10 @@ |
860 | expected_name = SAMPLE_DEVICES_INFO[0]["name"] |
861 | self.assertEqual(self.ui.ui.device_name_label.text(), expected_name) |
862 | self.assertEqual(container.count(), len(SAMPLE_DEVICES_INFO) - 1) |
863 | + |
864 | + def test_manage_devices_button(self): |
865 | + """Clicking the manage devices button opens the proper url.""" |
866 | + self.patch(gui, 'uri_hook', self._set_called) |
867 | + self.ui.ui.manage_devices_button.click() |
868 | + |
869 | + self.assertEqual(self._called, ((gui.EDIT_DEVICES_LINK,), {})) |
870 | |
871 | === modified file 'ubuntuone/controlpanel/gui/qt/tests/test_folders.py' |
872 | --- ubuntuone/controlpanel/gui/qt/tests/test_folders.py 2011-06-17 21:11:45 +0000 |
873 | +++ ubuntuone/controlpanel/gui/qt/tests/test_folders.py 2011-06-21 16:31:15 +0000 |
874 | @@ -25,59 +25,63 @@ |
875 | from twisted.internet import defer |
876 | from ubuntuone.devtools.handlers import MementoHandler |
877 | |
878 | -from ubuntuone.controlpanel.gui.qt import folders as gui |
879 | -from ubuntuone.controlpanel.gui.qt.tests import ( |
880 | - BaseTestCase, FakedConfirmDialog, FakedFileDialog, |
881 | -) |
882 | from ubuntuone.controlpanel.gui.tests import ( |
883 | FAKE_VOLUMES_INFO, |
884 | FAKE_VOLUMES_MINIMAL_INFO, |
885 | FAKE_VOLUMES_NO_FREE_SPACE_INFO, |
886 | MUSIC_FOLDER, ROOT, USER_HOME, |
887 | ) |
888 | +from ubuntuone.controlpanel.gui.qt import folders as gui |
889 | +from ubuntuone.controlpanel.gui.qt.tests import ( |
890 | + FakedConfirmDialog, FakedFileDialog, |
891 | +) |
892 | +from ubuntuone.controlpanel.gui.qt.tests.test_ubuntuonebin import ( |
893 | + UbuntuOneBinTestCase, |
894 | +) |
895 | |
896 | |
897 | # Access to a protected member |
898 | -# pylint: disable=W0212 |
899 | - |
900 | - |
901 | -class FoldersPanelTestCase(BaseTestCase): |
902 | +# Instance of 'ControlBackend' has no '_called' member |
903 | +# pylint: disable=W0212, E1103 |
904 | + |
905 | + |
906 | +class FoldersPanelTestCase(UbuntuOneBinTestCase): |
907 | """Test the qt cloud folders tab.""" |
908 | |
909 | innerclass_ui = gui.folders_ui |
910 | innerclass_name = "Ui_Form" |
911 | class_ui = gui.FoldersPanel |
912 | |
913 | + @defer.inlineCallbacks |
914 | def setUp(self): |
915 | - super(FoldersPanelTestCase, self).setUp() |
916 | + yield super(FoldersPanelTestCase, self).setUp() |
917 | self.memento = MementoHandler() |
918 | self.memento.setLevel(logging.DEBUG) |
919 | gui.logger.addHandler(self.memento) |
920 | |
921 | - def test_backend(self): |
922 | - """Backend is created.""" |
923 | - self.assertIsInstance(self.ui.backend, |
924 | - gui.backend.ControlBackend) |
925 | + old_home = os.environ['HOME'] |
926 | + os.environ['HOME'] = USER_HOME |
927 | + self.addCleanup(lambda: os.environ.__setitem__('HOME', old_home)) |
928 | |
929 | |
930 | class FoldersPanelVolumesInfoTestCase(FoldersPanelTestCase): |
931 | """Test the qt cloud folders tab.""" |
932 | |
933 | + @defer.inlineCallbacks |
934 | def setUp(self): |
935 | - super(FoldersPanelVolumesInfoTestCase, self).setUp() |
936 | - return self.ui.load() |
937 | + yield super(FoldersPanelVolumesInfoTestCase, self).setUp() |
938 | + yield self.ui.load() |
939 | |
940 | @defer.inlineCallbacks |
941 | - def test_is_processing_before_asking_volume_info(self): |
942 | - """The ui is processing when contents are load.""" |
943 | + def test_is_processing_while_asking_info(self): |
944 | + """The ui is processing while the contents are loaded.""" |
945 | def check(): |
946 | """The ui must be is_processing.""" |
947 | - # the ui is processing |
948 | self.assertTrue(self.ui.is_processing, 'ui must be processing') |
949 | |
950 | self.patch(self.ui.backend, 'volumes_info', check) |
951 | |
952 | - yield self.ui.load() # trigger the volumes_info request |
953 | + yield self.ui.load() # trigger the info request |
954 | |
955 | def test_is_not_processing_after_info_ready(self): |
956 | """The ui is not processing when contents are load.""" |
957 | @@ -312,11 +316,12 @@ |
958 | self.assertEqual(self._called, ((gui.MANAGE_FILES_LINK,), {})) |
959 | |
960 | |
961 | -class FoldersPanelFolderCreateTestCase(FoldersPanelTestCase): |
962 | +class FoldersPanelAddFolderTestCase(FoldersPanelTestCase): |
963 | """The test suite for the folder creation from a local dir.""" |
964 | |
965 | + @defer.inlineCallbacks |
966 | def setUp(self): |
967 | - super(FoldersPanelFolderCreateTestCase, self).setUp() |
968 | + yield super(FoldersPanelAddFolderTestCase, self).setUp() |
969 | # simulate volumes info properly processed |
970 | self.ui.process_folders([]) |
971 | # reset backend state |
972 | @@ -343,9 +348,9 @@ |
973 | yield self.ui.ui.add_folder_button.click() |
974 | |
975 | self.assertEqual(FakedFileDialog.args, ()) |
976 | + user_home = gui.append_path_sep(os.path.expanduser('~')) |
977 | self.assertEqual(FakedFileDialog.kwargs, |
978 | - {'parent': self.ui, |
979 | - 'directory': os.path.expanduser('~')}) |
980 | + {'parent': self.ui, 'directory': user_home}) |
981 | |
982 | @defer.inlineCallbacks |
983 | def test_does_nothing_if_user_closes_file_chooser(self): |
984 | @@ -367,11 +372,90 @@ |
985 | FakedFileDialog.response = gui.QtCore.QString(outside_home) |
986 | yield self.ui.ui.add_folder_button.click() |
987 | |
988 | - msg = gui.FOLDER_INVALID_PATH % {'folder_path': outside_home, |
989 | - 'home_folder': user_home} |
990 | - self.assertEqual(FakedConfirmDialog.args, |
991 | - (self.ui, '', msg, gui.CLOSE)) |
992 | - self.assertEqual(FakedConfirmDialog.kwargs, {}) |
993 | + args = {'folder_path': gui.append_path_sep(outside_home), |
994 | + 'home_folder': gui.append_path_sep(user_home)} |
995 | + msg = gui.FOLDER_INVALID_PATH % args |
996 | + self.assertEqual(FakedConfirmDialog.args, |
997 | + (self.ui, '', msg, gui.CLOSE)) |
998 | + self.assertEqual(FakedConfirmDialog.kwargs, {}) |
999 | + # the backend was not called |
1000 | + self.assertEqual(self.ui.backend._called, {}) |
1001 | + |
1002 | + @defer.inlineCallbacks |
1003 | + def test_opens_warning_if_folder_inside_root(self): |
1004 | + """If the user chooses a folder inside the root, show a warning.""" |
1005 | + self.ui.process_folders(FAKE_VOLUMES_MINIMAL_INFO) |
1006 | + root_path = ROOT['path'] |
1007 | + # create a valid path inside the root |
1008 | + inside_root = os.path.abspath(os.path.join(root_path, 'test')) |
1009 | + FakedFileDialog.response = gui.QtCore.QString(inside_root) |
1010 | + yield self.ui.ui.add_folder_button.click() |
1011 | + |
1012 | + args = {'folder_path': gui.append_path_sep(inside_root), |
1013 | + 'existent_folder_path': gui.append_path_sep(root_path)} |
1014 | + msg = gui.FOLDER_NESTED_PATH % args |
1015 | + self.assertEqual(FakedConfirmDialog.args, |
1016 | + (self.ui, '', msg, gui.CLOSE)) |
1017 | + self.assertEqual(FakedConfirmDialog.kwargs, {}) |
1018 | + # the backend was not called |
1019 | + self.assertEqual(self.ui.backend._called, {}) |
1020 | + |
1021 | + @defer.inlineCallbacks |
1022 | + def test_opens_warning_if_folder_inside_an_udf(self): |
1023 | + """If the user chooses a folder inside an UDF, show a warning.""" |
1024 | + self.ui.process_folders(FAKE_VOLUMES_MINIMAL_INFO) |
1025 | + udf_path = MUSIC_FOLDER['path'] |
1026 | + # create a valid path inside an existing UDF |
1027 | + inside_udf = os.path.abspath(os.path.join(udf_path, 'test')) |
1028 | + FakedFileDialog.response = gui.QtCore.QString(inside_udf) |
1029 | + yield self.ui.ui.add_folder_button.click() |
1030 | + |
1031 | + args = {'folder_path': gui.append_path_sep(inside_udf), |
1032 | + 'existent_folder_path': gui.append_path_sep(udf_path)} |
1033 | + msg = gui.FOLDER_NESTED_PATH % args |
1034 | + self.assertEqual(FakedConfirmDialog.args, |
1035 | + (self.ui, '', msg, gui.CLOSE)) |
1036 | + self.assertEqual(FakedConfirmDialog.kwargs, {}) |
1037 | + # the backend was not called |
1038 | + self.assertEqual(self.ui.backend._called, {}) |
1039 | + |
1040 | + @defer.inlineCallbacks |
1041 | + def test_no_warning_if_folder_is_prefix_from_a_udf(self): |
1042 | + """Test proper displaying of warning messages. |
1043 | + |
1044 | + If the user chooses a folder with the same prefix as an UDF, but |
1045 | + outside every UDF, do not show a warning and call backend. |
1046 | + |
1047 | + """ |
1048 | + self.ui.process_folders(FAKE_VOLUMES_MINIMAL_INFO) |
1049 | + tricky_path = ROOT['path'] |
1050 | + assert not tricky_path.endswith(os.path.sep) |
1051 | + tricky_path += ' Suffix' |
1052 | + assert tricky_path.startswith(ROOT['path']) |
1053 | + |
1054 | + FakedFileDialog.response = gui.QtCore.QString(tricky_path) |
1055 | + yield self.ui.ui.add_folder_button.click() |
1056 | + |
1057 | + # no warning |
1058 | + self.assertEqual(FakedConfirmDialog.args, None) |
1059 | + self.assertEqual(FakedConfirmDialog.kwargs, None) |
1060 | + # backend called |
1061 | + tricky_path = gui.append_path_sep(tricky_path) |
1062 | + self.assert_backend_called('create_folder', folder_path=tricky_path) |
1063 | + |
1064 | + @defer.inlineCallbacks |
1065 | + def test_handles_unicode_paths(self): |
1066 | + """Unicode paths are properly handled.""" |
1067 | + folder = os.path.join(os.path.expanduser('~'), u'ñoño ñandú ❤') |
1068 | + FakedFileDialog.response = gui.QtCore.QString(folder) |
1069 | + yield self.ui.ui.add_folder_button.click() |
1070 | + |
1071 | + # no warning |
1072 | + self.assertEqual(FakedConfirmDialog.args, None) |
1073 | + self.assertEqual(FakedConfirmDialog.kwargs, None) |
1074 | + # backend called |
1075 | + folder = gui.append_path_sep(folder) |
1076 | + self.assert_backend_called('create_folder', folder_path=folder) |
1077 | |
1078 | @defer.inlineCallbacks |
1079 | def test_calls_backend(self): |
1080 | @@ -380,6 +464,11 @@ |
1081 | FakedFileDialog.response = gui.QtCore.QString(folder) |
1082 | yield self.ui.ui.add_folder_button.click() |
1083 | |
1084 | + # no warning |
1085 | + self.assertEqual(FakedConfirmDialog.args, None) |
1086 | + self.assertEqual(FakedConfirmDialog.kwargs, None) |
1087 | + # backend called |
1088 | + folder = gui.append_path_sep(folder) |
1089 | self.assert_backend_called('create_folder', folder_path=folder) |
1090 | |
1091 | @defer.inlineCallbacks |
1092 | @@ -398,7 +487,7 @@ |
1093 | self.assertFalse(self.ui.is_processing, 'ui must not be processing') |
1094 | |
1095 | @defer.inlineCallbacks |
1096 | - def test_reload_volumes_info(self): |
1097 | + def test_reload_volumes_info_on_success(self): |
1098 | """Once the folder is created, the volumes info is reloaded.""" |
1099 | self.patch(self.ui, 'load', self._set_called) |
1100 | |
1101 | @@ -412,8 +501,9 @@ |
1102 | class FoldersPanelSubscriptionTestCase(FoldersPanelTestCase): |
1103 | """The test suite for the folder subscription.""" |
1104 | |
1105 | + @defer.inlineCallbacks |
1106 | def setUp(self): |
1107 | - super(FoldersPanelSubscriptionTestCase, self).setUp() |
1108 | + yield super(FoldersPanelSubscriptionTestCase, self).setUp() |
1109 | self.patch(gui.os.path, 'exists', lambda path: True) |
1110 | FakedConfirmDialog.response = gui.YES |
1111 | FakedConfirmDialog.args = None |
1112 | @@ -450,7 +540,6 @@ |
1113 | """Clicking on 'subscribed' sets is_processing flag until done.""" |
1114 | def check(volume_id, settings): |
1115 | """The ui must be is_processing when a change was requested.""" |
1116 | - # the ui is processing |
1117 | self.assertTrue(self.ui.is_processing, 'ui must be processing') |
1118 | |
1119 | self.patch(self.ui.backend, 'change_volume_settings', check) |
1120 | |
1121 | === modified file 'ubuntuone/controlpanel/gui/qt/tests/test_preferences.py' |
1122 | --- ubuntuone/controlpanel/gui/qt/tests/test_preferences.py 2011-06-17 20:08:02 +0000 |
1123 | +++ ubuntuone/controlpanel/gui/qt/tests/test_preferences.py 2011-06-21 16:31:15 +0000 |
1124 | @@ -20,28 +20,38 @@ |
1125 | |
1126 | from __future__ import division |
1127 | |
1128 | +from twisted.internet import defer |
1129 | + |
1130 | from ubuntuone.controlpanel.gui.qt import preferences as gui |
1131 | -from ubuntuone.controlpanel.gui.qt.tests import BaseTestCase |
1132 | +from ubuntuone.controlpanel.gui.qt.tests.test_ubuntuonebin import ( |
1133 | + UbuntuOneBinTestCase, |
1134 | +) |
1135 | |
1136 | # Access to a protected member |
1137 | # pylint: disable=W0212 |
1138 | |
1139 | |
1140 | -class PreferencesPanelTestCase(BaseTestCase): |
1141 | +class PreferencesPanelTestCase(UbuntuOneBinTestCase): |
1142 | """Test the qt cloud preferences tab.""" |
1143 | |
1144 | innerclass_ui = gui.preferences_ui |
1145 | innerclass_name = "Ui_Form" |
1146 | class_ui = gui.PreferencesPanel |
1147 | |
1148 | + @defer.inlineCallbacks |
1149 | def setUp(self): |
1150 | - super(PreferencesPanelTestCase, self).setUp() |
1151 | - return self.ui.load() |
1152 | - |
1153 | - def test_backend(self): |
1154 | - """Backend is created.""" |
1155 | - self.assertIsInstance(self.ui.backend, |
1156 | - gui.backend.ControlBackend) |
1157 | + yield super(PreferencesPanelTestCase, self).setUp() |
1158 | + yield self.ui.load() |
1159 | + |
1160 | + def test_is_processing_while_asking_info(self): |
1161 | + """The ui is processing while the contents are loaded.""" |
1162 | + def check(): |
1163 | + """The ui must be is_processing.""" |
1164 | + self.assertTrue(self.ui.is_processing, 'ui must be processing') |
1165 | + |
1166 | + self.patch(self.ui.backend, 'file_sync_settings_info', check) |
1167 | + |
1168 | + yield self.ui.load() # trigger the info request |
1169 | |
1170 | def test_file_sync_settings_info_is_requested(self): |
1171 | """The file_sync_settings info is requested to the backend.""" |
1172 | |
1173 | === modified file 'ubuntuone/controlpanel/gui/qt/tests/test_profile.py' |
1174 | --- ubuntuone/controlpanel/gui/qt/tests/test_profile.py 2011-06-16 20:33:39 +0000 |
1175 | +++ ubuntuone/controlpanel/gui/qt/tests/test_profile.py 2011-06-21 16:31:15 +0000 |
1176 | @@ -19,17 +19,31 @@ |
1177 | """Tests for the profile tab.""" |
1178 | |
1179 | from ubuntuone.controlpanel.gui.qt import profile as gui |
1180 | -from ubuntuone.controlpanel.gui.qt.tests import (BaseTestCase, |
1181 | - SAMPLE_ACCOUNT_INFO, SAMPLE_EMAIL, SAMPLE_NAME) |
1182 | - |
1183 | - |
1184 | -class ProfilePanelTestCase(BaseTestCase): |
1185 | +from ubuntuone.controlpanel.gui.qt.tests import ( |
1186 | + SAMPLE_ACCOUNT_INFO, SAMPLE_EMAIL, SAMPLE_NAME, |
1187 | +) |
1188 | +from ubuntuone.controlpanel.gui.qt.tests.test_ubuntuonebin import ( |
1189 | + UbuntuOneBinTestCase, |
1190 | +) |
1191 | + |
1192 | + |
1193 | +class ProfilePanelTestCase(UbuntuOneBinTestCase): |
1194 | """Test the qt control panel.""" |
1195 | |
1196 | innerclass_ui = gui.profile_ui |
1197 | innerclass_name = "Ui_Form" |
1198 | class_ui = gui.ProfilePanel |
1199 | |
1200 | + def test_is_processing_while_asking_info(self): |
1201 | + """The ui is processing while the contents are loaded.""" |
1202 | + def check(): |
1203 | + """The ui must be is_processing.""" |
1204 | + self.assertTrue(self.ui.is_processing, 'ui must be processing') |
1205 | + |
1206 | + self.patch(self.ui.backend, 'account_info', check) |
1207 | + |
1208 | + return self.ui.load() # trigger the info request |
1209 | + |
1210 | def test_update_account_info(self): |
1211 | """Backend's file sync status changed callback is connected.""" |
1212 | self.ui.update_account_info(SAMPLE_ACCOUNT_INFO) |
1213 | |
1214 | === modified file 'ubuntuone/controlpanel/gui/qt/tests/test_services.py' |
1215 | --- ubuntuone/controlpanel/gui/qt/tests/test_services.py 2011-06-16 20:33:39 +0000 |
1216 | +++ ubuntuone/controlpanel/gui/qt/tests/test_services.py 2011-06-21 16:31:15 +0000 |
1217 | @@ -19,17 +19,31 @@ |
1218 | """Tests for the services tab.""" |
1219 | |
1220 | from ubuntuone.controlpanel.gui.qt import services as gui |
1221 | -from ubuntuone.controlpanel.gui.qt.tests import (BaseTestCase, |
1222 | - SAMPLE_ACCOUNT_INFO, SAMPLE_PLAN) |
1223 | - |
1224 | - |
1225 | -class ServicesPanelTestCase(BaseTestCase): |
1226 | +from ubuntuone.controlpanel.gui.qt.tests import ( |
1227 | + SAMPLE_ACCOUNT_INFO, SAMPLE_PLAN, |
1228 | +) |
1229 | +from ubuntuone.controlpanel.gui.qt.tests.test_ubuntuonebin import ( |
1230 | + UbuntuOneBinTestCase, |
1231 | +) |
1232 | + |
1233 | + |
1234 | +class ServicesPanelTestCase(UbuntuOneBinTestCase): |
1235 | """Test the qt control panel.""" |
1236 | |
1237 | innerclass_ui = gui.services_ui |
1238 | innerclass_name = "Ui_Form" |
1239 | class_ui = gui.ServicesPanel |
1240 | |
1241 | + def test_is_processing_while_asking_info(self): |
1242 | + """The ui is processing while the contents are loaded.""" |
1243 | + def check(): |
1244 | + """The ui must be is_processing.""" |
1245 | + self.assertTrue(self.ui.is_processing, 'ui must be processing') |
1246 | + |
1247 | + self.patch(self.ui.backend, 'account_info', check) |
1248 | + |
1249 | + return self.ui.load() # trigger the info request |
1250 | + |
1251 | def test_update_account_info(self): |
1252 | """Backend's file sync status changed callback is connected.""" |
1253 | self.ui.update_account_info(SAMPLE_ACCOUNT_INFO) |
1254 | |
1255 | === added file 'ubuntuone/controlpanel/gui/qt/tests/test_ubuntuonebin.py' |
1256 | --- ubuntuone/controlpanel/gui/qt/tests/test_ubuntuonebin.py 1970-01-01 00:00:00 +0000 |
1257 | +++ ubuntuone/controlpanel/gui/qt/tests/test_ubuntuonebin.py 2011-06-21 16:31:15 +0000 |
1258 | @@ -0,0 +1,92 @@ |
1259 | +# -*- coding: utf-8 -*- |
1260 | + |
1261 | +# Authors: Natalia B Bidart <natalia.bidart@canonical.com> |
1262 | +# |
1263 | +# Copyright 2011 Canonical Ltd. |
1264 | +# |
1265 | +# This program is free software: you can redistribute it and/or modify it |
1266 | +# under the terms of the GNU General Public License version 3, as published |
1267 | +# by the Free Software Foundation. |
1268 | +# |
1269 | +# This program is distributed in the hope that it will be useful, but |
1270 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
1271 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1272 | +# PURPOSE. See the GNU General Public License for more details. |
1273 | +# |
1274 | +# You should have received a copy of the GNU General Public License along |
1275 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
1276 | + |
1277 | +"""Tests for the Ubuntu One Bin.""" |
1278 | + |
1279 | +from ubuntuone.controlpanel.gui.qt import ubuntuonebin as gui |
1280 | +from ubuntuone.controlpanel.gui.qt.tests import BaseTestCase, FakeUi |
1281 | + |
1282 | +# Attribute 'yyy' defined outside __init__, access to a protected member |
1283 | +# pylint: disable=W0201, W0212 |
1284 | + |
1285 | + |
1286 | +class UbuntuOneBinTestCase(BaseTestCase): |
1287 | + """Test the qt cloud folders tab.""" |
1288 | + |
1289 | + class_ui = gui.UbuntuOneBin |
1290 | + |
1291 | + def setUp(self): |
1292 | + super(UbuntuOneBinTestCase, self).setUp() |
1293 | + self.ui.show() # need to show to test widgets visibility |
1294 | + |
1295 | + def test_backend(self): |
1296 | + """Backend is created.""" |
1297 | + self.assertIsInstance(self.ui.backend, |
1298 | + gui.backend.ControlBackend) |
1299 | + |
1300 | + def test_init_loads_ui(self): |
1301 | + """The __init__ method loads the ui.""" |
1302 | + if self.innerclass_ui is None: |
1303 | + return |
1304 | + self.patch(self.innerclass_ui, self.innerclass_name, FakeUi) |
1305 | + # pylint: disable=E1102 |
1306 | + self.ui = self.class_ui(**self.kwargs) |
1307 | + # pylint: disable=E1101 |
1308 | + self.assertEqual(self.ui.ui._called, |
1309 | + {'setupUi': ((self.ui.bin_ui.container,), {})}) |
1310 | + |
1311 | + def test_is_not_processing_after_creation(self): |
1312 | + """After creation, is not processing.""" |
1313 | + self.assertFalse(self.ui.is_processing) |
1314 | + |
1315 | + def test_spinner_is_active(self): |
1316 | + """The spinner is active.""" |
1317 | + self.assertEqual(self.ui.bin_ui.spinner.minimum(), 0) |
1318 | + self.assertEqual(self.ui.bin_ui.spinner.maximum(), 0) |
1319 | + |
1320 | + def test_is_enabled_if_not_processing(self): |
1321 | + """If not processing, the UI is enabled.""" |
1322 | + self.ui.is_processing = False |
1323 | + self.assertTrue(self.ui.isEnabled()) |
1324 | + self.assertFalse(self.ui.bin_ui.spinner.isVisible()) |
1325 | + |
1326 | + def test_is_not_enabled_if_processing(self): |
1327 | + """If processing, the UI is disabled.""" |
1328 | + self.ui.is_processing = True |
1329 | + self.assertFalse(self.ui.isEnabled()) |
1330 | + self.assertTrue(self.ui.bin_ui.spinner.isVisible()) |
1331 | + |
1332 | + def test_title_when_none(self): |
1333 | + """The title is the empty string when class title is None.""" |
1334 | + |
1335 | + class MyBin(gui.UbuntuOneBin): |
1336 | + """Inherit to set a class title.""" |
1337 | + title = None |
1338 | + |
1339 | + ui = MyBin() |
1340 | + self.assertEqual(ui.bin_ui.title_label.text(), '') |
1341 | + |
1342 | + def test_title_when_not_none(self): |
1343 | + """The title is the empty string when class title is None.""" |
1344 | + |
1345 | + class MyBin(gui.UbuntuOneBin): |
1346 | + """Inherit to set a class title.""" |
1347 | + title = 'Hello World!' |
1348 | + |
1349 | + ui = MyBin() |
1350 | + self.assertEqual(ui.bin_ui.title_label.text(), MyBin.title) |
1351 | |
1352 | === added file 'ubuntuone/controlpanel/gui/qt/ubuntuonebin.py' |
1353 | --- ubuntuone/controlpanel/gui/qt/ubuntuonebin.py 1970-01-01 00:00:00 +0000 |
1354 | +++ ubuntuone/controlpanel/gui/qt/ubuntuonebin.py 2011-06-21 16:31:15 +0000 |
1355 | @@ -0,0 +1,85 @@ |
1356 | +# -*- coding: utf-8 -*- |
1357 | + |
1358 | +# Authors: Natalia B Bidart <natalia.bidart@canonical.com> |
1359 | +# |
1360 | +# Copyright 2011 Canonical Ltd. |
1361 | +# |
1362 | +# This program is free software: you can redistribute it and/or modify it |
1363 | +# under the terms of the GNU General Public License version 3, as published |
1364 | +# by the Free Software Foundation. |
1365 | +# |
1366 | +# This program is distributed in the hope that it will be useful, but |
1367 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
1368 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1369 | +# PURPOSE. See the GNU General Public License for more details. |
1370 | +# |
1371 | +# You should have received a copy of the GNU General Public License along |
1372 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
1373 | + |
1374 | +"""The base widget for the Control Panel tabs.""" |
1375 | + |
1376 | +from PyQt4 import QtGui, QtCore |
1377 | + |
1378 | +from ubuntuone.controlpanel import backend |
1379 | +from ubuntuone.controlpanel.gui.qt.ui import ubuntuonebin_ui |
1380 | + |
1381 | + |
1382 | +class UbuntuOneBin(QtGui.QWidget): |
1383 | + """The base widget for the Control Panel's tabs.""" |
1384 | + |
1385 | + title = None |
1386 | + ui_class = None |
1387 | + |
1388 | + def __init__(self, parent=None): |
1389 | + """Initialize the UI of the widget.""" |
1390 | + QtGui.QWidget.__init__(self, parent) |
1391 | + |
1392 | + self.bin_ui = ubuntuonebin_ui.Ui_Form() |
1393 | + self.bin_ui.setupUi(self) |
1394 | + |
1395 | + if self.title is None: |
1396 | + self.bin_ui.title_label.setText('') |
1397 | + else: |
1398 | + self.bin_ui.title_label.setText(self.title) |
1399 | + |
1400 | + self.ui = None |
1401 | + if self.ui_class is not None: |
1402 | + # self.ui_class is not callable |
1403 | + # pylint: disable=E1102 |
1404 | + self.ui = self.ui_class.Ui_Form() |
1405 | + self.ui.setupUi(self.bin_ui.container) |
1406 | + # connect all signals to this instance |
1407 | + QtCore.QMetaObject.connectSlotsByName(self) |
1408 | + |
1409 | + self._is_processing = None |
1410 | + self.is_processing = False |
1411 | + |
1412 | + self.backend = backend.ControlBackend() |
1413 | + |
1414 | + def _get_is_processing(self): |
1415 | + """Get the value of is_processing.""" |
1416 | + return self._is_processing |
1417 | + |
1418 | + def _set_is_processing(self, new_value): |
1419 | + """Set the value of is_processing. |
1420 | + |
1421 | + If is_processing, disable the UI and show a spinner. |
1422 | + If not is_processing, enable the UI and hide the spinner. |
1423 | + |
1424 | + """ |
1425 | + self.bin_ui.spinner.setVisible(new_value) |
1426 | + self.setEnabled(not new_value) |
1427 | + self._is_processing = new_value |
1428 | + |
1429 | + is_processing = property(fget=_get_is_processing, fset=_set_is_processing) |
1430 | + |
1431 | + # Invalid name "showEvent" |
1432 | + # pylint: disable=C0103 |
1433 | + |
1434 | + def showEvent(self, event): |
1435 | + """Load info.""" |
1436 | + self.load() |
1437 | + event.accept() |
1438 | + |
1439 | + def load(self): |
1440 | + """Load the widget with specific info.""" |
1441 | |
1442 | === modified file 'ubuntuone/controlpanel/sd_client/linux.py' |
1443 | --- ubuntuone/controlpanel/sd_client/linux.py 2011-06-14 21:44:32 +0000 |
1444 | +++ ubuntuone/controlpanel/sd_client/linux.py 2011-06-21 16:31:15 +0000 |
1445 | @@ -25,6 +25,8 @@ |
1446 | |
1447 | from ubuntuone.controlpanel.logger import setup_logging |
1448 | |
1449 | +# pylint: disable=E1101 |
1450 | + |
1451 | |
1452 | logger = setup_logging('sd_client') |
1453 |
- Adding folder A/B/C and then adding folder A/B/C/D fails as it should and gives a nice error dialog.
- Adding folder A/B/C/D and then adding folder A/B/C fails, and shows this on the command line:
Failure: ubuntuone. platform. linux.tools. ErrorSignal: ('FolderCreateE rror', (dbus.Dictionar y({dbus. String( u'path' ): dbus.String( u'/home/ eric/ogg/ dominik_ eulberg' )}, signature= dbus.Signature( 'ss')), dbus.String(u'UDFs can not be nested')))
But after this everything on the Files tab remains greyed out for a long time, and then becomes reponsive again with no changes.
We should at least show a similar error message. (For a future version we could think about just adding the new UDF and removing all previous once that it contains, but that may be harder to explain than just disallowing it.)
I will approve this branch, (unless I find other problems) since it is quite big already, so the other case can be solved in a new one.
I've tested with folders sharing a prefix, but I don't see that being very useful, since all UDFs share a common prefix already (/home/$USER/), so I would assume this has always worked? Maybe I misunderstand the instruction.