Merge lp:~dobey/ubuntuone-control-panel/update-4-2 into lp:ubuntuone-control-panel/stable-4-2
- update-4-2
- Merge into stable-4-2
Status: | Merged |
---|---|
Approved by: | dobey |
Approved revision: | no longer in the source branch. |
Merged at revision: | 375 |
Proposed branch: | lp:~dobey/ubuntuone-control-panel/update-4-2 |
Merge into: | lp:ubuntuone-control-panel/stable-4-2 |
Diff against target: |
2467 lines (+1014/-333) 34 files modified
data/qt/images.qrc (+1/-0) data/qt/share_links.ui (+175/-20) data/qt/ubuntuone.qss (+1/-1) run-tests (+1/-1) setup.py (+5/-7) ubuntuone-control-panel-qt-gnome.desktop.in (+11/-0) ubuntuone-control-panel.in (+0/-1) ubuntuone-installer.desktop.in (+1/-0) ubuntuone/controlpanel/backend.py (+13/-4) ubuntuone/controlpanel/gui/__init__.py (+7/-1) ubuntuone/controlpanel/gui/qt/controlpanel.py (+10/-5) ubuntuone/controlpanel/gui/qt/folders.py (+4/-3) ubuntuone/controlpanel/gui/qt/gui.py (+16/-0) ubuntuone/controlpanel/gui/qt/main/__init__.py (+22/-6) ubuntuone/controlpanel/gui/qt/main/darwin.py (+52/-8) ubuntuone/controlpanel/gui/qt/main/tests/test_darwin.py (+104/-5) ubuntuone/controlpanel/gui/qt/main/tests/test_main.py (+18/-8) ubuntuone/controlpanel/gui/qt/share_links.py (+86/-20) ubuntuone/controlpanel/gui/qt/share_links_search.py (+52/-32) ubuntuone/controlpanel/gui/qt/systray.py (+53/-68) ubuntuone/controlpanel/gui/qt/tests/test_controlpanel.py (+3/-4) ubuntuone/controlpanel/gui/qt/tests/test_folders.py (+6/-5) ubuntuone/controlpanel/gui/qt/tests/test_share_links.py (+51/-9) ubuntuone/controlpanel/gui/qt/tests/test_share_links_search.py (+47/-41) ubuntuone/controlpanel/gui/qt/tests/test_start.py (+11/-0) ubuntuone/controlpanel/gui/qt/tests/test_systray.py (+45/-57) ubuntuone/controlpanel/gui/qt/tests/test_wizard.py (+16/-5) ubuntuone/controlpanel/gui/qt/uniqueapp/__init__.py (+56/-0) ubuntuone/controlpanel/gui/qt/uniqueapp/tests/test_unique_app.py (+99/-4) ubuntuone/controlpanel/gui/qt/wizard.py (+7/-3) ubuntuone/controlpanel/gui/tests/__init__.py (+7/-7) ubuntuone/controlpanel/tests/test_backend.py (+30/-4) ubuntuone/controlpanel/utils/darwin.py (+2/-2) ubuntuone/controlpanel/web_client.py (+2/-2) |
To merge this branch: | bzr merge lp:~dobey/ubuntuone-control-panel/update-4-2 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Diego Sarmentero (community) | Approve | ||
Review via email: mp+138747@code.launchpad.net |
Commit message
[Mike McCracken]
- Trivial fix to make a test in test_folders actually run.
- Add support for menu in separate process on darwin. (LP: #1045939)
- Connect files service in wizard to enable display of remote folders. (LP: #978043)
[Rodney Dawes]
- Remove messaging menu integration file.
- Ignore some of the new pep8 warnings, and fix some others.
[Brian Curtin]
- Encode the volume path before checking whether or not it is a link.
[Barry Warsaw]
- Replace the third party simplejson module with the stdlib json module.
[Jeremy Bicha]
- Don't show Ubuntu One in System Settings when running GNOME.
[Diego Sarmentero]
- Adding IPC, so the new instances can send messages to the already running instance (LP: #1063927).
- Remove the text on x button pressed (LP: #1070917).
- Implementing new share tab design (LP: #1065194, #1056194).
- Accessing the Share tab from the systray menu and avoid refreshing the menu unnecesary (LP: #1072859 #1063781).
Description of the change
Ubuntu One Auto Pilot (otto-pilot) wrote : | # |
There was a problem validating some authors of the branch. Authors must be either one of the listed Launchpad users, or a member of one of the listed teams on Launchpad.
Persons or Teams:
contributor
ubuntuone-
Unaccepted Authors:
Barry Warsaw <email address hidden>
- 375. By Diego Sarmentero
-
[Mike McCracken]
- Trivial fix to make a test in test_folders actually run.
- Add support for menu in separate process on darwin. (LP: #1045939)
- Connect files service in wizard to enable display of remote folders. (LP: #978043)[Rodney Dawes]
- Remove messaging menu integration file.
- Ignore some of the new pep8 warnings, and fix some others.[Brian Curtin]
- Encode the volume path before checking whether or not it is a link.
[Barry Warsaw]
- Replace the third party simplejson module with the stdlib json module.
[Jeremy Bicha]
- Don't show Ubuntu One in System Settings when running GNOME.
[Diego Sarmentero]
- Adding IPC, so the new instances can send messages to the already running instance (LP: #1063927).
- Remove the text on x button pressed (LP: #1070917).
- Implementing new share tab design (LP: #1065194, #1056194).
- Accessing the Share tab from the systray menu and avoid refreshing the menu unnecesary (LP: #1072859 #1063781).
Preview Diff
1 | === modified file 'data/qt/images.qrc' |
2 | --- data/qt/images.qrc 2012-08-23 19:01:10 +0000 |
3 | +++ data/qt/images.qrc 2012-12-07 15:05:43 +0000 |
4 | @@ -26,6 +26,7 @@ |
5 | <file>../twitter.png</file> |
6 | <file>../facebook.png</file> |
7 | <file>../delete_search.png</file> |
8 | + <file>../search.png</file> |
9 | <file>../Ubuntu-R.ttf</file> |
10 | <file>../Ubuntu-B.ttf</file> |
11 | <file>ubuntuone.qss</file> |
12 | |
13 | === modified file 'data/qt/share_links.ui' |
14 | --- data/qt/share_links.ui 2012-08-23 19:01:10 +0000 |
15 | +++ data/qt/share_links.ui 2012-12-07 15:05:43 +0000 |
16 | @@ -59,28 +59,73 @@ |
17 | </font> |
18 | </property> |
19 | <property name="text"> |
20 | - <string>Search files</string> |
21 | + <string><html><head/><body><p>Search and share files</p></body></html></string> |
22 | </property> |
23 | </widget> |
24 | </item> |
25 | <item> |
26 | - <widget class="SearchBox" name="line_search"> |
27 | - <property name="sizePolicy"> |
28 | - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> |
29 | - <horstretch>0</horstretch> |
30 | - <verstretch>0</verstretch> |
31 | - </sizepolicy> |
32 | - </property> |
33 | - <property name="maximumSize"> |
34 | - <size> |
35 | - <width>400</width> |
36 | - <height>16777215</height> |
37 | - </size> |
38 | - </property> |
39 | - <property name="frame"> |
40 | - <bool>true</bool> |
41 | - </property> |
42 | - </widget> |
43 | + <layout class="QHBoxLayout" name="horizontalLayout_5"> |
44 | + <property name="spacing"> |
45 | + <number>0</number> |
46 | + </property> |
47 | + <item> |
48 | + <widget class="SearchBox" name="line_search"> |
49 | + <property name="sizePolicy"> |
50 | + <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> |
51 | + <horstretch>0</horstretch> |
52 | + <verstretch>0</verstretch> |
53 | + </sizepolicy> |
54 | + </property> |
55 | + <property name="minimumSize"> |
56 | + <size> |
57 | + <width>450</width> |
58 | + <height>0</height> |
59 | + </size> |
60 | + </property> |
61 | + <property name="maximumSize"> |
62 | + <size> |
63 | + <width>400</width> |
64 | + <height>16777215</height> |
65 | + </size> |
66 | + </property> |
67 | + <property name="frame"> |
68 | + <bool>true</bool> |
69 | + </property> |
70 | + </widget> |
71 | + </item> |
72 | + <item> |
73 | + <widget class="QPushButton" name="btn_search"> |
74 | + <property name="sizePolicy"> |
75 | + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> |
76 | + <horstretch>0</horstretch> |
77 | + <verstretch>0</verstretch> |
78 | + </sizepolicy> |
79 | + </property> |
80 | + <property name="maximumSize"> |
81 | + <size> |
82 | + <width>24</width> |
83 | + <height>24</height> |
84 | + </size> |
85 | + </property> |
86 | + <property name="text"> |
87 | + <string/> |
88 | + </property> |
89 | + </widget> |
90 | + </item> |
91 | + <item> |
92 | + <spacer name="horizontalSpacer_3"> |
93 | + <property name="orientation"> |
94 | + <enum>Qt::Horizontal</enum> |
95 | + </property> |
96 | + <property name="sizeHint" stdset="0"> |
97 | + <size> |
98 | + <width>40</width> |
99 | + <height>20</height> |
100 | + </size> |
101 | + </property> |
102 | + </spacer> |
103 | + </item> |
104 | + </layout> |
105 | </item> |
106 | </layout> |
107 | </item> |
108 | @@ -89,7 +134,7 @@ |
109 | <property name="currentIndex"> |
110 | <number>0</number> |
111 | </property> |
112 | - <widget class="QWidget" name="page"> |
113 | + <widget class="QWidget" name="page_public"> |
114 | <layout class="QVBoxLayout" name="verticalLayout_4"> |
115 | <property name="spacing"> |
116 | <number>0</number> |
117 | @@ -154,7 +199,7 @@ |
118 | </item> |
119 | </layout> |
120 | </widget> |
121 | - <widget class="QWidget" name="page_2"> |
122 | + <widget class="QWidget" name="page_details"> |
123 | <layout class="QVBoxLayout" name="verticalLayout_5"> |
124 | <property name="spacing"> |
125 | <number>0</number> |
126 | @@ -303,6 +348,116 @@ |
127 | </item> |
128 | </layout> |
129 | </widget> |
130 | + <widget class="QWidget" name="page_search"> |
131 | + <layout class="QVBoxLayout" name="verticalLayout_8"> |
132 | + <property name="spacing"> |
133 | + <number>0</number> |
134 | + </property> |
135 | + <property name="margin"> |
136 | + <number>0</number> |
137 | + </property> |
138 | + <item> |
139 | + <layout class="QHBoxLayout" name="horizontalLayout_2"> |
140 | + <property name="spacing"> |
141 | + <number>0</number> |
142 | + </property> |
143 | + <property name="leftMargin"> |
144 | + <number>15</number> |
145 | + </property> |
146 | + <item> |
147 | + <widget class="QLabel" name="label_search_results"> |
148 | + <property name="sizePolicy"> |
149 | + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> |
150 | + <horstretch>0</horstretch> |
151 | + <verstretch>0</verstretch> |
152 | + </sizepolicy> |
153 | + </property> |
154 | + <property name="font"> |
155 | + <font> |
156 | + <weight>75</weight> |
157 | + <bold>true</bold> |
158 | + </font> |
159 | + </property> |
160 | + <property name="text"> |
161 | + <string><html><head/><body><p>Search Results</p></body></html></string> |
162 | + </property> |
163 | + </widget> |
164 | + </item> |
165 | + <item> |
166 | + <spacer name="horizontalSpacer_2"> |
167 | + <property name="orientation"> |
168 | + <enum>Qt::Horizontal</enum> |
169 | + </property> |
170 | + <property name="sizeType"> |
171 | + <enum>QSizePolicy::Expanding</enum> |
172 | + </property> |
173 | + <property name="sizeHint" stdset="0"> |
174 | + <size> |
175 | + <width>40</width> |
176 | + <height>0</height> |
177 | + </size> |
178 | + </property> |
179 | + </spacer> |
180 | + </item> |
181 | + <item> |
182 | + <widget class="QPushButton" name="back_to_file_list_2"> |
183 | + <property name="text"> |
184 | + <string>Back to file list</string> |
185 | + </property> |
186 | + </widget> |
187 | + </item> |
188 | + </layout> |
189 | + </item> |
190 | + <item> |
191 | + <widget class="QTreeWidget" name="tree_search_results"> |
192 | + <property name="alternatingRowColors"> |
193 | + <bool>true</bool> |
194 | + </property> |
195 | + <property name="indentation"> |
196 | + <number>15</number> |
197 | + </property> |
198 | + <property name="rootIsDecorated"> |
199 | + <bool>false</bool> |
200 | + </property> |
201 | + <attribute name="headerVisible"> |
202 | + <bool>false</bool> |
203 | + </attribute> |
204 | + <attribute name="headerDefaultSectionSize"> |
205 | + <number>250</number> |
206 | + </attribute> |
207 | + <attribute name="headerStretchLastSection"> |
208 | + <bool>true</bool> |
209 | + </attribute> |
210 | + <column> |
211 | + <property name="text"> |
212 | + <string notr="true">Filename</string> |
213 | + </property> |
214 | + </column> |
215 | + <column> |
216 | + <property name="text"> |
217 | + <string notr="true">Actions</string> |
218 | + </property> |
219 | + </column> |
220 | + </widget> |
221 | + </item> |
222 | + <item> |
223 | + <widget class="QLabel" name="label_no_results"> |
224 | + <property name="sizePolicy"> |
225 | + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> |
226 | + <horstretch>0</horstretch> |
227 | + <verstretch>0</verstretch> |
228 | + </sizepolicy> |
229 | + </property> |
230 | + <property name="text"> |
231 | + <string>You have no files with this name.</string> |
232 | + </property> |
233 | + <property name="indent"> |
234 | + <number>20</number> |
235 | + </property> |
236 | + </widget> |
237 | + </item> |
238 | + </layout> |
239 | + </widget> |
240 | </widget> |
241 | </item> |
242 | </layout> |
243 | |
244 | === modified file 'data/qt/ubuntuone.qss' |
245 | --- data/qt/ubuntuone.qss 2012-10-12 19:38:02 +0000 |
246 | +++ data/qt/ubuntuone.qss 2012-12-07 15:05:43 +0000 |
247 | @@ -402,7 +402,7 @@ |
248 | background-origin: margin; |
249 | } |
250 | |
251 | -QPushButton#back_to_file_list { |
252 | +QPushButton#back_to_file_list, QPushButton#back_to_file_list_2 { |
253 | background: transparent; |
254 | border: none; |
255 | color: #dd4814; |
256 | |
257 | === added file 'data/search.png' |
258 | Binary files data/search.png 1970-01-01 00:00:00 +0000 and data/search.png 2012-12-07 15:05:43 +0000 differ |
259 | === modified file 'run-tests' |
260 | --- run-tests 2012-10-23 19:26:30 +0000 |
261 | +++ run-tests 2012-12-07 15:05:43 +0000 |
262 | @@ -34,7 +34,7 @@ |
263 | style_check() { |
264 | USE_PYFLAKES=1 u1lint --ignore ubuntuone/controlpanel/gui/qt/ui |
265 | if [ -x `which pep8` ]; then |
266 | - pep8 --exclude '.bzr' --repeat . bin/* |
267 | + pep8 --exclude '.bzr,.pc,build' --ignore=E126,E127,E128 --repeat . bin/* |
268 | else |
269 | echo "Please install the 'pep8' package." |
270 | fi |
271 | |
272 | === modified file 'setup.py' |
273 | --- setup.py 2012-05-24 18:59:58 +0000 |
274 | +++ setup.py 2012-12-07 15:05:43 +0000 |
275 | @@ -33,17 +33,16 @@ |
276 | 'https://launchpad.net/python-distutils-extra' |
277 | sys.exit(1) |
278 | assert DistUtilsExtra.auto.__version__ >= '2.18', \ |
279 | - 'needs DistUtilsExtra.auto >= 2.18' |
280 | + 'needs DistUtilsExtra.auto >= 2.18' |
281 | |
282 | from distutils import log |
283 | |
284 | POT_FILE = 'po/ubuntuone-control-panel.pot' |
285 | SERVICE_FILE = 'com.ubuntuone.controlpanel.service' |
286 | -MESSAGE_ENTRY = 'ubuntuone-control-panel' |
287 | CONSTANTS = 'ubuntuone/controlpanel/constants.py' |
288 | |
289 | CLEANFILES = [ |
290 | - SERVICE_FILE, MESSAGE_ENTRY, CONSTANTS, POT_FILE, |
291 | + SERVICE_FILE, CONSTANTS, POT_FILE, |
292 | 'MANIFEST'] |
293 | QT_UI_DIR = os.path.join('ubuntuone', 'controlpanel', 'gui', 'qt', 'ui') |
294 | |
295 | @@ -51,7 +50,7 @@ |
296 | def replace_prefix(prefix): |
297 | """Replace every '@prefix@' with prefix within 'filename' content.""" |
298 | # replace .service file, DATA_DIR constant |
299 | - for filename in (SERVICE_FILE, MESSAGE_ENTRY, CONSTANTS): |
300 | + for filename in (SERVICE_FILE, CONSTANTS): |
301 | with open(filename + '.in') as in_file: |
302 | content = in_file.read() |
303 | with open(filename, 'w') as out_file: |
304 | @@ -116,7 +115,7 @@ |
305 | os.putenv('PATH', path + os.path.pathsep + os.path.join( |
306 | os.path.dirname(PyQt4.__file__), 'bin')) |
307 | if os.system('pyrcc4 -no-compress "%s" -o "%s"' % |
308 | - (qrc_file, py_file)) > 0: |
309 | + (qrc_file, py_file)) > 0: |
310 | self.warn('Unable to generate python module {py_file}' |
311 | ' for resource file {qrc_file}'.format( |
312 | py_file=py_file, qrc_file=qrc_file)) |
313 | @@ -159,7 +158,7 @@ |
314 | |
315 | def run(self): |
316 | """Execute the command.""" |
317 | - basepath = os.path.join('data', 'qt') |
318 | + basepath = os.path.join('data', 'qt') |
319 | # TODO: build the resource files so that we can include them |
320 | #self.build_rc(os.path.join(basepath, 'icons_rc.py'), |
321 | # os.path.join(os.path.dirname(__file__), 'icons'), |
322 | @@ -217,7 +216,6 @@ |
323 | ('lib/ubuntuone-control-panel', |
324 | ['bin/ubuntuone-control-panel-backend']), |
325 | ('share/dbus-1/services/', [SERVICE_FILE]), |
326 | - ('share/indicators/messages/applications/', [MESSAGE_ENTRY]), |
327 | ('share/apport/package-hooks/', |
328 | ['data/source_ubuntuone-control-panel.py']), |
329 | ], |
330 | |
331 | === added file 'ubuntuone-control-panel-qt-gnome.desktop.in' |
332 | --- ubuntuone-control-panel-qt-gnome.desktop.in 1970-01-01 00:00:00 +0000 |
333 | +++ ubuntuone-control-panel-qt-gnome.desktop.in 2012-12-07 15:05:43 +0000 |
334 | @@ -0,0 +1,11 @@ |
335 | +[Desktop Entry] |
336 | +Name=Ubuntu One |
337 | +_Comment=Configure and manage your Ubuntu One account |
338 | +Exec=ubuntuone-control-panel-qt |
339 | +Icon=ubuntuone |
340 | +Terminal=false |
341 | +Type=Application |
342 | +OnlyShowIn=GNOME; |
343 | +Categories=GNOME;GTK;Settings;DesktopSettings;X-GNOME-PersonalSettings |
344 | +StartupNotify=true |
345 | +X-Ayatana-Appmenu-Show-Stubs=False |
346 | |
347 | === removed file 'ubuntuone-control-panel.in' |
348 | --- ubuntuone-control-panel.in 2011-09-16 14:37:20 +0000 |
349 | +++ ubuntuone-control-panel.in 1970-01-01 00:00:00 +0000 |
350 | @@ -1,1 +0,0 @@ |
351 | -@prefix@/share/applications/ubuntuone-installer.desktop |
352 | |
353 | === modified file 'ubuntuone-installer.desktop.in' |
354 | --- ubuntuone-installer.desktop.in 2012-09-10 14:12:04 +0000 |
355 | +++ ubuntuone-installer.desktop.in 2012-12-07 15:05:43 +0000 |
356 | @@ -5,6 +5,7 @@ |
357 | Icon=ubuntuone |
358 | Terminal=false |
359 | Type=Application |
360 | +NotShowIn=GNOME; |
361 | Categories=GNOME;GTK;Settings;DesktopSettings;X-GNOME-Settings-Panel;X-GNOME-PersonalSettings |
362 | StartupNotify=true |
363 | X-Ayatana-Appmenu-Show-Stubs=False |
364 | |
365 | === modified file 'ubuntuone/controlpanel/backend.py' |
366 | --- ubuntuone/controlpanel/backend.py 2012-10-29 16:02:46 +0000 |
367 | +++ ubuntuone/controlpanel/backend.py 2012-12-07 15:05:43 +0000 |
368 | @@ -226,10 +226,17 @@ |
369 | self.sd_client.set_status_changed_handler(cb) |
370 | self.is_sd_client_handler_set = True |
371 | |
372 | + def remove_status_changed_handler(self, handler): |
373 | + """Remove 'handler' from callbacks list.""" |
374 | + if handler in self._status_changed_handlers: |
375 | + self._status_changed_handlers.remove(handler) |
376 | + |
377 | @inlineCallbacks |
378 | def _process_device_web_info(self, devices, |
379 | - enabled=None, limit_bw=None, limits=None, autoconnect=None, |
380 | - show_notifs=None, share_autosubscribe=None, udf_autosubscribe=None): |
381 | + enabled=None, limit_bw=None, |
382 | + limits=None, autoconnect=None, |
383 | + show_notifs=None, share_autosubscribe=None, |
384 | + udf_autosubscribe=None): |
385 | """Return a lis of processed devices. |
386 | |
387 | If all the file sync settings are None, do not attach that info. |
388 | @@ -275,8 +282,10 @@ |
389 | |
390 | @inlineCallbacks |
391 | def _process_device_local_info(self, |
392 | - enabled=None, limit_bw=None, limits=None, autoconnect=None, |
393 | - show_notifs=None, share_autosubscribe=None, udf_autosubscribe=None): |
394 | + enabled=None, limit_bw=None, |
395 | + limits=None, autoconnect=None, |
396 | + show_notifs=None, share_autosubscribe=None, |
397 | + udf_autosubscribe=None): |
398 | """Return the information for the local device. |
399 | |
400 | If all the file sync settings are None, do not attach that info. |
401 | |
402 | === modified file 'ubuntuone/controlpanel/gui/__init__.py' |
403 | --- ubuntuone/controlpanel/gui/__init__.py 2012-10-23 19:26:30 +0000 |
404 | +++ ubuntuone/controlpanel/gui/__init__.py 2012-12-07 15:05:43 +0000 |
405 | @@ -232,18 +232,24 @@ |
406 | NO_DEVICES = _('No devices to show.') |
407 | NO_FOLDERS = _('No folders to show.') |
408 | NO_PAIRING_RECORD = _('There is no Ubuntu One pairing record.') |
409 | +NO_RESULTS = _('No results') |
410 | +NO_RESULTS_LABEL = _('You have no files with this name.') |
411 | OPEN = _('Open') |
412 | OPEN_UBUNTU_ONE = _('Open Ubuntu One') |
413 | OPEN_UBUNTU_ONE_FOLDER = _('Open the Ubuntu One Folder') |
414 | PERCENTAGE_LABEL = _('%(percentage)s used') |
415 | PLEASE_WAIT = _('Please wait') |
416 | PROFILE_LABEL = _('Personal details') |
417 | +SHARE_FILE = _('Share file') |
418 | +SHARE_THESE_FILES = _('Select a file to share:') |
419 | QUOTA_LABEL = _('Using %(used)s of %(total)s (%(percentage).0f%%)') |
420 | REMOVE_BUTTON = _('Remove') |
421 | RECENT_TRANSFERS = _('Recent Transfers') |
422 | RESTORE_LABEL = _('Restore') |
423 | -SEARCH_FILES = _('Search files') |
424 | +SEARCH_FILES = _('Search and share files') |
425 | SELECT_FOLDERS = _('Select sync folders') |
426 | +SEARCH_FOR = _('Search for: %s') |
427 | +SEARCH_RESULTS = _('Search Results for "%s"') |
428 | SERVICES_BUTTON_TOOLTIP = _('Manage the sync services') |
429 | SERVICES_TITLE = _('Enable the sync services for this computer.') |
430 | SETTINGS_ALLOW_NOTIFICATIONS = _('Allow all notifications to this device') |
431 | |
432 | === modified file 'ubuntuone/controlpanel/gui/qt/controlpanel.py' |
433 | --- ubuntuone/controlpanel/gui/qt/controlpanel.py 2012-10-16 11:31:33 +0000 |
434 | +++ ubuntuone/controlpanel/gui/qt/controlpanel.py 2012-12-07 15:05:43 +0000 |
435 | @@ -100,7 +100,7 @@ |
436 | |
437 | @log_call(logger.debug) |
438 | def on_credentials_not_found(self): |
439 | - """Credentials are not found or were removed.""" |
440 | + """Credentials not found / were removed. Log in, then show wizard.""" |
441 | self.ui.wizard.restart() |
442 | self.ui.switcher.setCurrentWidget(self.ui.wizard) |
443 | # By design request, when the user logs in, he has to end |
444 | @@ -113,10 +113,14 @@ |
445 | @defer.inlineCallbacks |
446 | @log_call(logger.debug) |
447 | def on_credentials_found(self): |
448 | - """Credentials are not found or were removed.""" |
449 | + """Credentials are found on first try, connect and show mgmt UI.""" |
450 | + self.connect_file_sync() |
451 | + yield self.show_management_ui() |
452 | + |
453 | + @defer.inlineCallbacks |
454 | + def show_management_ui(self): |
455 | + """Show mgmt UI. File sync is already connected if desired.""" |
456 | self.ui.switcher.setCurrentWidget(self.ui.management) |
457 | - self.connect_file_sync() |
458 | - |
459 | info = yield self.backend.account_info() |
460 | self.process_info(info) |
461 | self.is_processing = False |
462 | @@ -182,7 +186,8 @@ |
463 | @log_call(logger.info) |
464 | def on_wizard_finished(self, status): |
465 | """Move to controlpanel if wizard ended successfully.""" |
466 | - self.on_credentials_found() |
467 | + # NOTE: the wizard has connected the files service already |
468 | + self.show_management_ui() |
469 | |
470 | @log_call(logger.info) |
471 | def start_from_license(self): |
472 | |
473 | === modified file 'ubuntuone/controlpanel/gui/qt/folders.py' |
474 | --- ubuntuone/controlpanel/gui/qt/folders.py 2012-08-07 01:37:24 +0000 |
475 | +++ ubuntuone/controlpanel/gui/qt/folders.py 2012-12-07 15:05:43 +0000 |
476 | @@ -331,7 +331,8 @@ |
477 | os.path.exists(volume_path)) |
478 | response = YES |
479 | if subscribed and os.path.exists(volume_path): |
480 | - if os.path.isdir(volume_path) and not is_link(volume_path): |
481 | + if os.path.isdir(volume_path) and \ |
482 | + not is_link(volume_path.encode("utf8")): |
483 | text = FOLDERS_CONFIRM_MERGE % {'folder_path': volume_path} |
484 | buttons = YES | NO | CANCEL |
485 | response = QtGui.QMessageBox.warning(self, '', text, buttons, |
486 | @@ -522,8 +523,8 @@ |
487 | folders = [] |
488 | for _, _, volumes in volumes_info: |
489 | for volume in volumes: |
490 | - if (volume[u'type'] == self.backend.FOLDER_TYPE and |
491 | - bool(volume['subscribed'])): |
492 | + if volume[u'type'] == self.backend.FOLDER_TYPE and \ |
493 | + bool(volume['subscribed']): |
494 | suggested = volume.get(u'suggested_path', |
495 | volume[u'display_name']) |
496 | folders.append((volume['path'], volume['volume_id'], |
497 | |
498 | === modified file 'ubuntuone/controlpanel/gui/qt/gui.py' |
499 | --- ubuntuone/controlpanel/gui/qt/gui.py 2012-08-20 17:51:54 +0000 |
500 | +++ ubuntuone/controlpanel/gui/qt/gui.py 2012-12-07 15:05:43 +0000 |
501 | @@ -61,6 +61,21 @@ |
502 | else: |
503 | self.entry = None |
504 | |
505 | + def connect_app_signal(self, app): |
506 | + """Connect App signals.""""" |
507 | + app.switch_to.connect(self.switch_to) |
508 | + app.activate_window.connect(self.raise_to_top) |
509 | + |
510 | + def raise_to_top(self): |
511 | + """Force the application to raise in top of any other app.""" |
512 | + self.activateWindow() |
513 | + self.raise_() |
514 | + if self.isMinimized(): |
515 | + self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint) |
516 | + self.showNormal() |
517 | + self.setWindowFlags(QtCore.Qt.Window) |
518 | + self.showNormal() |
519 | + |
520 | def _setup(self): |
521 | """Do some extra setupping for the UI.""" |
522 | self.ui.control_panel.finished.connect(self.close) |
523 | @@ -128,6 +143,7 @@ |
524 | window.check_updates() |
525 | window.show() |
526 | window.raise_() |
527 | + window.connect_app_signal(app) |
528 | else: |
529 | window = None |
530 | if with_icon or minimized: |
531 | |
532 | === modified file 'ubuntuone/controlpanel/gui/qt/main/__init__.py' |
533 | --- ubuntuone/controlpanel/gui/qt/main/__init__.py 2012-10-23 19:26:30 +0000 |
534 | +++ ubuntuone/controlpanel/gui/qt/main/__init__.py 2012-12-07 15:05:43 +0000 |
535 | @@ -40,10 +40,14 @@ |
536 | |
537 | if sys.platform == 'darwin': |
538 | from ubuntuone.controlpanel.gui.qt.main.darwin import ( |
539 | - install_platform_event_handlers) |
540 | + install_platform_event_handlers, MenubarIconLauncher) |
541 | assert(install_platform_event_handlers) |
542 | + assert(MenubarIconLauncher) |
543 | else: |
544 | - install_platform_event_handlers = lambda app: None |
545 | + class MenubarIconLauncher: |
546 | + """Dummy. Separate menu launcher not used on win/linux.""" |
547 | + install_platform_event_handlers = lambda app, **kwargs: None |
548 | + |
549 | # pylint: enable=C0103 |
550 | |
551 | from ubuntuone.controlpanel.utils import install_config_and_daemons |
552 | @@ -118,9 +122,7 @@ |
553 | args = parser.parse_args(args=arg_list[bin_position:]) |
554 | switch_to = args.switch_to |
555 | minimized = args.minimized |
556 | - # On Darwin, because of how apps are started, we need |
557 | - # this by default. |
558 | - with_icon = args.with_icon or sys.platform == 'darwin' |
559 | + with_icon = args.with_icon |
560 | installer = args.installer |
561 | source.main(app) |
562 | |
563 | @@ -135,9 +137,17 @@ |
564 | |
565 | install_config_and_daemons() |
566 | |
567 | + if sys.platform == 'darwin': |
568 | + with_icon = False |
569 | + # cmd-q in CP shouldn't kill SD or the menu: |
570 | + quit_kills_sd = False |
571 | + else: |
572 | + quit_kills_sd = not with_icon |
573 | + |
574 | # need to keep a reference to the menu or our handler will be |
575 | # removed |
576 | - menubar = install_platform_event_handlers(app) |
577 | + menubar = install_platform_event_handlers(app, |
578 | + quit_kills_sd=quit_kills_sd) |
579 | menubar |
580 | |
581 | # Unused variable 'window', 'icon', pylint: disable=W0612 |
582 | @@ -151,4 +161,10 @@ |
583 | if icon: |
584 | app.new_instance.connect(icon.restore_window) |
585 | |
586 | + if sys.platform == 'darwin': |
587 | + # start separate menu app instead of Qt systray menu, waiting |
588 | + # until it is safe to do so without IPC races: |
589 | + menu_launcher = MenubarIconLauncher() |
590 | + assert(menu_launcher) |
591 | + |
592 | source.main_start(app) |
593 | |
594 | === modified file 'ubuntuone/controlpanel/gui/qt/main/darwin.py' |
595 | --- ubuntuone/controlpanel/gui/qt/main/darwin.py 2012-10-04 20:34:22 +0000 |
596 | +++ ubuntuone/controlpanel/gui/qt/main/darwin.py 2012-12-07 15:05:43 +0000 |
597 | @@ -16,28 +16,40 @@ |
598 | |
599 | """Darwin-specific GUI event handling code.""" |
600 | |
601 | +import sys |
602 | +import subprocess |
603 | + |
604 | from PyQt4 import QtGui |
605 | from twisted.internet.defer import inlineCallbacks |
606 | |
607 | +from dirspec.utils import get_program_path |
608 | + |
609 | from ubuntuone.platform.tools import SyncDaemonTool |
610 | |
611 | +from ubuntuone.controlpanel import cache |
612 | +from ubuntuone.controlpanel.backend import (STATUS_KEY, FILE_SYNC_ERROR) |
613 | from ubuntuone.controlpanel.gui.qt.main import twisted_main |
614 | +from ubuntuone.controlpanel.logger import setup_logging |
615 | + |
616 | + |
617 | +logger = setup_logging('qt.main.darwin') |
618 | |
619 | |
620 | @inlineCallbacks |
621 | -def handle_cmd_q(app, event): |
622 | +def handle_cmd_q(app, event, should_quit_sd=False): |
623 | """Handle the quit event on darwin.""" |
624 | # pylint: disable=W0702 |
625 | - try: |
626 | - st = SyncDaemonTool() |
627 | - yield st.quit() |
628 | - except: |
629 | - pass |
630 | + if should_quit_sd: |
631 | + try: |
632 | + st = SyncDaemonTool() |
633 | + yield st.quit() |
634 | + except: |
635 | + pass |
636 | twisted_main.main_quit(app) |
637 | app.aboutToQuit.emit() |
638 | |
639 | |
640 | -def install_platform_event_handlers(app): |
641 | +def install_platform_event_handlers(app, quit_kills_sd=False): |
642 | """Add code to catch cmd-Q.""" |
643 | # on darwin, a menu item with 'quit' in its title is moved to |
644 | # the application menu and given the cmd-Q shortcut by Qt. If |
645 | @@ -52,9 +64,41 @@ |
646 | |
647 | quit_action = QtGui.QAction("quit", menubar, |
648 | triggered=lambda x: |
649 | - handle_cmd_q(app, x)) |
650 | + handle_cmd_q(app, |
651 | + x, |
652 | + quit_kills_sd)) |
653 | quit_action.setObjectName("darwin-cmd-q") |
654 | menu = menubar.addMenu("This string is not used.") |
655 | menu.addAction(quit_action) |
656 | |
657 | return menubar |
658 | + |
659 | + |
660 | +class MenubarIconLauncher(cache.Cache): |
661 | + """Listens to status, launches the menubar app once it is safe to do so.""" |
662 | + |
663 | + def __init__(self): |
664 | + """Register as listener.""" |
665 | + super(MenubarIconLauncher, self).__init__() |
666 | + self.backend.add_status_changed_handler(self.handle_status_update) |
667 | + |
668 | + def handle_status_update(self, result): |
669 | + """Process updates, launch menu on non-error""" |
670 | + |
671 | + if result[STATUS_KEY] == FILE_SYNC_ERROR: |
672 | + logger.warning("not starting menu, file sync in error state") |
673 | + return |
674 | + |
675 | + self.start_menu_process() |
676 | + self.backend.remove_status_changed_handler(self.handle_status_update) |
677 | + |
678 | + def start_menu_process(self): |
679 | + """Find and launch separate menu process.""" |
680 | + if getattr(sys, 'frozen', None) is None: |
681 | + logger.warning("Can not launch pyobjc menu from source, ignoring.") |
682 | + return |
683 | + menu_app_name = 'Ubuntu One Menu' |
684 | + path = get_program_path(menu_app_name, |
685 | + app_names={menu_app_name: |
686 | + 'Ubuntu One Menu.app'}) |
687 | + subprocess.Popen(path) |
688 | |
689 | === modified file 'ubuntuone/controlpanel/gui/qt/main/tests/test_darwin.py' |
690 | --- ubuntuone/controlpanel/gui/qt/main/tests/test_darwin.py 2012-10-04 20:34:22 +0000 |
691 | +++ ubuntuone/controlpanel/gui/qt/main/tests/test_darwin.py 2012-12-07 15:05:43 +0000 |
692 | @@ -18,8 +18,12 @@ |
693 | |
694 | from PyQt4 import QtGui |
695 | |
696 | +import subprocess |
697 | +import sys |
698 | + |
699 | from twisted.internet.defer import (inlineCallbacks, succeed) |
700 | |
701 | +from ubuntuone.controlpanel import backend |
702 | from ubuntuone.controlpanel.gui.qt import main |
703 | from ubuntuone.controlpanel.tests import TestCase |
704 | from ubuntuone.controlpanel.gui.tests import FakeSignal |
705 | @@ -60,6 +64,28 @@ |
706 | raise Exception("exception") |
707 | |
708 | |
709 | +class FakeBackend(object): |
710 | + """Mock backend""" |
711 | + |
712 | + def __init__(self): |
713 | + """init""" |
714 | + |
715 | + super(FakeBackend, self).__init__() |
716 | + self.handlers = [] |
717 | + |
718 | + def add_status_changed_handler(self, h): |
719 | + """add handler""" |
720 | + self.handlers.append(h) |
721 | + |
722 | + def remove_status_changed_handler(self, h): |
723 | + """remove handler """ |
724 | + self.handlers.remove(h) |
725 | + |
726 | + def clear(self): |
727 | + """reset handlers""" |
728 | + self.handlers = [] |
729 | + |
730 | + |
731 | class QuitTestCase(TestCase): |
732 | """Test quit event handling.""" |
733 | |
734 | @@ -73,13 +99,22 @@ |
735 | |
736 | @inlineCallbacks |
737 | def test_cmd_q_func_quits_sd(self): |
738 | - """Test that we call syncdaemontool.quit """ |
739 | + """Test that we call syncdaemontool.quit when asked""" |
740 | self.patch(main.darwin, 'SyncDaemonTool', FakeSDTool) |
741 | |
742 | - yield main.darwin.handle_cmd_q(self.fake_app, None) |
743 | + yield main.darwin.handle_cmd_q(self.fake_app, None, |
744 | + should_quit_sd=True) |
745 | self.assertEqual(FakeSDTool.called, True) |
746 | |
747 | @inlineCallbacks |
748 | + def test_cmd_q_func_doesnt_quit_sd(self): |
749 | + """Test that we don't call syncdaemontool.quit by default""" |
750 | + self.patch(main.darwin, 'SyncDaemonTool', FakeSDTool) |
751 | + |
752 | + yield main.darwin.handle_cmd_q(self.fake_app, None) |
753 | + self.assertEqual(FakeSDTool.called, False) |
754 | + |
755 | + @inlineCallbacks |
756 | def test_cmd_q_func_ignores_exception(self): |
757 | """Test that we keep going when SDT raises.""" |
758 | self.patch(main.darwin, 'SyncDaemonTool', FakeSDToolRaising) |
759 | @@ -93,12 +128,76 @@ |
760 | class InstallEventHandlersTestCase(TestCase): |
761 | """Test creating Qt menu items.""" |
762 | |
763 | - def test_install_handlers_creates_action(self): |
764 | - """Test menu items created""" |
765 | + def test_install_handlers_creates_kill_action(self): |
766 | + """Test creating menu item that will kill sd""" |
767 | + self.patch(main.darwin, 'handle_cmd_q', |
768 | + self._set_called) |
769 | + app = QtGui.QApplication.instance() |
770 | + k = dict(quit_kills_sd=True) |
771 | + menubar = main.darwin.install_platform_event_handlers(app, **k) |
772 | + quit_action = menubar.findChild(QtGui.QAction, "darwin-cmd-q") |
773 | + quit_action.trigger() |
774 | + self.assertEqual(self._called, ((app, False, True), {})) |
775 | + |
776 | + def test_install_handlers_creates_nonkill_action(self): |
777 | + """Test creating menu item that will not kill sd""" |
778 | self.patch(main.darwin, 'handle_cmd_q', |
779 | self._set_called) |
780 | app = QtGui.QApplication.instance() |
781 | menubar = main.darwin.install_platform_event_handlers(app) |
782 | quit_action = menubar.findChild(QtGui.QAction, "darwin-cmd-q") |
783 | quit_action.trigger() |
784 | - self.assertEqual(self._called, ((app, False), {})) |
785 | + self.assertEqual(self._called, ((app, False, False), {})) |
786 | + |
787 | + |
788 | +class LauncherTestCase(TestCase): |
789 | + """Test pyobjc menu launcher.""" |
790 | + |
791 | + @inlineCallbacks |
792 | + def setUp(self): |
793 | + """Set up launcher and patches""" |
794 | + yield super(LauncherTestCase, self).setUp() |
795 | + |
796 | + self.patch(backend, 'ControlBackend', FakeBackend) |
797 | + self.patch(main.darwin, 'get_program_path', |
798 | + lambda a, **kw: 'testpath') |
799 | + self.addCleanup(lambda: self.launcher.backend.clear()) |
800 | + |
801 | + self.launcher = main.darwin.MenubarIconLauncher() |
802 | + |
803 | + def test_add_on_init(self): |
804 | + """Test adding to handler.""" |
805 | + self.assertEqual(self.launcher.backend.handlers, |
806 | + [self.launcher.handle_status_update]) |
807 | + |
808 | + def test_handle_status_update_error(self): |
809 | + """Test doing nothing on error in handle_status_update""" |
810 | + self.patch(self.launcher, 'start_menu_process', self._set_called) |
811 | + self.launcher.handle_status_update({backend.STATUS_KEY: |
812 | + backend.FILE_SYNC_ERROR}) |
813 | + self.assertEqual(self._called, False) |
814 | + |
815 | + def test_handle_status_update(self): |
816 | + """Test calling start_menu_process and removing handler""" |
817 | + self.patch(self.launcher, 'start_menu_process', self._set_called) |
818 | + self.launcher.handle_status_update({backend.STATUS_KEY: 'copacetic'}) |
819 | + self.assertEqual(self._called, ((), {})) |
820 | + self.assertEqual(self.launcher.backend.handlers, []) |
821 | + |
822 | + def test_start_menu_process_nonfrozen(self): |
823 | + """Start does nothing when not frozen.""" |
824 | + sys.frozen = None |
825 | + self.addCleanup(delattr, sys, 'frozen') |
826 | + |
827 | + self.patch(subprocess, 'Popen', self._set_called) |
828 | + self.launcher.start_menu_process() |
829 | + self.assertEqual(self._called, False) |
830 | + |
831 | + def test_start_menu_process_frozen(self): |
832 | + """Start launches menu when frozen.""" |
833 | + sys.frozen = 'macosx' |
834 | + self.addCleanup(delattr, sys, 'frozen') |
835 | + |
836 | + self.patch(subprocess, 'Popen', self._set_called) |
837 | + self.launcher.start_menu_process() |
838 | + self.assertEqual(self._called, (('testpath',), {})) |
839 | |
840 | === modified file 'ubuntuone/controlpanel/gui/qt/main/tests/test_main.py' |
841 | --- ubuntuone/controlpanel/gui/qt/main/tests/test_main.py 2012-10-04 20:47:39 +0000 |
842 | +++ ubuntuone/controlpanel/gui/qt/main/tests/test_main.py 2012-12-07 15:05:43 +0000 |
843 | @@ -137,7 +137,7 @@ |
844 | self.patch(main.source, "main_start", lambda app: None) |
845 | self.patch(QtCore, "QTranslator", lambda: self.translator) |
846 | self.patch(main, 'install_platform_event_handlers', |
847 | - lambda app: None) |
848 | + lambda app, **kwargs: None) |
849 | |
850 | self.qt4reactor_installed = False |
851 | |
852 | @@ -194,11 +194,11 @@ |
853 | {'minimized': True, 'with_icon': False, 'installer': False}) |
854 | |
855 | def test_darwin_defult_is_with_icon(self): |
856 | - """On Darwin, the default is to show the icon.""" |
857 | + """On Darwin, the default is not to show the icon.""" |
858 | self.patch(sys, 'platform', 'darwin') |
859 | main.main([sys.argv[0]]) |
860 | self.assertEqual(self.start.args[1], |
861 | - {'minimized': False, 'with_icon': True, 'installer': False}) |
862 | + {'minimized': False, 'with_icon': False, 'installer': False}) |
863 | |
864 | def test_not_darwin_defult_is_with_icon(self): |
865 | """On Not-darwin, the default is to not show the icon.""" |
866 | @@ -215,7 +215,7 @@ |
867 | {'minimized': True, 'with_icon': False, 'installer': False}) |
868 | |
869 | def test_with_icon_option(self): |
870 | - """Ensure the --minimized option is parsed and passed correctly.""" |
871 | + """Ensure the --with-icon option is parsed and passed correctly.""" |
872 | self.patch(sys, 'platform', 'not-darwin') |
873 | main.main([sys.argv[0], "--with-icon"]) |
874 | self.assertEqual(self.start.args[1], |
875 | @@ -274,7 +274,7 @@ |
876 | """Ensure the new_instance signal is connected.""" |
877 | main.main([sys.argv[0]]) |
878 | self.assertEqual(self.app.new_instance.target, |
879 | - self.start.window.raise_) |
880 | + [self.start.window.raise_]) |
881 | |
882 | def test_darwin_installs_qt4reactor(self): |
883 | """Ensure the qt4 reactor is installed when requested.""" |
884 | @@ -308,10 +308,20 @@ |
885 | main.main([sys.argv[0]], install_reactor_darwin=False) |
886 | self.assertEqual(self._called, ((), {})) |
887 | |
888 | - def test_install_event_handlers(self): |
889 | - """Test that install_platform_event_handlers is called.""" |
890 | + def test_install_event_handlers_darwin(self): |
891 | + """Test that install_platform_event_handlers is called on darwin.""" |
892 | + self.patch(sys, 'platform', 'darwin') |
893 | self.patch(main, 'install_platform_event_handlers', |
894 | self._set_called) |
895 | |
896 | main.main([sys.argv[0]], install_reactor_darwin=False) |
897 | - self.assertEqual(self._called, ((self.app,), {})) |
898 | + self.assertEqual(self._called, ((self.app,), {'quit_kills_sd': False})) |
899 | + |
900 | + def test_install_event_handlers_non_darwin(self): |
901 | + """Test install_platform_event_handlers is called on not-darwin.""" |
902 | + self.patch(sys, 'platform', 'not-darwin') |
903 | + self.patch(main, 'install_platform_event_handlers', |
904 | + self._set_called) |
905 | + |
906 | + main.main([sys.argv[0], '--with-icon'], install_reactor_darwin=False) |
907 | + self.assertEqual(self._called, ((self.app,), {'quit_kills_sd': False})) |
908 | |
909 | === modified file 'ubuntuone/controlpanel/gui/qt/share_links.py' |
910 | --- ubuntuone/controlpanel/gui/qt/share_links.py 2012-10-23 19:26:30 +0000 |
911 | +++ ubuntuone/controlpanel/gui/qt/share_links.py 2012-12-07 15:05:43 +0000 |
912 | @@ -27,7 +27,10 @@ |
913 | from ubuntuone.controlpanel.gui import ( |
914 | COPY_LINK, |
915 | OPEN, |
916 | + SHARE_FILE, |
917 | + NO_RESULTS_LABEL, |
918 | SEARCH_FILES, |
919 | + SEARCH_RESULTS, |
920 | SHARED_FILES, |
921 | ) |
922 | |
923 | @@ -47,9 +50,14 @@ |
924 | logger = setup_logging('qt.share_links') |
925 | |
926 | |
927 | +# Table items index |
928 | FILE_NAME_COL = 0 |
929 | PUBLIC_LINK_COL = 1 |
930 | ACTIONS_COL = 2 |
931 | +# StackedLayout widgets index |
932 | +SHARED_FILES_INDEX = 0 |
933 | +FILE_DETAILS_INDEX = 1 |
934 | +SEARCH_RESULTS_INDEX = 2 |
935 | |
936 | |
937 | class ShareLinksPanel(UbuntuOneBin): |
938 | @@ -67,14 +75,15 @@ |
939 | self.home_dir = '' |
940 | self.ui.search_files_lbl.setText(SEARCH_FILES) |
941 | self.ui.shared_group.setTitle(SHARED_FILES) |
942 | + self.ui.btn_search.setIcon(QtGui.QIcon(":/search.png")) |
943 | + self.ui.label_no_results.setText(NO_RESULTS_LABEL) |
944 | + self.ui.label_no_results.hide() |
945 | + self.ui.page_search.layout().setAlignment(QtCore.Qt.AlignTop) |
946 | |
947 | # Set enhanced line edits |
948 | self._enhanced_line = EnhancedLineEdit(self.ui.line_search, |
949 | self._line_close_btn, icon=":/delete_search.png", |
950 | style='enhanced_borderless') |
951 | - self._enhanced_line.btn_operation.hide() |
952 | - self.ui.line_search.popup.popupHidden.connect( |
953 | - self._hide_line_btn_close_hide) |
954 | self.ui.line_search.popup.popupShown.connect( |
955 | self._hide_line_btn_close_show) |
956 | EnhancedLineEdit(self.ui.line_copy_link, self._copy_link_from_line, |
957 | @@ -82,15 +91,21 @@ |
958 | |
959 | # Set cursor type |
960 | self.ui.back_to_file_list.setCursor(QtCore.Qt.PointingHandCursor) |
961 | + self.ui.back_to_file_list_2.setCursor(QtCore.Qt.PointingHandCursor) |
962 | self.ui.open_in_browser.setCursor(QtCore.Qt.PointingHandCursor) |
963 | + self._enhanced_line.btn_operation.setCursor( |
964 | + QtCore.Qt.ArrowCursor) |
965 | |
966 | self.ui.line_search.itemSelected.connect(self.share_file) |
967 | + self.ui.line_search.filesFound.connect(self._load_search_results) |
968 | self.ui.back_to_file_list.clicked.connect(self._move_to_main_list) |
969 | + self.ui.back_to_file_list_2.clicked.connect(self._move_to_main_list) |
970 | self.ui.open_in_browser.clicked.connect(self._open_in_browser) |
971 | self.ui.tree_shared_files.itemDoubleClicked.connect( |
972 | self._open_properties_selected_file) |
973 | |
974 | self.get_public_files() |
975 | + self._enhanced_line.btn_operation.hide() |
976 | |
977 | @inlineCallbacks |
978 | def share_file(self, file_path): |
979 | @@ -118,6 +133,9 @@ |
980 | """Open the file properties on double click.""" |
981 | file_path = unicode(item.toolTip(FILE_NAME_COL)) |
982 | full_path = expand_user(file_path.encode('utf-8')).decode('utf-8') |
983 | + if self.ui.hbox_share_file.count() > 0: |
984 | + widget = self.ui.hbox_share_file.takeAt(0).widget() |
985 | + widget.close() |
986 | self.open_file_properties(file_path, full_path) |
987 | |
988 | def open_file_properties(self, file_path, full_path): |
989 | @@ -127,13 +145,13 @@ |
990 | share_file_widget.linkDisabled.connect( |
991 | lambda: self.ui.line_copy_link.setText('')) |
992 | self.ui.line_copy_link.setText(self._shared_files[full_path]) |
993 | - self.ui.stacked_widget.setCurrentIndex(1) |
994 | + self.ui.stacked_widget.setCurrentIndex(FILE_DETAILS_INDEX) |
995 | |
996 | def _file_shared(self, info): |
997 | """Receive the notification that the file has been published.""" |
998 | url = info.get("public_url") |
999 | self.ui.line_copy_link.setText(url) |
1000 | - self.ui.stacked_widget.setCurrentIndex(1) |
1001 | + self.ui.stacked_widget.setCurrentIndex(FILE_DETAILS_INDEX) |
1002 | self.is_processing = False |
1003 | |
1004 | def _open_in_browser(self): |
1005 | @@ -148,7 +166,7 @@ |
1006 | |
1007 | def _move_to_main_list(self): |
1008 | """Set the share files list as current widget.""" |
1009 | - self.ui.stacked_widget.setCurrentIndex(0) |
1010 | + self.ui.stacked_widget.setCurrentIndex(SHARED_FILES_INDEX) |
1011 | self.get_public_files() |
1012 | |
1013 | @inlineCallbacks |
1014 | @@ -190,17 +208,46 @@ |
1015 | label) |
1016 | |
1017 | actions = ActionsButtons(path, public_url, |
1018 | - self.ui.tree_shared_files) |
1019 | + parent=self.ui.tree_shared_files) |
1020 | self.ui.tree_shared_files.setItemWidget(item, ACTIONS_COL, actions) |
1021 | self.is_processing = False |
1022 | |
1023 | + def _load_search_results(self, results, prefix): |
1024 | + """Load the search results from the search box.""" |
1025 | + self.ui.tree_search_results.clear() |
1026 | + self.ui.label_search_results.setText(SEARCH_RESULTS % prefix) |
1027 | + if len(results) > 0: |
1028 | + for result in results: |
1029 | + item = QtGui.QTreeWidgetItem() |
1030 | + name = os.path.basename(result) |
1031 | + item.setText(FILE_NAME_COL, name) |
1032 | + tooltip = result |
1033 | + if tooltip.startswith(self.home_dir): |
1034 | + tooltip = tooltip.replace(self.home_dir, '~', 1) |
1035 | + item.setToolTip(FILE_NAME_COL, tooltip) |
1036 | + icon = get_system_icon_for_filename(result.encode('utf-8')) |
1037 | + item.setIcon(FILE_NAME_COL, icon) |
1038 | + self.ui.tree_search_results.setColumnWidth(FILE_NAME_COL, 300) |
1039 | + |
1040 | + item.setSizeHint(FILE_NAME_COL, QtCore.QSize(-1, 35)) |
1041 | + self.ui.tree_search_results.addTopLevelItem(item) |
1042 | + |
1043 | + actions = ActionsResultsButtons(result, self.share_file, |
1044 | + self.ui.tree_search_results) |
1045 | + self.ui.tree_search_results.setItemWidget(item, |
1046 | + PUBLIC_LINK_COL, actions) |
1047 | + self.ui.tree_search_results.show() |
1048 | + self.ui.label_no_results.hide() |
1049 | + else: |
1050 | + self.ui.tree_search_results.hide() |
1051 | + self.ui.label_no_results.show() |
1052 | + self.ui.stacked_widget.setCurrentIndex(SEARCH_RESULTS_INDEX) |
1053 | + |
1054 | def _line_close_btn(self): |
1055 | """Close button in the line edit was pressed, hide the popup.""" |
1056 | + self.ui.line_search.setText('') |
1057 | self.ui.line_search.popup.hide() |
1058 | self.ui.line_search.setFocus() |
1059 | - |
1060 | - def _hide_line_btn_close_hide(self): |
1061 | - """Hide the button inside the search line edit-""" |
1062 | self._enhanced_line.btn_operation.hide() |
1063 | |
1064 | def _hide_line_btn_close_show(self): |
1065 | @@ -211,23 +258,24 @@ |
1066 | class ActionsButtons(QtGui.QWidget): |
1067 | """Widget that contains the open and copy link actions on the list.""" |
1068 | |
1069 | - def __init__(self, path, link, parent=None): |
1070 | + def __init__(self, path, link, parent=None, both_buttons=True): |
1071 | super(ActionsButtons, self).__init__(parent) |
1072 | self.path = path |
1073 | self.link = link |
1074 | |
1075 | - hbox = QtGui.QHBoxLayout(self) |
1076 | + self.hbox = QtGui.QHBoxLayout(self) |
1077 | + self.hbox.addSpacerItem(QtGui.QSpacerItem(1, 0, |
1078 | + QtGui.QSizePolicy.Expanding)) |
1079 | + |
1080 | btn_open = QtGui.QPushButton(OPEN) |
1081 | - btn_copy = QtGui.QPushButton(COPY_LINK) |
1082 | btn_open.setObjectName('action_button') |
1083 | - btn_copy.setObjectName('action_button') |
1084 | - hbox.addSpacerItem(QtGui.QSpacerItem(1, 0, |
1085 | - QtGui.QSizePolicy.Expanding)) |
1086 | - hbox.addWidget(btn_open) |
1087 | - hbox.addWidget(btn_copy) |
1088 | - |
1089 | + self.hbox.addWidget(btn_open) |
1090 | btn_open.clicked.connect(self.open) |
1091 | - btn_copy.clicked.connect(self.copy) |
1092 | + if both_buttons: |
1093 | + btn_copy = QtGui.QPushButton(COPY_LINK) |
1094 | + btn_copy.setObjectName('action_button') |
1095 | + self.hbox.addWidget(btn_copy) |
1096 | + btn_copy.clicked.connect(self.copy) |
1097 | |
1098 | def open(self): |
1099 | """Open the file.""" |
1100 | @@ -240,6 +288,24 @@ |
1101 | app.clipboard().setText(self.link) |
1102 | |
1103 | |
1104 | +class ActionsResultsButtons(ActionsButtons): |
1105 | + """Widget that contains: open and publish actions on the results list.""" |
1106 | + |
1107 | + def __init__(self, path, publish_function=None, parent=None): |
1108 | + super(ActionsResultsButtons, self).__init__(path, '', parent, False) |
1109 | + self.publish_function = publish_function |
1110 | + |
1111 | + btn_publish = QtGui.QPushButton(SHARE_FILE) |
1112 | + btn_publish.setObjectName('action_button') |
1113 | + self.hbox.addWidget(btn_publish) |
1114 | + btn_publish.clicked.connect(self.publish_file) |
1115 | + |
1116 | + def publish_file(self): |
1117 | + """Publish the file specified.""" |
1118 | + if self.publish_function is not None: |
1119 | + self.publish_function(self.path) |
1120 | + |
1121 | + |
1122 | class EnhancedLineEdit(object): |
1123 | """Add a button inside the QLineEdit received.""" |
1124 | |
1125 | |
1126 | === modified file 'ubuntuone/controlpanel/gui/qt/share_links_search.py' |
1127 | --- ubuntuone/controlpanel/gui/qt/share_links_search.py 2012-10-29 14:00:16 +0000 |
1128 | +++ ubuntuone/controlpanel/gui/qt/share_links_search.py 2012-12-07 15:05:43 +0000 |
1129 | @@ -25,10 +25,17 @@ |
1130 | |
1131 | from ubuntuone.controlpanel import cache |
1132 | from ubuntuone.controlpanel.logger import setup_logging |
1133 | +from ubuntuone.controlpanel.gui import ( |
1134 | + NO_RESULTS, |
1135 | + SEARCH_FOR, |
1136 | + SHARE_THESE_FILES, |
1137 | +) |
1138 | |
1139 | logger = setup_logging('qt.share_links_search') |
1140 | |
1141 | HOME_DIR = '' |
1142 | +AVOID_SECOND_ITEM = 2 |
1143 | +NORMAL_INCREMENT = 1 |
1144 | |
1145 | |
1146 | def get_system_icon_for_filename(file_path): |
1147 | @@ -45,6 +52,7 @@ |
1148 | """Search widget for the synced files.""" |
1149 | |
1150 | itemSelected = QtCore.pyqtSignal(unicode) |
1151 | + filesFound = QtCore.pyqtSignal(list, unicode) |
1152 | |
1153 | def __init__(self, parent=None): |
1154 | super(SearchBox, self).__init__(parent) |
1155 | @@ -56,9 +64,6 @@ |
1156 | self.items_step = 20 |
1157 | self.prefix = '' |
1158 | self._thread_explore = None |
1159 | - self._pre_key_event = { |
1160 | - QtCore.Qt.Key_Space: self._key_space_pressed, |
1161 | - } |
1162 | self._post_key_event = { |
1163 | QtCore.Qt.Key_Escape: lambda *args: self.popup.hide(), |
1164 | QtCore.Qt.Key_Down: self._key_down_pressed, |
1165 | @@ -112,7 +117,7 @@ |
1166 | self.temp_u1_files = [filename for filename in self.temp_u1_files |
1167 | if os.path.basename(filename).find(self.prefix) > -1] |
1168 | files = self._get_filtered_list(self.temp_u1_files) |
1169 | - self.popup.load_items(files) |
1170 | + self.popup.load_items(files, self.prefix) |
1171 | |
1172 | def _get_filtered_list(self, filenames): |
1173 | """Get pages of results.""" |
1174 | @@ -121,34 +126,28 @@ |
1175 | files = [filename for filename in filenames[begin:self.items_per_page]] |
1176 | return files |
1177 | |
1178 | - def _key_space_pressed(self): |
1179 | - """The user pressed the space key.""" |
1180 | - item = self.popup.list_widget.currentItem() |
1181 | - if item is None: |
1182 | - return False |
1183 | - widget = self.popup.list_widget.itemWidget(item) |
1184 | - self.setText(widget.name) |
1185 | - self.popup.hide() |
1186 | - return True |
1187 | - |
1188 | - def _key_down_pressed(self, current): |
1189 | + def _key_down_pressed(self, focus_index): |
1190 | """The user pressed the down key.""" |
1191 | #While the current position is lower that the list size go to next |
1192 | - if current != self.popup.list_widget.count() - 1: |
1193 | + if focus_index != self.popup.list_widget.count() - 1: |
1194 | + list_offset = (AVOID_SECOND_ITEM if focus_index == 0 else |
1195 | + NORMAL_INCREMENT) |
1196 | self.popup.list_widget.setCurrentRow( |
1197 | - self.popup.list_widget.currentRow() + 1) |
1198 | + self.popup.list_widget.currentRow() + list_offset) |
1199 | #If the current position is greater than the amount of items in |
1200 | #the list - 6, then try to fetch more items in the list. |
1201 | - if current >= (self.popup.list_widget.count() - 6): |
1202 | + if focus_index >= (self.popup.list_widget.count() - 6): |
1203 | filenames = self._get_filtered_list(self.temp_u1_files) |
1204 | self.popup.fetch_more(filenames) |
1205 | |
1206 | - def _key_up_pressed(self, current): |
1207 | + def _key_up_pressed(self, focus_index): |
1208 | """The user pressed the up key.""" |
1209 | #while the current position is greater than 0, go to previous |
1210 | - if current > 0: |
1211 | + if focus_index > 0: |
1212 | + list_offset = (AVOID_SECOND_ITEM if focus_index == 2 else |
1213 | + NORMAL_INCREMENT) |
1214 | self.popup.list_widget.setCurrentRow( |
1215 | - self.popup.list_widget.currentRow() - 1) |
1216 | + self.popup.list_widget.currentRow() - list_offset) |
1217 | |
1218 | def _key_return_pressed(self, current): |
1219 | """The user pressed the return key.""" |
1220 | @@ -158,9 +157,6 @@ |
1221 | |
1222 | def keyPressEvent(self, event): |
1223 | """Process the different behaviour for the keyPress event.""" |
1224 | - if self._pre_key_event.get(event.key(), lambda: False)(): |
1225 | - return |
1226 | - |
1227 | super(SearchBox, self).keyPressEvent(event) |
1228 | current = self.popup.list_widget.currentRow() |
1229 | self._post_key_event.get(event.key(), lambda *args: None)(current) |
1230 | @@ -173,8 +169,11 @@ |
1231 | def _set_selected_item(self, item): |
1232 | """Notify of the selected item.""" |
1233 | widget = self.popup.list_widget.itemWidget(item) |
1234 | - self.itemSelected.emit(widget.file_path) |
1235 | - self.setText('') |
1236 | + if widget is not None: |
1237 | + self.itemSelected.emit(widget.file_path) |
1238 | + self.setText('') |
1239 | + else: |
1240 | + self.filesFound.emit(self.temp_u1_files, self.prefix) |
1241 | self.popup.hide() |
1242 | |
1243 | def moveEvent(self, event): |
1244 | @@ -242,14 +241,36 @@ |
1245 | """Set the proper style for the current and previous items.""" |
1246 | if current is not None: |
1247 | widget = self.list_widget.itemWidget(current) |
1248 | - widget.set_selected() |
1249 | + if widget is not None: |
1250 | + widget.set_selected() |
1251 | if previous is not None: |
1252 | widget = self.list_widget.itemWidget(previous) |
1253 | - widget.set_not_selected() |
1254 | - |
1255 | - def load_items(self, file_items): |
1256 | + if widget is not None: |
1257 | + widget.set_not_selected() |
1258 | + |
1259 | + def _add_special_item(self, text): |
1260 | + """Add special items to the popup.""" |
1261 | + item = QtGui.QListWidgetItem(text) |
1262 | + font = item.font() |
1263 | + font.setBold(True) |
1264 | + item.setSizeHint(QtCore.QSize(20, 30)) |
1265 | + item.setBackground(QtGui.QBrush(QtCore.Qt.lightGray)) |
1266 | + item.setForeground(QtGui.QBrush(QtCore.Qt.black)) |
1267 | + item.setFont(font) |
1268 | + return item |
1269 | + |
1270 | + def load_items(self, file_items, search_text): |
1271 | """Load the initial items.""" |
1272 | self.list_widget.clear() |
1273 | + # Search item |
1274 | + self.list_widget.addItem(self._add_special_item( |
1275 | + SEARCH_FOR % search_text)) |
1276 | + # Message item |
1277 | + if len(file_items) > 0: |
1278 | + message_publish = self._add_special_item(SHARE_THESE_FILES) |
1279 | + else: |
1280 | + message_publish = self._add_special_item(NO_RESULTS) |
1281 | + self.list_widget.addItem(message_publish) |
1282 | for file_ in file_items: |
1283 | item = QtGui.QListWidgetItem("\n") |
1284 | file_widget = FileItem(file_) |
1285 | @@ -257,8 +278,7 @@ |
1286 | self.list_widget.setItemWidget(item, file_widget) |
1287 | icon = get_system_icon_for_filename(file_.encode('utf-8')) |
1288 | item.setIcon(icon) |
1289 | - if file_items: |
1290 | - self.list_widget.setCurrentRow(0) |
1291 | + self.list_widget.setCurrentRow(0) |
1292 | |
1293 | def fetch_more(self, file_items): |
1294 | """Add more items to the list on user scroll.""" |
1295 | |
1296 | === modified file 'ubuntuone/controlpanel/gui/qt/systray.py' |
1297 | --- ubuntuone/controlpanel/gui/qt/systray.py 2012-09-05 19:43:16 +0000 |
1298 | +++ ubuntuone/controlpanel/gui/qt/systray.py 2012-12-07 15:05:43 +0000 |
1299 | @@ -36,12 +36,12 @@ |
1300 | IN_PROGRESS, |
1301 | IN_PROGRESS_FILE, |
1302 | LOADING, |
1303 | - NEW_SHARE_BY, |
1304 | OPEN_UBUNTU_ONE, |
1305 | OPEN_UBUNTU_ONE_FOLDER, |
1306 | PLEASE_WAIT, |
1307 | RECENT_TRANSFERS, |
1308 | RECENTTRANSFERS, |
1309 | + SHARE_A_FILE, |
1310 | TRANSFERS, |
1311 | UPLOADING, |
1312 | ) |
1313 | @@ -122,6 +122,7 @@ |
1314 | self.quit = None |
1315 | self.get_help_online = None |
1316 | self.open_u1 = None |
1317 | + self.share_a_file = None |
1318 | self.status_action = None |
1319 | self.go_to_web = None |
1320 | self.get_more_storage = None |
1321 | @@ -130,75 +131,48 @@ |
1322 | self.backend.add_status_changed_handler(self._process_status) |
1323 | |
1324 | self.load_menu() |
1325 | - # Refresh the Shares every five minutes if needed. |
1326 | - self._timer_id = self.startTimer(300000) |
1327 | - |
1328 | - # pylint: disable=C0103 |
1329 | - |
1330 | - def timerEvent(self, event): |
1331 | - """Update the menu on each iteration.""" |
1332 | - self.load_menu() |
1333 | - |
1334 | - # pylint: enable=C0103 |
1335 | - |
1336 | - @inlineCallbacks |
1337 | + |
1338 | def load_menu(self): |
1339 | """Load the content of the menu.""" |
1340 | - shares_info = yield self.backend.get_shares() |
1341 | - shares_info = [share for share in shares_info if not share['accepted']] |
1342 | - if shares_info != self._previous_shares: |
1343 | - # Items |
1344 | - self.context_menu.clear() |
1345 | - |
1346 | - self.status = self.context_menu.addAction(LOADING) |
1347 | - self.status.setEnabled(False) |
1348 | - self.status_action = self.context_menu.addAction(PLEASE_WAIT) |
1349 | - self.refresh_status() |
1350 | - self.context_menu.addSeparator() |
1351 | - |
1352 | - self.open_u1 = self.context_menu.addAction(OPEN_UBUNTU_ONE) |
1353 | - # TODO: Share a file action when the Shares tab is ready in U1-CP |
1354 | - self.open_u1_folder = self.context_menu.addAction( |
1355 | - OPEN_UBUNTU_ONE_FOLDER) |
1356 | - self.go_to_web = self.context_menu.addAction(GO_TO_WEB) |
1357 | - self.context_menu.addSeparator() |
1358 | - |
1359 | - # Shares |
1360 | - self._previous_shares = shares_info |
1361 | - max_shares = 0 |
1362 | - for share in self._previous_shares: |
1363 | - if max_shares == 3: |
1364 | - break |
1365 | - max_shares += 1 |
1366 | - text = NEW_SHARE_BY % share['other_visible_name'] |
1367 | - share_action = SharesAction(text, share['volume_id'], |
1368 | - self.context_menu) |
1369 | - self.context_menu.addAction(share_action) |
1370 | - if self._previous_shares: |
1371 | - self.context_menu.addSeparator() |
1372 | - |
1373 | - # Transfers |
1374 | - self.transfers = TransfersMenu(self) |
1375 | - self.context_menu.addMenu(self.transfers) |
1376 | - |
1377 | - self.get_more_storage = self.context_menu.addAction( |
1378 | - GET_MORE_STORAGE) |
1379 | - self.get_help_online = self.context_menu.addAction(GET_HELP_ONLINE) |
1380 | - self.quit = self.context_menu.addAction("Quit") |
1381 | - |
1382 | - self.setContextMenu(self.context_menu) |
1383 | - |
1384 | - # Connect Signals |
1385 | - self.status_action.triggered.connect(self.change_status) |
1386 | - self.open_u1.triggered.connect(self.restore_window) |
1387 | - self.open_u1_folder.triggered.connect(self.open_u1_folder_action) |
1388 | - self.get_more_storage.triggered.connect( |
1389 | - self.get_more_storage_action) |
1390 | - self.go_to_web.triggered.connect(self.go_to_web_action) |
1391 | - self.get_help_online.triggered.connect(self.get_help_action) |
1392 | - self.quit.triggered.connect(self.stop) |
1393 | - |
1394 | - self._get_volumes_info() |
1395 | + # Items |
1396 | + self.context_menu.clear() |
1397 | + |
1398 | + self.status = self.context_menu.addAction(LOADING) |
1399 | + self.status.setEnabled(False) |
1400 | + self.status_action = self.context_menu.addAction(PLEASE_WAIT) |
1401 | + self.refresh_status() |
1402 | + self.context_menu.addSeparator() |
1403 | + |
1404 | + self.open_u1 = self.context_menu.addAction(OPEN_UBUNTU_ONE) |
1405 | + self.share_a_file = self.context_menu.addAction(SHARE_A_FILE) |
1406 | + self.open_u1_folder = self.context_menu.addAction( |
1407 | + OPEN_UBUNTU_ONE_FOLDER) |
1408 | + self.go_to_web = self.context_menu.addAction(GO_TO_WEB) |
1409 | + self.context_menu.addSeparator() |
1410 | + |
1411 | + # Transfers |
1412 | + self.transfers = TransfersMenu(self) |
1413 | + self.context_menu.addMenu(self.transfers) |
1414 | + |
1415 | + self.get_more_storage = self.context_menu.addAction( |
1416 | + GET_MORE_STORAGE) |
1417 | + self.get_help_online = self.context_menu.addAction(GET_HELP_ONLINE) |
1418 | + self.quit = self.context_menu.addAction("Quit") |
1419 | + |
1420 | + self.setContextMenu(self.context_menu) |
1421 | + |
1422 | + # Connect Signals |
1423 | + self.status_action.triggered.connect(self.change_status) |
1424 | + self.open_u1.triggered.connect(self.restore_window) |
1425 | + self.share_a_file.triggered.connect(self.open_share_tab) |
1426 | + self.open_u1_folder.triggered.connect(self.open_u1_folder_action) |
1427 | + self.get_more_storage.triggered.connect( |
1428 | + self.get_more_storage_action) |
1429 | + self.go_to_web.triggered.connect(self.go_to_web_action) |
1430 | + self.get_help_online.triggered.connect(self.get_help_action) |
1431 | + self.quit.triggered.connect(self.stop) |
1432 | + |
1433 | + self._get_volumes_info() |
1434 | |
1435 | @inlineCallbacks |
1436 | def refresh_status(self): |
1437 | @@ -278,6 +252,17 @@ |
1438 | self.window.show() |
1439 | self.window.activateWindow() |
1440 | |
1441 | + def open_share_tab(self): |
1442 | + """Show the main window in the share tab.""" |
1443 | + if self.window is None: |
1444 | + # pylint: disable=W0404 |
1445 | + from ubuntuone.controlpanel.gui.qt.gui import MainWindow |
1446 | + # pylint: enable=W0404 |
1447 | + self.window = MainWindow(close_callback=self.delete_window) |
1448 | + self.window.switch_to('share_links') |
1449 | + self.window.show() |
1450 | + self.window.activateWindow() |
1451 | + |
1452 | def delete_window(self): |
1453 | """Close and remove the main window.""" |
1454 | if self.window is not None: |
1455 | |
1456 | === modified file 'ubuntuone/controlpanel/gui/qt/tests/test_controlpanel.py' |
1457 | --- ubuntuone/controlpanel/gui/qt/tests/test_controlpanel.py 2012-10-08 18:11:55 +0000 |
1458 | +++ ubuntuone/controlpanel/gui/qt/tests/test_controlpanel.py 2012-12-07 15:05:43 +0000 |
1459 | @@ -91,10 +91,9 @@ |
1460 | self.assertEqual(self._called, ((), {})) |
1461 | |
1462 | def test_on_credentials_found(self): |
1463 | - """The management panel is shown.""" |
1464 | + """File sync is connected and the management panel is shown.""" |
1465 | self.patch(self.ui, 'connect_file_sync', self._set_called) |
1466 | - self.ui.on_credentials_not_found() |
1467 | - self.ui.on_credentials_found() |
1468 | + yield self.ui.on_credentials_found() |
1469 | |
1470 | self.assertIs(self.ui.ui.switcher.currentWidget(), |
1471 | self.ui.ui.management) |
1472 | @@ -154,7 +153,7 @@ |
1473 | |
1474 | def test_on_wizard_finished(self): |
1475 | """When the wizard is finished, the management panel is shown.""" |
1476 | - self.patch(self.ui, 'on_credentials_found', self._set_called) |
1477 | + self.patch(self.ui, 'show_management_ui', self._set_called) |
1478 | self.ui.ui.wizard.finished.emit(1) |
1479 | |
1480 | self.assertEqual(self._called, ((), {})) |
1481 | |
1482 | === modified file 'ubuntuone/controlpanel/gui/qt/tests/test_folders.py' |
1483 | --- ubuntuone/controlpanel/gui/qt/tests/test_folders.py 2012-08-07 01:37:10 +0000 |
1484 | +++ ubuntuone/controlpanel/gui/qt/tests/test_folders.py 2012-12-07 15:05:43 +0000 |
1485 | @@ -54,9 +54,9 @@ |
1486 | # pylint: disable=W0212, E1103 |
1487 | |
1488 | DEFAULT_FOLDERS = [u'Music', u'Configuración', 'An invalid folder', |
1489 | - os.path.join(u'I ♥ Ubuntu', u'Documents'), |
1490 | - os.path.join(u'I ♥ Ubuntu', u'Something', u'Long'), |
1491 | -] |
1492 | + os.path.join(u'I ♥ Ubuntu', u'Documents'), |
1493 | + os.path.join(u'I ♥ Ubuntu', u'Something', u'Long'), |
1494 | + ] |
1495 | |
1496 | |
1497 | def volumes_with_music_unsubscribed(): |
1498 | @@ -1133,8 +1133,8 @@ |
1499 | volumes = sorted(volumes, key=operator.itemgetter('path')) |
1500 | |
1501 | for volume in volumes: |
1502 | - if (volume['type'] != self.ui.backend.FOLDER_TYPE or |
1503 | - not bool(volume['subscribed'])): |
1504 | + if volume['type'] != self.ui.backend.FOLDER_TYPE or \ |
1505 | + not bool(volume['subscribed']): |
1506 | # non-folders or unsubscribed should not appear in the tree |
1507 | continue |
1508 | self.assert_folder_added(volume['path'], |
1509 | @@ -1319,6 +1319,7 @@ |
1510 | yield self.changes_applied |
1511 | self.assertEqual(self.ui.backend._called, {}) |
1512 | |
1513 | + @defer.inlineCallbacks |
1514 | def test_timer_is_stopped(self): |
1515 | """When applying changes, timer is stopped.""" |
1516 | yield self.ui.apply_changes() |
1517 | |
1518 | === modified file 'ubuntuone/controlpanel/gui/qt/tests/test_share_links.py' |
1519 | --- ubuntuone/controlpanel/gui/qt/tests/test_share_links.py 2012-10-23 14:24:16 +0000 |
1520 | +++ ubuntuone/controlpanel/gui/qt/tests/test_share_links.py 2012-12-07 15:05:43 +0000 |
1521 | @@ -57,6 +57,7 @@ |
1522 | QtCore.Qt.PointingHandCursor) |
1523 | self.assertEqual(self.ui.ui.back_to_file_list.cursor().shape(), |
1524 | QtCore.Qt.PointingHandCursor) |
1525 | + self.assertFalse(self.ui._enhanced_line.btn_operation.isVisible()) |
1526 | |
1527 | def test_share_file(self): |
1528 | """Check that the state of the widgets on share_file.""" |
1529 | @@ -76,6 +77,19 @@ |
1530 | self.assertEqual(len(data), 2) |
1531 | self.assertEqual(data[0], self.ui._file_shared) |
1532 | |
1533 | + def test_open_file_properties(self): |
1534 | + """Check that we clean the Details Page before adding a new item.""" |
1535 | + publicfiles = [ |
1536 | + {'path': '/home/file1', 'public_url': 'http:ubuntuone.com/asd123'}, |
1537 | + {'path': '/home/file2', 'public_url': 'http:ubuntuone.com/qwe456'}, |
1538 | + ] |
1539 | + self.ui._load_public_files(publicfiles) |
1540 | + item = self.ui.ui.tree_shared_files.topLevelItem(0) |
1541 | + self.ui.ui.tree_shared_files.itemDoubleClicked.emit(item, 0) |
1542 | + self.assertEqual(self.ui.ui.hbox_share_file.count(), 1) |
1543 | + self.ui.ui.tree_shared_files.itemDoubleClicked.emit(item, 0) |
1544 | + self.assertEqual(self.ui.ui.hbox_share_file.count(), 1) |
1545 | + |
1546 | def test_share_file_actions(self): |
1547 | """Check the behaviour of share_file buttons.""" |
1548 | path = '/home/user/Ubuntu One/file1.txt' |
1549 | @@ -91,7 +105,8 @@ |
1550 | info = {'public_url': 'http://ubuntuone.com/asd123'} |
1551 | self.ui._file_shared(info) |
1552 | self.assertEqual(self.ui.ui.line_copy_link.text(), info['public_url']) |
1553 | - self.assertEqual(self.ui.ui.stacked_widget.currentIndex(), 1) |
1554 | + self.assertEqual(self.ui.ui.stacked_widget.currentIndex(), |
1555 | + gui.FILE_DETAILS_INDEX) |
1556 | self.assertFalse(self.ui.is_processing) |
1557 | |
1558 | def test_file_already_shared(self): |
1559 | @@ -109,7 +124,8 @@ |
1560 | self.ui._shared_files = shared |
1561 | self.ui.share_file(path) |
1562 | self.assertEqual(self.ui.ui.line_copy_link.text(), shared[path]) |
1563 | - self.assertEqual(self.ui.ui.stacked_widget.currentIndex(), 1) |
1564 | + self.assertEqual(self.ui.ui.stacked_widget.currentIndex(), |
1565 | + gui.FILE_DETAILS_INDEX) |
1566 | self.assertEqual(data, []) |
1567 | |
1568 | def test_open_in_browser(self): |
1569 | @@ -133,7 +149,8 @@ |
1570 | def test_move_to_main_list(self): |
1571 | """Test that the stacked widget shows the proper index.""" |
1572 | self.ui._move_to_main_list() |
1573 | - self.assertEqual(self.ui.ui.stacked_widget.currentIndex(), 0) |
1574 | + self.assertEqual(self.ui.ui.stacked_widget.currentIndex(), |
1575 | + gui.SHARED_FILES_INDEX) |
1576 | |
1577 | def test_get_public_files(self): |
1578 | """Test that the proper actions are executed on files requested..""" |
1579 | @@ -142,7 +159,8 @@ |
1580 | data.append) |
1581 | self.ui.get_public_files() |
1582 | self.assertTrue(self.ui.is_processing) |
1583 | - self.assertEqual(self.ui.ui.stacked_widget.currentIndex(), 0) |
1584 | + self.assertEqual(self.ui.ui.stacked_widget.currentIndex(), |
1585 | + gui.SHARED_FILES_INDEX) |
1586 | self.assertEqual(self.ui.home_dir, USER_HOME) |
1587 | self.assertEqual(len(data), 1) |
1588 | self.assertEqual(data[0], self.ui._load_public_files) |
1589 | @@ -153,11 +171,6 @@ |
1590 | self.addCleanup(self.ui.ui.line_search.popup.hide) |
1591 | self.ui._line_close_btn() |
1592 | self.assertFalse(self.ui.ui.line_search.popup.isVisible()) |
1593 | - |
1594 | - def test_hide_line_btn_close_hide(self): |
1595 | - """Check the state of the inline button.""" |
1596 | - self.ui._enhanced_line.btn_operation.show() |
1597 | - self.ui.ui.line_search.popup.popupHidden.emit() |
1598 | self.assertFalse(self.ui._enhanced_line.btn_operation.isVisible()) |
1599 | |
1600 | def test_hide_line_btn_close_show(self): |
1601 | @@ -194,6 +207,35 @@ |
1602 | actions = self.ui.ui.tree_shared_files.itemWidget(item, 2) |
1603 | self.assertIsInstance(actions, gui.ActionsButtons) |
1604 | |
1605 | + def test_load_search_results(self): |
1606 | + """Test if the list of public files is loaded properly.""" |
1607 | + results = ['/home/file1', '/home/file2'] |
1608 | + self.ui._load_search_results(results, 'file') |
1609 | + item = self.ui.ui.tree_search_results.topLevelItem(0) |
1610 | + self.assertEqual(item.text(0), os.path.basename('/home/file1')) |
1611 | + self.assertEqual(item.toolTip(0), '/home/file1') |
1612 | + actions = self.ui.ui.tree_search_results.itemWidget(item, 1) |
1613 | + self.assertIsInstance(actions, gui.ActionsResultsButtons) |
1614 | + |
1615 | + item = self.ui.ui.tree_search_results.topLevelItem(1) |
1616 | + self.assertEqual(item.text(0), os.path.basename('/home/file2')) |
1617 | + self.assertEqual(item.toolTip(0), '/home/file2') |
1618 | + actions = self.ui.ui.tree_search_results.itemWidget(item, 1) |
1619 | + self.assertIsInstance(actions, gui.ActionsResultsButtons) |
1620 | + |
1621 | + self.assertEqual(self.ui.ui.label_search_results.text(), |
1622 | + gui.SEARCH_RESULTS % 'file') |
1623 | + self.assertTrue(self.ui.ui.tree_search_results.isVisible()) |
1624 | + self.assertFalse(self.ui.ui.label_no_results.isVisible()) |
1625 | + |
1626 | + def test_load_search_results_empty_list(self): |
1627 | + """Test if the list of public files is loaded properly.""" |
1628 | + self.ui._load_search_results([], 'file') |
1629 | + self.assertEqual(self.ui.ui.label_search_results.text(), |
1630 | + gui.SEARCH_RESULTS % 'file') |
1631 | + self.assertFalse(self.ui.ui.tree_search_results.isVisible()) |
1632 | + self.assertTrue(self.ui.ui.label_no_results.isVisible()) |
1633 | + |
1634 | def test_tree_item_clicked(self): |
1635 | """Check if the proper info is displayed when the item is clicked.""" |
1636 | publicfiles = [ |
1637 | |
1638 | === modified file 'ubuntuone/controlpanel/gui/qt/tests/test_share_links_search.py' |
1639 | --- ubuntuone/controlpanel/gui/qt/tests/test_share_links_search.py 2012-10-29 18:30:31 +0000 |
1640 | +++ ubuntuone/controlpanel/gui/qt/tests/test_share_links_search.py 2012-12-07 15:05:43 +0000 |
1641 | @@ -87,7 +87,7 @@ |
1642 | self.ui.temp_u1_files = self.ui.backend.search_files('') |
1643 | self.ui.temp_u1_files = self.ui.temp_u1_files * 4 |
1644 | self.ui._load_items() |
1645 | - self.assertEqual(self.ui.popup.list_widget.count(), 20) |
1646 | + self.assertEqual(self.ui.popup.list_widget.count(), 22) |
1647 | self.ui.popup.list_widget.setCurrentRow( |
1648 | self.ui.popup.list_widget.count() - 5) |
1649 | current = self.ui.popup.list_widget.currentRow() |
1650 | @@ -120,7 +120,7 @@ |
1651 | self.ui.popup.setVisible(True) |
1652 | self.ui.temp_u1_files = self.ui.backend.search_files('') |
1653 | self.ui._load_items() |
1654 | - self.ui.popup.list_widget.setCurrentRow(1) |
1655 | + self.ui.popup.list_widget.setCurrentRow(3) |
1656 | current = self.ui.popup.list_widget.currentRow() |
1657 | self.ui.itemSelected.connect(self.fake_slot) |
1658 | self.ui._key_return_pressed(current) |
1659 | @@ -132,7 +132,7 @@ |
1660 | self.ui.temp_u1_files = self.ui.backend.search_files('') |
1661 | self.ui.popup.setVisible(True) |
1662 | self.ui._load_items() |
1663 | - self.ui.popup.list_widget.setCurrentRow(1) |
1664 | + self.ui.popup.list_widget.setCurrentRow(3) |
1665 | current = self.ui.popup.list_widget.currentItem() |
1666 | self.ui.itemSelected.connect(self.fake_slot) |
1667 | self.ui.popup.list_widget.itemPressed.emit(current) |
1668 | @@ -150,29 +150,6 @@ |
1669 | expected = (([],), {}) |
1670 | self.assertEqual(expected, self._called) |
1671 | |
1672 | - def test_key_space_pressed(self): |
1673 | - """Check the proper actions are executed on key space pressed.""" |
1674 | - data = [] |
1675 | - |
1676 | - def fake_set_text(text): |
1677 | - """fake setText.""" |
1678 | - data.append(text) |
1679 | - |
1680 | - self.patch(self.ui, "setText", fake_set_text) |
1681 | - self.ui.popup.setVisible(True) |
1682 | - self.ui.temp_u1_files = self.ui.backend.search_files('') |
1683 | - self.ui._load_items() |
1684 | - self.ui.popup.list_widget.setCurrentRow(2) |
1685 | - |
1686 | - self.ui._key_space_pressed() |
1687 | - expected = [os.path.basename(self.files[2])] |
1688 | - self.assertEqual(expected, data) |
1689 | - |
1690 | - def test_key_space_pressed_without_selection(self): |
1691 | - """Check the proper actions are executed on key space pressed.""" |
1692 | - self.ui.popup.list_widget.setCurrentItem(None) |
1693 | - self.assertFalse(self.ui._key_space_pressed()) |
1694 | - |
1695 | def test_process_volumes_info(self): |
1696 | """Check that _process_volumes_info obtain the proper info.""" |
1697 | self.ui.temp_u1_files = self.ui.backend.search_files('') |
1698 | @@ -182,7 +159,7 @@ |
1699 | expected = self.files |
1700 | expected.sort() |
1701 | self.assertEqual(self.ui.temp_u1_files, expected) |
1702 | - self.assertEqual(self.ui.popup.list_widget.count(), 6) |
1703 | + self.assertEqual(self.ui.popup.list_widget.count(), 8) |
1704 | |
1705 | def test_filter(self): |
1706 | """Check the results of the filter.""" |
1707 | @@ -212,7 +189,7 @@ |
1708 | self.patch(self.ui.popup, "isVisible", lambda: True) |
1709 | self.ui.temp_u1_files = self.ui.backend.search_files('') |
1710 | self.ui._load_items() |
1711 | - self.ui.popup.list_widget.setCurrentRow(0) |
1712 | + self.ui.popup.list_widget.setCurrentRow(2) |
1713 | item = self.ui.popup.list_widget.currentItem() |
1714 | self.ui._set_selected_item(item) |
1715 | self.assertEqual(self._called, |
1716 | @@ -280,11 +257,11 @@ |
1717 | ] |
1718 | |
1719 | self.assertEqual(self.ui.list_widget.count(), 0) |
1720 | - self.ui.load_items(items) |
1721 | - self.assertEqual(self.ui.list_widget.count(), 3) |
1722 | + self.ui.load_items(items, '') |
1723 | + self.assertEqual(self.ui.list_widget.count(), 5) |
1724 | # Check that we erase the list on reload |
1725 | - self.ui.load_items(items) |
1726 | - self.assertEqual(self.ui.list_widget.count(), 3) |
1727 | + self.ui.load_items(items, '') |
1728 | + self.assertEqual(self.ui.list_widget.count(), 5) |
1729 | |
1730 | def test_fetch_more(self): |
1731 | """Tests that the items are loaded properly.""" |
1732 | @@ -295,10 +272,10 @@ |
1733 | ] |
1734 | |
1735 | self.assertEqual(self.ui.list_widget.count(), 0) |
1736 | - self.ui.load_items(items) |
1737 | - self.assertEqual(self.ui.list_widget.count(), 3) |
1738 | + self.ui.load_items(items, '') |
1739 | + self.assertEqual(self.ui.list_widget.count(), 5) |
1740 | self.ui.fetch_more(items) |
1741 | - self.assertEqual(self.ui.list_widget.count(), 6) |
1742 | + self.assertEqual(self.ui.list_widget.count(), 8) |
1743 | |
1744 | def test_repaint_items(self): |
1745 | """Check the style of the items change acording to the selection.""" |
1746 | @@ -308,10 +285,11 @@ |
1747 | '/home/tester/file3', |
1748 | ] |
1749 | |
1750 | - self.ui.load_items(items) |
1751 | - current = self.ui.list_widget.item(0) |
1752 | + self.ui.load_items(items, '') |
1753 | + self.ui.list_widget.setCurrentRow(2) |
1754 | + current = self.ui.list_widget.item(2) |
1755 | widget = self.ui.list_widget.itemWidget(current) |
1756 | - next_ = self.ui.list_widget.item(1) |
1757 | + next_ = self.ui.list_widget.item(3) |
1758 | widget2 = self.ui.list_widget.itemWidget(next_) |
1759 | name = os.path.basename('/home/tester/file1') |
1760 | style = self.text_style.format(name, '/home/tester/file1', |
1761 | @@ -322,10 +300,10 @@ |
1762 | self.assertEqual(widget.text(), style) |
1763 | self.assertEqual(widget2.text(), style2) |
1764 | |
1765 | - self.ui.list_widget.setCurrentRow(1) |
1766 | - current = self.ui.list_widget.item(1) |
1767 | + self.ui.list_widget.setCurrentRow(3) |
1768 | + current = self.ui.list_widget.item(3) |
1769 | widget = self.ui.list_widget.itemWidget(current) |
1770 | - previous = self.ui.list_widget.item(0) |
1771 | + previous = self.ui.list_widget.item(2) |
1772 | widget2 = self.ui.list_widget.itemWidget(previous) |
1773 | name = os.path.basename('/home/tester/file2') |
1774 | style = self.text_style.format(name, '/home/tester/file2', |
1775 | @@ -335,3 +313,31 @@ |
1776 | '#333333', 'grey') |
1777 | self.assertEqual(widget.text(), style) |
1778 | self.assertEqual(widget2.text(), style2) |
1779 | + |
1780 | + def test_first_list_item(self): |
1781 | + """Check that the first item in the popup has the proper string.""" |
1782 | + self.ui.load_items([], 'file') |
1783 | + current = self.ui.list_widget.item(0) |
1784 | + text = current.text() |
1785 | + expected = gui.SEARCH_FOR % 'file' |
1786 | + self.assertEqual(expected, text) |
1787 | + |
1788 | + def test_second_list_item_for_empty_list(self): |
1789 | + """Check that the second item in the popup has the proper string.""" |
1790 | + self.ui.load_items([], 'file') |
1791 | + current = self.ui.list_widget.item(1) |
1792 | + text = current.text() |
1793 | + self.assertEqual(gui.NO_RESULTS, text) |
1794 | + |
1795 | + def test_second_list_item(self): |
1796 | + """Check that the second item in the popup has the proper string.""" |
1797 | + items = [ |
1798 | + '/home/tester/file1', |
1799 | + '/home/tester/file2', |
1800 | + '/home/tester/file3', |
1801 | + ] |
1802 | + |
1803 | + self.ui.load_items(items, 'file') |
1804 | + current = self.ui.list_widget.item(1) |
1805 | + text = current.text() |
1806 | + self.assertEqual(gui.SHARE_THESE_FILES, text) |
1807 | |
1808 | === modified file 'ubuntuone/controlpanel/gui/qt/tests/test_start.py' |
1809 | --- ubuntuone/controlpanel/gui/qt/tests/test_start.py 2012-06-28 05:32:24 +0000 |
1810 | +++ ubuntuone/controlpanel/gui/qt/tests/test_start.py 2012-12-07 15:05:43 +0000 |
1811 | @@ -36,6 +36,7 @@ |
1812 | def __init__(self): |
1813 | self.args = [] |
1814 | self.updates_checked = defer.Deferred() |
1815 | + self.app = None |
1816 | |
1817 | def __call__(self, *args, **kwargs): |
1818 | self.args.append((args, kwargs)) |
1819 | @@ -57,6 +58,10 @@ |
1820 | self.updates_checked.callback(None) |
1821 | return self.updates_checked |
1822 | |
1823 | + def connect_app_signal(self, app): |
1824 | + """Fake connect_app_signal.""" |
1825 | + self.app = app |
1826 | + |
1827 | |
1828 | class StartTestCase(TestCase): |
1829 | """Test the qt control panel.""" |
1830 | @@ -124,3 +129,9 @@ |
1831 | # a timeout in this test means that the check_updates method |
1832 | # was not called |
1833 | yield self.main_window.updates_checked |
1834 | + |
1835 | + def test_connect_app_signals_is_called(self): |
1836 | + """Check that connect_app_signal is properly called on start.""" |
1837 | + gui.start(close_callback=self.close_cb) |
1838 | + app = gui.QtGui.QApplication.instance() |
1839 | + self.assertEqual(self.main_window.app, app) |
1840 | |
1841 | === modified file 'ubuntuone/controlpanel/gui/qt/tests/test_systray.py' |
1842 | --- ubuntuone/controlpanel/gui/qt/tests/test_systray.py 2012-09-05 22:58:33 +0000 |
1843 | +++ ubuntuone/controlpanel/gui/qt/tests/test_systray.py 2012-12-07 15:05:43 +0000 |
1844 | @@ -60,6 +60,11 @@ |
1845 | def __init__(self, *args, **kwargs): |
1846 | self.args = (args, kwargs) |
1847 | super(FakeMainWindow, self).__init__() |
1848 | + self.tabname = '' |
1849 | + |
1850 | + def switch_to(self, tabname): |
1851 | + """Fake switch_to.""" |
1852 | + self.tabname = tabname |
1853 | |
1854 | |
1855 | class SystrayTestCase(BaseTestCase): |
1856 | @@ -72,7 +77,6 @@ |
1857 | def setUp(self): |
1858 | # We need to patch the startTimer first, to avoid the timer |
1859 | # to get started on initialization. |
1860 | - self.patch(systray.TrayIcon, "startTimer", lambda s, x: None) |
1861 | self.patch(systray.TransfersMenu, "startTimer", lambda s, x: None) |
1862 | yield super(SystrayTestCase, self).setUp() |
1863 | self.fake_desktop_service = FakeDesktopService() |
1864 | @@ -249,6 +253,46 @@ |
1865 | tray.open_u1.trigger() |
1866 | self.assertEqual(self._called, ((), {})) |
1867 | |
1868 | + def test_open_share_file_no_window(self): |
1869 | + """Test the open_share_file option in the menu, with no window.""" |
1870 | + self.patch(systray.TransfersMenu, "startTimer", lambda s, x: None) |
1871 | + self.patch(ubuntuone.controlpanel.gui.qt.gui, |
1872 | + "MainWindow", FakeMainWindow) |
1873 | + tray = systray.TrayIcon() |
1874 | + self.assertEqual(tray.window, None) |
1875 | + tray.share_a_file.trigger() |
1876 | + self.assertIsInstance(tray.window, FakeMainWindow) |
1877 | + self.assertTrue(tray.window.isVisible()) |
1878 | + self.assertEqual(tray.window.tabname, 'share_links') |
1879 | + self.assertEqual(tray.window.args, ((), |
1880 | + {'close_callback': tray.delete_window})) |
1881 | + |
1882 | + def test_open_share_file(self): |
1883 | + """Test the open_share_file option in the menu, with a window.""" |
1884 | + self.patch(systray.TransfersMenu, "startTimer", lambda s, x: None) |
1885 | + tray = systray.TrayIcon() |
1886 | + window = FakeMainWindow() |
1887 | + tray.window = window |
1888 | + self.assertFalse(tray.window.isVisible()) |
1889 | + tray.share_a_file.trigger() |
1890 | + self.assertEqual(tray.window, window) |
1891 | + self.assertEqual(tray.window.tabname, 'share_links') |
1892 | + self.assertTrue(tray.window.isVisible()) |
1893 | + |
1894 | + def test_open_share_file_window_minimized(self): |
1895 | + """Test the open_share_file option in the menu, with a min. window.""" |
1896 | + self.patch(systray.TransfersMenu, "startTimer", lambda s, x: None) |
1897 | + tray = systray.TrayIcon() |
1898 | + window = FakeMainWindow() |
1899 | + # This cannot be tested with the real activateWindow |
1900 | + # because the un-minimization is done by the WM, so |
1901 | + # it has a small delay, and it fails. |
1902 | + self.patch(window, "activateWindow", self._set_called) |
1903 | + tray.window = window |
1904 | + tray.share_a_file.trigger() |
1905 | + self.assertEqual(tray.window.tabname, 'share_links') |
1906 | + self.assertEqual(self._called, ((), {})) |
1907 | + |
1908 | def test_delete_window(self): |
1909 | """Test deleting an existing window.""" |
1910 | self.patch(systray.TransfersMenu, "startTimer", lambda s, x: None) |
1911 | @@ -337,7 +381,6 @@ |
1912 | def setUp(self): |
1913 | # We need to patch the startTimer first, to avoid the timer |
1914 | # to get started on initialization. |
1915 | - self.patch(systray.TrayIcon, "startTimer", lambda s, x: None) |
1916 | self.patch(systray.TransfersMenu, "startTimer", lambda s, x: None) |
1917 | yield super(TransfersMenuTestCase, self).setUp() |
1918 | self.fake_desktop_service = FakeDesktopService() |
1919 | @@ -531,58 +574,3 @@ |
1920 | current_actions = self.ui.transfers.actions() |
1921 | |
1922 | self.assertNotEqual(previous_actions, current_actions) |
1923 | - |
1924 | - |
1925 | -class SharesTestCase(BaseTestCase): |
1926 | - |
1927 | - """Test the info to be displayed in the shares section.""" |
1928 | - |
1929 | - class_ui = systray.TrayIcon |
1930 | - |
1931 | - @inlineCallbacks |
1932 | - def setUp(self): |
1933 | - # We need to patch the startTimer first, to avoid the timer |
1934 | - # to get started on initialization. |
1935 | - self.patch(systray.TrayIcon, "startTimer", lambda s, x: None) |
1936 | - self.patch(systray.TransfersMenu, "startTimer", lambda s, x: None) |
1937 | - yield super(SharesTestCase, self).setUp() |
1938 | - self.fake_desktop_service = FakeDesktopService() |
1939 | - self.patch(QtGui, "QDesktopServices", self.fake_desktop_service) |
1940 | - self.patch(self.ui.backend, "get_shares", self.fake_get_backend) |
1941 | - self._shares_info = [] |
1942 | - |
1943 | - def fake_get_backend(self): |
1944 | - """Fake get_backend.""" |
1945 | - return self._shares_info |
1946 | - |
1947 | - def test_shares_refresh_not_reload(self): |
1948 | - """Check that we get the new info of shares but not reload the menu.""" |
1949 | - actions = self.ui.context_menu.actions() |
1950 | - self.ui.load_menu() |
1951 | - post_actions = self.ui.context_menu.actions() |
1952 | - self.assertEqual(actions, post_actions) |
1953 | - |
1954 | - def test_shares_refresh_reload(self): |
1955 | - """Check that we get the new info of shares but not reload the menu.""" |
1956 | - actions = self.ui.context_menu.actions() |
1957 | - self._shares_info = [ |
1958 | - {'accepted': False, 'other_visible_name': 'name', |
1959 | - 'volume_id': 'asd123-asd123-asd123-asd123'} |
1960 | - ] |
1961 | - self.ui.load_menu() |
1962 | - post_actions = self.ui.context_menu.actions() |
1963 | - self.assertNotEqual(actions, post_actions) |
1964 | - |
1965 | - def test_share_triggered(self): |
1966 | - """Check that we get the new info of shares but not reload the menu.""" |
1967 | - volume_id = 'asd123-asd123-asd123-asd123' |
1968 | - self._shares_info = [ |
1969 | - {'accepted': False, 'other_visible_name': 'name', |
1970 | - 'volume_id': volume_id} |
1971 | - ] |
1972 | - self.ui.load_menu() |
1973 | - actions = self.ui.context_menu.actions() |
1974 | - share_action = actions[7] |
1975 | - share_action.trigger() |
1976 | - expected = QtCore.QUrl(systray.ACCEPT_SHARES % volume_id) |
1977 | - self.assertEqual(self.fake_desktop_service.opened_url, expected) |
1978 | |
1979 | === modified file 'ubuntuone/controlpanel/gui/qt/tests/test_wizard.py' |
1980 | --- ubuntuone/controlpanel/gui/qt/tests/test_wizard.py 2012-03-29 19:05:02 +0000 |
1981 | +++ ubuntuone/controlpanel/gui/qt/tests/test_wizard.py 2012-12-07 15:05:43 +0000 |
1982 | @@ -296,7 +296,7 @@ |
1983 | |
1984 | |
1985 | class UbuntuOneWizardSettingsTestCase(UbuntuOneWizardSignInTestCase): |
1986 | - """Test the CloudToComputerPage wizard page.""" |
1987 | + """Test the Settings wizard page.""" |
1988 | |
1989 | buttons = {'BackButton': (None, 'currentIdChanged', (1,))} |
1990 | page_name = 'settings' |
1991 | @@ -304,7 +304,7 @@ |
1992 | |
1993 | |
1994 | class UbuntuOneWizardComputerToCloudTestCase(UbuntuOneWizardSignInTestCase): |
1995 | - """Test the CloudToComputerPage wizard page.""" |
1996 | + """Test the ComputerToCloudPage wizard page.""" |
1997 | |
1998 | buttons = { |
1999 | 'FinishButton': (None, 'finished', (gui.QtGui.QDialog.Accepted,)), |
2000 | @@ -354,6 +354,17 @@ |
2001 | method = 'login' |
2002 | |
2003 | @defer.inlineCallbacks |
2004 | + def setUp(self): |
2005 | + yield super(UbuntuOneWizardLoginTestCase, self).setUp() |
2006 | + self.connect_files_called = False |
2007 | + |
2008 | + def fake_connect_files(): |
2009 | + self.connect_files_called = True |
2010 | + |
2011 | + self.patch(self.ui.backend, 'connect_files', |
2012 | + fake_connect_files) |
2013 | + |
2014 | + @defer.inlineCallbacks |
2015 | def test_with_credentials(self): |
2016 | """Wizard is done when credentials were retrieved.""" |
2017 | self.ui.currentIdChanged.connect(self._set_called) |
2018 | @@ -370,7 +381,7 @@ |
2019 | button.click() |
2020 | |
2021 | yield d |
2022 | - |
2023 | + self.assertEqual(True, self.connect_files_called) |
2024 | self.assertTrue(self.ui.signin_page.isEnabled()) |
2025 | expected_next_id = self.ui.pages[self.ui.cloud_folders_page] |
2026 | self.assertEqual(self._called, ((expected_next_id,), {})) |
2027 | @@ -392,7 +403,7 @@ |
2028 | button.click() |
2029 | |
2030 | yield d |
2031 | - |
2032 | + self.assertEqual(False, self.connect_files_called) |
2033 | self.assertTrue(self.ui.signin_page.isEnabled()) |
2034 | self.assertFalse(self._called) |
2035 | |
2036 | @@ -413,7 +424,7 @@ |
2037 | button.click() |
2038 | |
2039 | yield d |
2040 | - |
2041 | + self.assertEqual(False, self.connect_files_called) |
2042 | self.assertTrue(self.ui.signin_page.isEnabled()) |
2043 | self.assertFalse(self._called) # the wizard page was not changed |
2044 | |
2045 | |
2046 | === modified file 'ubuntuone/controlpanel/gui/qt/uniqueapp/__init__.py' |
2047 | --- ubuntuone/controlpanel/gui/qt/uniqueapp/__init__.py 2012-04-24 17:59:49 +0000 |
2048 | +++ ubuntuone/controlpanel/gui/qt/uniqueapp/__init__.py 2012-12-07 15:05:43 +0000 |
2049 | @@ -20,15 +20,31 @@ |
2050 | |
2051 | from PyQt4 import QtNetwork, QtGui, QtCore |
2052 | |
2053 | +from ubuntuone.controlpanel.logger import setup_logging |
2054 | + |
2055 | + |
2056 | +logger = setup_logging('uniqueapp') |
2057 | + |
2058 | +# Arg with valid value |
2059 | +SOCKET_MESSAGES = { |
2060 | + '--switch-to': ["folders", "share_links", "devices", |
2061 | + "settings", "account"], |
2062 | +} |
2063 | + |
2064 | |
2065 | class UniqueApplication(QtGui.QApplication): |
2066 | |
2067 | """A dummy UniqueApplication class.""" |
2068 | |
2069 | new_instance = QtCore.pyqtSignal() |
2070 | + switch_to = QtCore.pyqtSignal(unicode) |
2071 | + activate_window = QtCore.pyqtSignal() |
2072 | |
2073 | def __init__(self, argv, key): |
2074 | super(UniqueApplication, self).__init__(argv) |
2075 | + self.mapping_signals = { |
2076 | + '--switch-to': self.switch_to, |
2077 | + } |
2078 | self.key = key |
2079 | self.server = QtNetwork.QLocalServer(self) |
2080 | self.server.newConnection.connect(self.new_instance.emit) |
2081 | @@ -38,11 +54,51 @@ |
2082 | socket.connectToServer(key, QtCore.QIODevice.WriteOnly) |
2083 | if socket.waitForConnected(500): |
2084 | # Connected, exit |
2085 | + self._send_messages(socket, argv) |
2086 | sys.exit() |
2087 | |
2088 | # Not connected, start server |
2089 | + self.cleanup() |
2090 | self.ready = self.server.listen(key) |
2091 | + if not self.ready: |
2092 | + logger.debug(self.server.errorString()) |
2093 | + self.server.newConnection.connect(self._process_messages) |
2094 | |
2095 | def cleanup(self): |
2096 | """Remove the socket when we die.""" |
2097 | self.server.removeServer(self.key) |
2098 | + |
2099 | + def _send_messages(self, socket, argv): |
2100 | + """Send messages to the running application.""" |
2101 | + # This only take care of those args that are defined in SOCKET_MESSAGES |
2102 | + # at this moment just "--switch-to", sending a message to the already |
2103 | + # running client with: "arg=arg_value". If we have more than a single |
2104 | + # arg, each arg is concatenated with "|". |
2105 | + try: |
2106 | + data = [] |
2107 | + size_argv = len(argv) |
2108 | + for index in range(2, size_argv): |
2109 | + if (argv[index] in SOCKET_MESSAGES and index < size_argv and |
2110 | + argv[index + 1] in SOCKET_MESSAGES[argv[index]]): |
2111 | + data.append('%s=%s' % (argv[index], argv[index + 1])) |
2112 | + message = '|'.join(data) |
2113 | + socket.write(message) |
2114 | + socket.flush() |
2115 | + socket.close() |
2116 | + except: |
2117 | + # The message couldn't be send through the socket, |
2118 | + # but we avoided to open multiple instances of control panel. |
2119 | + pass |
2120 | + |
2121 | + def _process_messages(self): |
2122 | + """Get the messages from the other instances.""" |
2123 | + connection = self.server.nextPendingConnection() |
2124 | + connection.waitForReadyRead() |
2125 | + data = unicode(connection.readAll()).split('|') |
2126 | + connection.close() |
2127 | + for item in data: |
2128 | + item_value = item.split('=') |
2129 | + signal = self.mapping_signals.get(item_value[0]) |
2130 | + if signal: |
2131 | + signal.emit(item_value[1]) |
2132 | + self.activate_window.emit() |
2133 | |
2134 | === modified file 'ubuntuone/controlpanel/gui/qt/uniqueapp/tests/test_unique_app.py' |
2135 | --- ubuntuone/controlpanel/gui/qt/uniqueapp/tests/test_unique_app.py 2012-04-24 17:59:49 +0000 |
2136 | +++ ubuntuone/controlpanel/gui/qt/uniqueapp/tests/test_unique_app.py 2012-12-07 15:05:43 +0000 |
2137 | @@ -19,9 +19,9 @@ |
2138 | from PyQt4 import QtCore |
2139 | from twisted.internet.defer import inlineCallbacks |
2140 | |
2141 | +from ubuntuone.controlpanel.gui.tests import FakeSignal |
2142 | from ubuntuone.controlpanel.gui.qt import uniqueapp |
2143 | from ubuntuone.controlpanel.tests import TestCase |
2144 | -from ubuntuone.controlpanel.gui.tests import FakeSignal |
2145 | |
2146 | |
2147 | #pylint: disable=C0103 |
2148 | @@ -32,6 +32,7 @@ |
2149 | self.connect_calls = [] |
2150 | self.connect_timeouts = [] |
2151 | self.connect_succeeds = True |
2152 | + self.message = None |
2153 | |
2154 | def connectToServer(self, *args, **kwargs): |
2155 | """Fake connectToServer.""" |
2156 | @@ -42,18 +43,51 @@ |
2157 | self.connect_timeouts.append(timeout) |
2158 | return self.connect_succeeds |
2159 | |
2160 | + def write(self, message): |
2161 | + """Fake write.""" |
2162 | + self.message = message |
2163 | + |
2164 | + def flush(self): |
2165 | + """Fake flush.""" |
2166 | + |
2167 | + def close(self): |
2168 | + """Fake close.""" |
2169 | + |
2170 | + def waitForReadyRead(self): |
2171 | + """Fake waitForReadyRead.""" |
2172 | + |
2173 | + def readAll(self): |
2174 | + """Fake readAll: return the message.""" |
2175 | + return self.message |
2176 | + |
2177 | |
2178 | class FakeLocalServer(object): |
2179 | |
2180 | """A fake QLocalServer.""" |
2181 | |
2182 | - def __init__(self): |
2183 | + def __init__(self, connected=True): |
2184 | self.newConnection = FakeSignal() |
2185 | self.listen_args = [] |
2186 | + self.socket = None |
2187 | + self._removed_key = None |
2188 | + self._is_connected = connected |
2189 | |
2190 | def listen(self, *args, **kwargs): |
2191 | """Fake listen.""" |
2192 | self.listen_args.append((args, kwargs)) |
2193 | + return self._is_connected |
2194 | + |
2195 | + def nextPendingConnection(self): |
2196 | + """Fake nextPendingConnection.""" |
2197 | + return self.socket |
2198 | + |
2199 | + def removeServer(self, key): |
2200 | + """Fake removeServer.""" |
2201 | + self._removed_key = key |
2202 | + |
2203 | + def errorString(self): |
2204 | + """Fake errorString.""" |
2205 | + return 'error' |
2206 | |
2207 | |
2208 | class FakeApplication(object): |
2209 | @@ -77,6 +111,21 @@ |
2210 | self.patch(uniqueapp.UniqueApplication, "aboutToQuit", self.fake_quit) |
2211 | self.patch(uniqueapp.QtGui, "QApplication", FakeApplication) |
2212 | |
2213 | + def test_cleanup_called_on_init(self): |
2214 | + """Check that cleanup is called on initialization.""" |
2215 | + uniapp = uniqueapp.UniqueApplication([], "key") |
2216 | + self.assertEqual("key", uniapp.server._removed_key) |
2217 | + |
2218 | + def test_on_failed_connection(self): |
2219 | + """Check the flow of the program on connection fail.""" |
2220 | + data = [] |
2221 | + local_server = FakeLocalServer(False) |
2222 | + self.patch(uniqueapp.QtNetwork, "QLocalServer", |
2223 | + lambda parent: local_server) |
2224 | + self.patch(uniqueapp.logger, "debug", data.append) |
2225 | + uniqueapp.UniqueApplication([], "key") |
2226 | + self.assertEqual(data, ['error']) |
2227 | + |
2228 | def test_client_socket(self): |
2229 | """Check that the client socket is used correctly.""" |
2230 | self.local_socket.connect_succeeds = True |
2231 | @@ -105,10 +154,56 @@ |
2232 | app = uniqueapp.UniqueApplication([], "key") |
2233 | # Yes, this is ugly. I can't find any other meaningful |
2234 | # way to compare them though. |
2235 | - self.assertEqual(str(app.server.newConnection.target.__self__), |
2236 | + self.assertEqual(str(app.server.newConnection.target[0].__self__), |
2237 | str(app.new_instance)) |
2238 | + self.assertEqual(app.server.newConnection.target[1], |
2239 | + app._process_messages) |
2240 | |
2241 | def test_cleanup(self): |
2242 | """Check that cleanup is called with the right key.""" |
2243 | app = uniqueapp.UniqueApplication([], "key") |
2244 | - self.assertEqual(self.fake_quit.target, app.cleanup) |
2245 | + self.assertEqual(self.fake_quit.target, [app.cleanup]) |
2246 | + |
2247 | + def test_send_messages_valid(self): |
2248 | + """Check the message is created correctly.""" |
2249 | + self.local_socket.connect_succeeds = True |
2250 | + argv = ['python', 'ubuntuone-control-panel-qt', |
2251 | + '--switch-to', 'share_links'] |
2252 | + uniqueapp.UniqueApplication(argv, "key") |
2253 | + expected = "--switch-to=share_links" |
2254 | + self.assertEqual(self.local_socket.message, expected) |
2255 | + |
2256 | + def test_send_messages_invalid(self): |
2257 | + """Check the message is created correctly.""" |
2258 | + self.local_socket.connect_succeeds = True |
2259 | + argv = ['python', 'ubuntuone-control-panel-qt'] |
2260 | + uniqueapp.UniqueApplication(argv, "key") |
2261 | + expected = "" |
2262 | + self.assertEqual(self.local_socket.message, expected) |
2263 | + |
2264 | + def test_process_message_with_message(self): |
2265 | + """Check that we are able to parse the message received.""" |
2266 | + data = [] |
2267 | + self.local_socket.connect_succeeds = True |
2268 | + argv = ['python', 'ubuntuone-control-panel-qt', |
2269 | + '--switch-to', 'share_links'] |
2270 | + app = uniqueapp.UniqueApplication(argv, "key") |
2271 | + self.local_server.socket = self.local_socket |
2272 | + app.switch_to.connect(data.append) |
2273 | + app.activate_window.connect(lambda: data.append(True)) |
2274 | + |
2275 | + app.server.newConnection.emit() |
2276 | + self.assertEqual(data, ["share_links", True]) |
2277 | + |
2278 | + def test_process_message_no_message(self): |
2279 | + """Check that we are able to parse the message received.""" |
2280 | + data = [] |
2281 | + self.local_socket.connect_succeeds = True |
2282 | + argv = ['python', 'ubuntuone-control-panel-qt'] |
2283 | + app = uniqueapp.UniqueApplication(argv, "key") |
2284 | + self.local_server.socket = self.local_socket |
2285 | + app.switch_to.connect(data.append) |
2286 | + app.activate_window.connect(lambda: data.append(True)) |
2287 | + |
2288 | + app.server.newConnection.emit() |
2289 | + self.assertEqual(data, [True]) |
2290 | |
2291 | === modified file 'ubuntuone/controlpanel/gui/qt/wizard.py' |
2292 | --- ubuntuone/controlpanel/gui/qt/wizard.py 2012-03-29 19:05:02 +0000 |
2293 | +++ ubuntuone/controlpanel/gui/qt/wizard.py 2012-12-07 15:05:43 +0000 |
2294 | @@ -68,9 +68,9 @@ |
2295 | <p>{license_link}</p> |
2296 | </body> |
2297 | <html>""".format(license_agreement=LICENSE_AGREEMENT, |
2298 | - license_gpl3=LICENSE_GPL3, license_basic=LICENSE_BASIC, |
2299 | - license_link=LICENSE_LINK.format(license_link=GPL_LINK), |
2300 | -) |
2301 | + license_gpl3=LICENSE_GPL3, license_basic=LICENSE_BASIC, |
2302 | + license_link=LICENSE_LINK.format(license_link=GPL_LINK), |
2303 | + ) |
2304 | |
2305 | |
2306 | class AreYouSure(QtGui.QDialog): |
2307 | @@ -298,6 +298,10 @@ |
2308 | def _process_credentials(self, credentials=None): |
2309 | """Confirm which is the next step after analyzing 'credentials'.""" |
2310 | if credentials: |
2311 | + # NOTE: we don't check the autoconnect key here because we |
2312 | + # do need the connection for the initial remote volumes |
2313 | + # list, and it is on by default anyway. |
2314 | + self.backend.connect_files() |
2315 | self.next() |
2316 | |
2317 | @QtCore.pyqtSlot() |
2318 | |
2319 | === modified file 'ubuntuone/controlpanel/gui/tests/__init__.py' |
2320 | --- ubuntuone/controlpanel/gui/tests/__init__.py 2012-04-24 17:59:49 +0000 |
2321 | +++ ubuntuone/controlpanel/gui/tests/__init__.py 2012-12-07 15:05:43 +0000 |
2322 | @@ -91,12 +91,12 @@ |
2323 | [{u'volume_id': u'0', u'name': u'full', u'path': u'full-share', |
2324 | u'subscribed': u'', u'type': ControlBackend.SHARE_TYPE, |
2325 | u'display_name': u'something', |
2326 | - }]), |
2327 | + }]), |
2328 | (u'Almost no free space', gui.SHARES_MIN_SIZE_FULL - 1, |
2329 | [{u'volume_id': u'1', u'name': u'almostfull', u'path': u'almost-full', |
2330 | u'subscribed': u'', u'type': ControlBackend.SHARE_TYPE, |
2331 | u'display_name': u'yadda', |
2332 | - }]), |
2333 | + }]), |
2334 | ] |
2335 | |
2336 | FAKE_VOLUMES_ONLY_ROOT_INFO = [(u'', u'5368709120', [ROOT])] |
2337 | @@ -185,17 +185,17 @@ |
2338 | |
2339 | def __init__(self, *args, **kwargs): |
2340 | """Initialize.""" |
2341 | - self.target = None |
2342 | + self.target = [] |
2343 | |
2344 | def connect(self, target): |
2345 | """Fake connect.""" |
2346 | - self.target = target |
2347 | + self.target.append(target) |
2348 | |
2349 | def disconnect(self, *args): |
2350 | """Fake disconnect.""" |
2351 | - self.target = None |
2352 | + self.target = [] |
2353 | |
2354 | def emit(self, *args): |
2355 | """Fake emit.""" |
2356 | - if self.target: |
2357 | - self.target(*args) |
2358 | + for target in self.target: |
2359 | + target(*args) |
2360 | |
2361 | === modified file 'ubuntuone/controlpanel/tests/test_backend.py' |
2362 | --- ubuntuone/controlpanel/tests/test_backend.py 2012-10-29 16:02:46 +0000 |
2363 | +++ ubuntuone/controlpanel/tests/test_backend.py 2012-12-07 15:05:43 +0000 |
2364 | @@ -16,13 +16,12 @@ |
2365 | |
2366 | """Tests for the control panel backend.""" |
2367 | |
2368 | +import json |
2369 | import operator |
2370 | import os |
2371 | |
2372 | from collections import defaultdict |
2373 | |
2374 | -import simplejson |
2375 | - |
2376 | from twisted.internet import defer |
2377 | from twisted.internet.defer import inlineCallbacks, returnValue |
2378 | from ubuntuone.devtools.handlers import MementoHandler |
2379 | @@ -43,7 +42,8 @@ |
2380 | UBUNTUONE_FROM_OAUTH, |
2381 | UBUNTUONE_LINK, |
2382 | ) |
2383 | -from ubuntuone.controlpanel.tests import (TestCase, |
2384 | +from ubuntuone.controlpanel.tests import ( |
2385 | + TestCase, |
2386 | EMPTY_DESCRIPTION_JSON, |
2387 | EXPECTED_ACCOUNT_INFO, |
2388 | EXPECTED_ACCOUNT_INFO_WITH_CURRENT_PLAN, |
2389 | @@ -106,7 +106,7 @@ |
2390 | elif self.failure: |
2391 | return defer.fail(backend.WebClientError(self.failure)) |
2392 | else: |
2393 | - result = simplejson.loads(self.results[method]) |
2394 | + result = json.loads(self.results[method]) |
2395 | return defer.succeed(result) |
2396 | |
2397 | @defer.inlineCallbacks |
2398 | @@ -1485,6 +1485,32 @@ |
2399 | self.assertEqual(self.call_count_b, 1) |
2400 | self.assertEqual(self.call_count_a, 1) |
2401 | |
2402 | + def test_remove_status_handler(self): |
2403 | + """Test removing a handler.""" |
2404 | + self.call_count_a = 0 |
2405 | + self.call_count_b = 0 |
2406 | + |
2407 | + def inc_a(status): |
2408 | + """Fake status handler #1""" |
2409 | + self.call_count_a += 1 |
2410 | + |
2411 | + def inc_b(status): |
2412 | + """Fake status handler #2""" |
2413 | + self.call_count_b += 1 |
2414 | + |
2415 | + self.addCleanup(delattr, self, "call_count_a") |
2416 | + self.addCleanup(delattr, self, "call_count_b") |
2417 | + |
2418 | + self.be.add_status_changed_handler(inc_a) |
2419 | + self.be.add_status_changed_handler(inc_b) |
2420 | + |
2421 | + self.be.sd_client.status_changed_handler({}) |
2422 | + self.be.remove_status_changed_handler(inc_b) |
2423 | + self.be.sd_client.status_changed_handler({}) |
2424 | + |
2425 | + self.assertEqual(self.call_count_b, 1) |
2426 | + self.assertEqual(self.call_count_a, 2) |
2427 | + |
2428 | |
2429 | class BackendSyncStatusIfDisabledTestCase(BackendSyncStatusTestCase): |
2430 | """Syncdaemon state for the backend when file sync is disabled.""" |
2431 | |
2432 | === modified file 'ubuntuone/controlpanel/utils/darwin.py' |
2433 | --- ubuntuone/controlpanel/utils/darwin.py 2012-10-17 17:28:43 +0000 |
2434 | +++ ubuntuone/controlpanel/utils/darwin.py 2012-12-07 15:05:43 +0000 |
2435 | @@ -395,8 +395,8 @@ |
2436 | |
2437 | authRef = get_authorization() |
2438 | |
2439 | - if (installed_version is not None and |
2440 | - installed_version < bundled_version): |
2441 | + if installed_version is not None and \ |
2442 | + installed_version < bundled_version: |
2443 | logger.info("Found installed daemon version %r < %r, removing." % |
2444 | (installed_version, bundled_version)) |
2445 | |
2446 | |
2447 | === modified file 'ubuntuone/controlpanel/web_client.py' |
2448 | --- ubuntuone/controlpanel/web_client.py 2012-10-23 19:26:30 +0000 |
2449 | +++ ubuntuone/controlpanel/web_client.py 2012-12-07 15:05:43 +0000 |
2450 | @@ -16,7 +16,7 @@ |
2451 | |
2452 | """The web client.""" |
2453 | |
2454 | -import simplejson |
2455 | +import json |
2456 | |
2457 | from twisted.internet import defer |
2458 | from ubuntu_sso.utils.webclient import webclient_factory |
2459 | @@ -48,7 +48,7 @@ |
2460 | credentials = yield self.get_credentials() |
2461 | response = yield self.wc.request(iri, extra_headers=extra_headers, |
2462 | oauth_credentials=credentials) |
2463 | - result = simplejson.loads(response.content) |
2464 | + result = json.loads(response.content) |
2465 | defer.returnValue(result) |
2466 | |
2467 | @defer.inlineCallbacks |
+1
"For a few to be immortal, many must die." [In Time]