Merge lp:~nataliabidart/ubuntu/oneiric/ubuntuone-control-panel/ubuntuone-control-panel-2.0.0 into lp:ubuntu/oneiric/ubuntuone-control-panel

Proposed by Natalia Bidart
Status: Merged
Merged at revision: 33
Proposed branch: lp:~nataliabidart/ubuntu/oneiric/ubuntuone-control-panel/ubuntuone-control-panel-2.0.0
Merge into: lp:ubuntu/oneiric/ubuntuone-control-panel
Diff against target: 30030 lines (+2419/-25703)
78 files modified
MANIFEST (+0/-140)
PKG-INFO (+2/-21)
bin/ubuntuone-control-panel-qt (+10/-1)
data/gtk/overview.ui (+4/-29)
data/gtk/services.ui (+16/-86)
data/qt/account.ui (+23/-2)
data/qt/controlpanel.ui (+228/-191)
data/qt/images.qrc (+1/-0)
data/qt/preferences.ui (+3/-3)
data/qt/signin.ui (+203/-0)
data/qt/ubuntuone.qss (+27/-5)
debian/changelog (+29/-0)
debian/control (+7/-7)
debian/ubuntuone-control-panel-gtk.install (+0/-1)
docs/ubuntuone-control-panel-gtk.1 (+2/-2)
po/POTFILES.in (+0/-1)
po/ubuntuone-control-panel.pot (+0/-486)
setup.py (+2/-2)
ubuntuone-control-panel-gtk.desktop.in (+0/-11)
ubuntuone-control-panel.in (+1/-1)
ubuntuone/controlpanel/backend.py (+24/-20)
ubuntuone/controlpanel/cache.py (+50/-0)
ubuntuone/controlpanel/dbus_service.py (+0/-40)
ubuntuone/controlpanel/dbustests/test_dbus_service.py (+3/-34)
ubuntuone/controlpanel/gui/__init__.py (+5/-5)
ubuntuone/controlpanel/gui/gtk/gui.py (+9/-12)
ubuntuone/controlpanel/gui/gtk/tests/__init__.py (+3/-3)
ubuntuone/controlpanel/gui/gtk/tests/test_gui_basic.py (+9/-1)
ubuntuone/controlpanel/gui/qt/__init__.py (+57/-2)
ubuntuone/controlpanel/gui/qt/account.py (+3/-0)
ubuntuone/controlpanel/gui/qt/addfolder.py (+11/-9)
ubuntuone/controlpanel/gui/qt/controlpanel.py (+73/-30)
ubuntuone/controlpanel/gui/qt/device.py (+15/-6)
ubuntuone/controlpanel/gui/qt/devices.py (+12/-7)
ubuntuone/controlpanel/gui/qt/filesyncstatus.py (+7/-8)
ubuntuone/controlpanel/gui/qt/folders.py (+25/-11)
ubuntuone/controlpanel/gui/qt/gotoweb.py (+19/-5)
ubuntuone/controlpanel/gui/qt/gui.py (+28/-5)
ubuntuone/controlpanel/gui/qt/loadingoverlay.py (+6/-4)
ubuntuone/controlpanel/gui/qt/main/linux.py (+4/-6)
ubuntuone/controlpanel/gui/qt/main/windows.py (+8/-11)
ubuntuone/controlpanel/gui/qt/preferences.py (+2/-0)
ubuntuone/controlpanel/gui/qt/signin.py (+89/-0)
ubuntuone/controlpanel/gui/qt/systray.py (+68/-0)
ubuntuone/controlpanel/gui/qt/tests/__init__.py (+93/-33)
ubuntuone/controlpanel/gui/qt/tests/test_account.py (+4/-9)
ubuntuone/controlpanel/gui/qt/tests/test_addfolder.py (+24/-24)
ubuntuone/controlpanel/gui/qt/tests/test_common.py (+210/-0)
ubuntuone/controlpanel/gui/qt/tests/test_controlpanel.py (+129/-24)
ubuntuone/controlpanel/gui/qt/tests/test_device.py (+20/-18)
ubuntuone/controlpanel/gui/qt/tests/test_devices.py (+12/-7)
ubuntuone/controlpanel/gui/qt/tests/test_folders.py (+27/-38)
ubuntuone/controlpanel/gui/qt/tests/test_gotoweb.py (+54/-2)
ubuntuone/controlpanel/gui/qt/tests/test_gui.py (+7/-0)
ubuntuone/controlpanel/gui/qt/tests/test_signin.py (+168/-0)
ubuntuone/controlpanel/gui/qt/tests/test_start.py (+91/-0)
ubuntuone/controlpanel/gui/qt/tests/test_systray.py (+103/-0)
ubuntuone/controlpanel/gui/qt/tests/test_ubuntuonebin.py (+20/-10)
ubuntuone/controlpanel/gui/qt/ubuntuonebin.py (+19/-10)
ubuntuone/controlpanel/gui/qt/ui/account_ui.py (+0/-84)
ubuntuone/controlpanel/gui/qt/ui/controlpanel_ui.py (+0/-208)
ubuntuone/controlpanel/gui/qt/ui/device_ui.py (+0/-46)
ubuntuone/controlpanel/gui/qt/ui/devices_ui.py (+0/-71)
ubuntuone/controlpanel/gui/qt/ui/filesyncstatus_ui.py (+0/-48)
ubuntuone/controlpanel/gui/qt/ui/folders_ui.py (+0/-90)
ubuntuone/controlpanel/gui/qt/ui/images_rc.py (+0/-23506)
ubuntuone/controlpanel/gui/qt/ui/loadingoverlay_ui.py (+0/-59)
ubuntuone/controlpanel/gui/qt/ui/mainwindow_ui.py (+0/-54)
ubuntuone/controlpanel/gui/qt/ui/preferences_ui.py (+0/-115)
ubuntuone/controlpanel/gui/tests/__init__.py (+4/-2)
ubuntuone/controlpanel/gui/tests/test_url_sign.py (+2/-2)
ubuntuone/controlpanel/logger.py (+5/-1)
ubuntuone/controlpanel/replication_client.py (+1/-2)
ubuntuone/controlpanel/tests/test_backend.py (+88/-31)
ubuntuone/controlpanel/tests/test_cache.py (+58/-0)
ubuntuone/controlpanel/web_client/tests/__init__.py (+19/-0)
ubuntuone/controlpanel/web_client/tests/test_txwebclient.py (+155/-0)
ubuntuone/controlpanel/web_client/txwebclient.py (+18/-1)
To merge this branch: bzr merge lp:~nataliabidart/ubuntu/oneiric/ubuntuone-control-panel/ubuntuone-control-panel-2.0.0
Reviewer Review Type Date Requested Status
Ubuntu Sponsors Pending
Review via email: mp+77034@code.launchpad.net

Description of the change

  * New upstream release:
    [ Alejandro J. Cura <email address hidden>]
      - Do not throw a webclient error when closing
        (LP: #845105).
    [ Natalia B. Bidart <email address hidden> ]
      - Removed all code related to Bookmarks (LP: #850142).
      - Replaces references to "Evolution" by "Thunderbird" (LP: #849494).
    [ Rodney Dawes <email address hidden> ]
      - Don't install a .desktop file for control panel
        (part of LP: #838778).
      - Point the indicator/Unity API at the installer .desktop file
        (part of LP: #838778).
      - Set the WMCLASS so Unity will fall back properly
        (part of LP: #838778).
      - Fix a few grammar mistakes (LP: #835093).
      - Don't show the "Get NGB free!" label on "Join now" button at all
        (LP: #819955).
  * debian/control:
    - ubuntuone-control-panel-gtk depends now on ubuntuone-installer >= 2.0.0.
    - require ubuntuone-client >= 2.0.0.
    - require ubuntu-sso-client >= 1.4.0.
    - no longer install a .desktop file (will be installed by ubuntuone-installer).

To post a comment you must log in.
Revision history for this message
Sebastien Bacher (seb128) wrote :

Thank you for your work

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== removed file 'MANIFEST'
2--- MANIFEST 2011-08-12 19:12:08 +0000
3+++ MANIFEST 1970-01-01 00:00:00 +0000
4@@ -1,140 +0,0 @@
5-# file GENERATED by distutils, do NOT edit
6-COPYING
7-MANIFEST
8-README
9-com.ubuntuone.controlpanel.gui.service.in
10-com.ubuntuone.controlpanel.service.in
11-pylintrc
12-run-tests
13-run-tests.bat
14-setup.py
15-ubuntuone-control-panel-gtk.desktop.in
16-ubuntuone-control-panel.in
17-ubuntuone.controlpanel.pth
18-bin/ubuntuone-control-panel-backend
19-bin/ubuntuone-control-panel-gtk
20-bin/ubuntuone-control-panel-qt
21-data/Ubuntu-R.ttf
22-data/computer.png
23-data/contacts.png
24-data/external_icon_orange.png
25-data/external_icon_white.png
26-data/facebook.png
27-data/files.png
28-data/folder.png
29-data/music-store.png
30-data/music-stream.png
31-data/notes.png
32-data/overview.png
33-data/phone.png
34-data/services-bookmarks.png
35-data/services-contacts.png
36-data/services-files-example.png
37-data/services-files.png
38-data/sync_status_alert.png
39-data/sync_status_disconnected.png
40-data/sync_status_sync_done.png
41-data/sync_status_syncing.png
42-data/twitter.png
43-data/u1icon.png
44-data/gtk/dashboard.ui
45-data/gtk/device.ui
46-data/gtk/devices.ui
47-data/gtk/install.ui
48-data/gtk/management.ui
49-data/gtk/overview.ui
50-data/gtk/services.ui
51-data/gtk/volumes.ui
52-data/qt/account.ui
53-data/qt/controlpanel.ui
54-data/qt/device.ui
55-data/qt/devices.ui
56-data/qt/filesyncstatus.ui
57-data/qt/folders.ui
58-data/qt/images.qrc
59-data/qt/loadingoverlay.ui
60-data/qt/mainwindow.ui
61-data/qt/preferences.ui
62-data/qt/ubuntuone.qss
63-docs/ubuntuone-control-panel-gtk.1
64-po/POTFILES.in
65-po/ubuntuone-control-panel.pot
66-ubuntuone/__init__.py
67-ubuntuone/controlpanel/__init__.py
68-ubuntuone/controlpanel/backend.py
69-ubuntuone/controlpanel/constants.py.in
70-ubuntuone/controlpanel/dbus_service.py
71-ubuntuone/controlpanel/logger.py
72-ubuntuone/controlpanel/login_client.py
73-ubuntuone/controlpanel/replication_client.py
74-ubuntuone/controlpanel/utils.py
75-ubuntuone/controlpanel/dbustests/__init__.py
76-ubuntuone/controlpanel/dbustests/test_dbus_service.py
77-ubuntuone/controlpanel/dbustests/test_gui_service.py
78-ubuntuone/controlpanel/dbustests/test_sd_client/__init__.py
79-ubuntuone/controlpanel/dbustests/test_sd_client/test_linux.py
80-ubuntuone/controlpanel/gui/__init__.py
81-ubuntuone/controlpanel/gui/gtk/__init__.py
82-ubuntuone/controlpanel/gui/gtk/gui.py
83-ubuntuone/controlpanel/gui/gtk/package_manager.py
84-ubuntuone/controlpanel/gui/gtk/widgets.py
85-ubuntuone/controlpanel/gui/gtk/tests/__init__.py
86-ubuntuone/controlpanel/gui/gtk/tests/test_gui.py
87-ubuntuone/controlpanel/gui/gtk/tests/test_gui_basic.py
88-ubuntuone/controlpanel/gui/gtk/tests/test_package_manager.py
89-ubuntuone/controlpanel/gui/gtk/tests/test_widgets.py
90-ubuntuone/controlpanel/gui/qt/__init__.py
91-ubuntuone/controlpanel/gui/qt/account.py
92-ubuntuone/controlpanel/gui/qt/addfolder.py
93-ubuntuone/controlpanel/gui/qt/controlpanel.py
94-ubuntuone/controlpanel/gui/qt/device.py
95-ubuntuone/controlpanel/gui/qt/devices.py
96-ubuntuone/controlpanel/gui/qt/filesyncstatus.py
97-ubuntuone/controlpanel/gui/qt/folders.py
98-ubuntuone/controlpanel/gui/qt/gotoweb.py
99-ubuntuone/controlpanel/gui/qt/gui.py
100-ubuntuone/controlpanel/gui/qt/loadingoverlay.py
101-ubuntuone/controlpanel/gui/qt/preferences.py
102-ubuntuone/controlpanel/gui/qt/ubuntuonebin.py
103-ubuntuone/controlpanel/gui/qt/main/__init__.py
104-ubuntuone/controlpanel/gui/qt/main/linux.py
105-ubuntuone/controlpanel/gui/qt/main/windows.py
106-ubuntuone/controlpanel/gui/qt/tests/__init__.py
107-ubuntuone/controlpanel/gui/qt/tests/test_account.py
108-ubuntuone/controlpanel/gui/qt/tests/test_addfolder.py
109-ubuntuone/controlpanel/gui/qt/tests/test_controlpanel.py
110-ubuntuone/controlpanel/gui/qt/tests/test_device.py
111-ubuntuone/controlpanel/gui/qt/tests/test_devices.py
112-ubuntuone/controlpanel/gui/qt/tests/test_filesyncstatus.py
113-ubuntuone/controlpanel/gui/qt/tests/test_folders.py
114-ubuntuone/controlpanel/gui/qt/tests/test_gotoweb.py
115-ubuntuone/controlpanel/gui/qt/tests/test_gui.py
116-ubuntuone/controlpanel/gui/qt/tests/test_loadingoverlay.py
117-ubuntuone/controlpanel/gui/qt/tests/test_preferences.py
118-ubuntuone/controlpanel/gui/qt/tests/test_ubuntuonebin.py
119-ubuntuone/controlpanel/gui/qt/ui/__init__.py
120-ubuntuone/controlpanel/gui/qt/ui/account_ui.py
121-ubuntuone/controlpanel/gui/qt/ui/controlpanel_ui.py
122-ubuntuone/controlpanel/gui/qt/ui/device_ui.py
123-ubuntuone/controlpanel/gui/qt/ui/devices_ui.py
124-ubuntuone/controlpanel/gui/qt/ui/filesyncstatus_ui.py
125-ubuntuone/controlpanel/gui/qt/ui/folders_ui.py
126-ubuntuone/controlpanel/gui/qt/ui/images_rc.py
127-ubuntuone/controlpanel/gui/qt/ui/loadingoverlay_ui.py
128-ubuntuone/controlpanel/gui/qt/ui/mainwindow_ui.py
129-ubuntuone/controlpanel/gui/qt/ui/preferences_ui.py
130-ubuntuone/controlpanel/gui/tests/__init__.py
131-ubuntuone/controlpanel/gui/tests/test_humanize.py
132-ubuntuone/controlpanel/gui/tests/test_url_sign.py
133-ubuntuone/controlpanel/sd_client/__init__.py
134-ubuntuone/controlpanel/sd_client/linux.py
135-ubuntuone/controlpanel/tests/__init__.py
136-ubuntuone/controlpanel/tests/test_backend.py
137-ubuntuone/controlpanel/tests/test_login_client.py
138-ubuntuone/controlpanel/tests/test_replication_client.py
139-ubuntuone/controlpanel/tests/test_sd_client.py
140-ubuntuone/controlpanel/tests/test_utils.py
141-ubuntuone/controlpanel/tests/test_web_client.py
142-ubuntuone/controlpanel/web_client/__init__.py
143-ubuntuone/controlpanel/web_client/libsoup.py
144-ubuntuone/controlpanel/web_client/txwebclient.py
145
146=== modified file 'PKG-INFO'
147--- PKG-INFO 2011-08-25 19:37:14 +0000
148+++ PKG-INFO 2011-09-26 18:38:27 +0000
149@@ -1,30 +1,11 @@
150 Metadata-Version: 1.1
151 Name: ubuntuone-control-panel
152-Version: 1.1.3
153+Version: 2.0.0
154 Summary: Ubuntu One Control Panel
155 Home-page: https://launchpad.net/ubuntuone-control-panel
156 Author: Natalia Bidart
157 Author-email: natalia.bidart@canonical.com
158 License: GPL v3
159-Description: Application to manage a Ubuntu One account. Provides a DBus service to query/modify all the Ubuntu One bits.
160+Description: Application to manage an Ubuntu One account. Provides a DBus service to query/modify all the Ubuntu One bits.
161 Platform: UNKNOWN
162-Requires: PyQt4
163-Requires: apt
164-Requires: aptdaemon
165-Requires: dbus
166-Requires: defer
167-Requires: desktopcouch.application.replication_services
168-Requires: gi.repository
169-Requires: gobject
170-Requires: gtk
171-Requires: mocker
172-Requires: oauth
173-Requires: pango
174-Requires: simplejson
175-Requires: twisted.application
176-Requires: twisted.internet
177-Requires: twisted.python.failure
178-Requires: twisted.trial.unittest
179-Requires: twisted.web
180-Requires: ubuntu_sso
181 Provides: ubuntuone
182
183=== modified file 'bin/ubuntuone-control-panel-qt'
184--- bin/ubuntuone-control-panel-qt 2011-08-25 19:37:14 +0000
185+++ bin/ubuntuone-control-panel-qt 2011-09-26 18:38:27 +0000
186@@ -45,10 +45,19 @@
187 result.add_option("-a", "--alert", dest="alert", action="store_true",
188 default=False, help="Start Ubuntu One "
189 "alerting the user to its presence.")
190+ result.add_option("--minimized", dest="minimized", action="store_true",
191+ default=False, help="Start Ubuntu One "
192+ "only in the notification area, with no visible window. "
193+ "Implies --with-icon")
194+ result.add_option("--with-icon", dest="with_icon", action="store_true",
195+ default=False, help="Start Ubuntu One "
196+ "with an icon in the notification area.")
197 return result
198
199
200 if __name__ == "__main__":
201 parser = parser_options()
202 (options, args) = parser.parse_args(sys.argv)
203- main.main(switch_to=options.switch_to, alert=options.alert)
204+ main.main(switch_to=options.switch_to,
205+ alert=options.alert, minimized=options.minimized,
206+ with_icon=options.with_icon)
207
208=== added file 'data/Ubuntu-B.ttf'
209Binary files data/Ubuntu-B.ttf 1970-01-01 00:00:00 +0000 and data/Ubuntu-B.ttf 2011-09-26 18:38:27 +0000 differ
210=== modified file 'data/gtk/overview.ui'
211--- data/gtk/overview.ui 2011-07-22 21:26:48 +0000
212+++ data/gtk/overview.ui 2011-09-26 18:38:27 +0000
213@@ -150,7 +150,7 @@
214 <property name="can_focus">False</property>
215 <property name="xalign">0</property>
216 <property name="label" translatable="yes">Stay Productive
217-&lt;span foreground="#909090"&gt;Keep your Firefox bookmarks and Tomboy notes synced&lt;/span&gt;</property>
218+&lt;span foreground="#909090"&gt;Keep your Tomboy notes synced&lt;/span&gt;</property>
219 <property name="use_markup">True</property>
220 <property name="wrap">True</property>
221 </object>
222@@ -251,36 +251,11 @@
223 <property name="use_action_appearance">False</property>
224 <signal name="clicked" handler="on_join_now_button_clicked" swapped="no"/>
225 <child>
226- <object class="GtkVBox" id="vbox4">
227+ <object class="GtkLabel" id="label1">
228 <property name="visible">True</property>
229 <property name="can_focus">False</property>
230- <property name="spacing">5</property>
231- <child>
232- <object class="GtkLabel" id="label1">
233- <property name="visible">True</property>
234- <property name="can_focus">False</property>
235- <property name="label" translatable="yes">&lt;span font_size="xx-large" foreground="#4d4d4d"&gt;Join now&lt;/span&gt;</property>
236- <property name="use_markup">True</property>
237- </object>
238- <packing>
239- <property name="expand">True</property>
240- <property name="fill">True</property>
241- <property name="position">0</property>
242- </packing>
243- </child>
244- <child>
245- <object class="GtkLabel" id="label2">
246- <property name="visible">True</property>
247- <property name="can_focus">False</property>
248- <property name="label" translatable="yes">&lt;span foreground="#909090"&gt;2GB of free storage&lt;/span&gt;</property>
249- <property name="use_markup">True</property>
250- </object>
251- <packing>
252- <property name="expand">True</property>
253- <property name="fill">True</property>
254- <property name="position">1</property>
255- </packing>
256- </child>
257+ <property name="label" translatable="yes">&lt;span font_size="xx-large" foreground="#4d4d4d"&gt;Join now&lt;/span&gt;</property>
258+ <property name="use_markup">True</property>
259 </object>
260 </child>
261 </object>
262
263=== modified file 'data/gtk/services.ui'
264--- data/gtk/services.ui 2011-07-22 21:26:48 +0000
265+++ data/gtk/services.ui 2011-09-26 18:38:27 +0000
266@@ -59,16 +59,21 @@
267 <property name="use_action_appearance">False</property>
268 <property name="draw_indicator">True</property>
269 </object>
270+ <packing>
271+ <property name="x_options"></property>
272+ <property name="y_options"></property>
273+ </packing>
274 </child>
275 <child>
276 <object class="GtkImage" id="files_icon">
277 <property name="visible">True</property>
278 <property name="can_focus">False</property>
279- <property name="xpad">5</property>
280 </object>
281 <packing>
282 <property name="left_attach">1</property>
283 <property name="right_attach">2</property>
284+ <property name="x_options"></property>
285+ <property name="y_options"></property>
286 </packing>
287 </child>
288 <child>
289@@ -154,8 +159,6 @@
290 <object class="GtkImage" id="files_example">
291 <property name="visible">True</property>
292 <property name="can_focus">False</property>
293- <property name="xpad">5</property>
294- <property name="ypad">5</property>
295 </object>
296 <packing>
297 <property name="expand">False</property>
298@@ -188,6 +191,7 @@
299 <object class="GtkAlignment" id="alignment3">
300 <property name="visible">True</property>
301 <property name="can_focus">False</property>
302+ <property name="top_padding">6</property>
303 <child>
304 <object class="GtkHBox" id="hbox3">
305 <property name="visible">True</property>
306@@ -203,6 +207,7 @@
307 <property name="can_focus">False</property>
308 <property name="n_rows">2</property>
309 <property name="n_columns">3</property>
310+ <property name="column_spacing">5</property>
311 <property name="row_spacing">5</property>
312 <child>
313 <object class="GtkCheckButton" id="contacts_check">
314@@ -212,16 +217,21 @@
315 <property name="use_action_appearance">False</property>
316 <property name="draw_indicator">True</property>
317 </object>
318+ <packing>
319+ <property name="x_options"></property>
320+ <property name="y_options"></property>
321+ </packing>
322 </child>
323 <child>
324 <object class="GtkImage" id="contacts_icon">
325 <property name="visible">True</property>
326 <property name="can_focus">False</property>
327- <property name="xpad">5</property>
328 </object>
329 <packing>
330 <property name="left_attach">1</property>
331 <property name="right_attach">2</property>
332+ <property name="x_options"></property>
333+ <property name="y_options"></property>
334 </packing>
335 </child>
336 <child>
337@@ -245,7 +255,6 @@
338 <property name="label" translatable="yes">&lt;span font_size="small"&gt;Once enabled, visit the &lt;a href="https://one.ubuntu.com"&gt;Ubuntu One website&lt;/a&gt; to manage your contacts, including Gmail and Facebook import&lt;/span&gt;</property>
339 <property name="use_markup">True</property>
340 <property name="wrap">True</property>
341- <property name="width_chars">35</property>
342 </object>
343 <packing>
344 <property name="left_attach">2</property>
345@@ -269,92 +278,13 @@
346 </child>
347 </object>
348 <packing>
349- <property name="expand">False</property>
350+ <property name="expand">True</property>
351 <property name="fill">True</property>
352 <property name="position">0</property>
353 </packing>
354 </child>
355 <child>
356- <object class="GtkVBox" id="bookmarks">
357- <property name="visible">True</property>
358- <property name="can_focus">False</property>
359- <child>
360- <object class="GtkTable" id="bookmarks_sync">
361- <property name="visible">True</property>
362- <property name="can_focus">False</property>
363- <property name="n_rows">2</property>
364- <property name="n_columns">3</property>
365- <property name="row_spacing">5</property>
366- <child>
367- <object class="GtkCheckButton" id="bookmarks_check">
368- <property name="visible">True</property>
369- <property name="can_focus">True</property>
370- <property name="receives_default">False</property>
371- <property name="use_action_appearance">False</property>
372- <property name="draw_indicator">True</property>
373- </object>
374- </child>
375- <child>
376- <object class="GtkImage" id="bookmarks_icon">
377- <property name="visible">True</property>
378- <property name="can_focus">False</property>
379- <property name="xpad">5</property>
380- </object>
381- <packing>
382- <property name="left_attach">1</property>
383- <property name="right_attach">2</property>
384- </packing>
385- </child>
386- <child>
387- <object class="GtkLabel" id="label6">
388- <property name="visible">True</property>
389- <property name="can_focus">False</property>
390- <property name="xalign">0</property>
391- <property name="label" translatable="yes">Enable Bookmarks Sync</property>
392- </object>
393- <packing>
394- <property name="left_attach">2</property>
395- <property name="right_attach">3</property>
396- </packing>
397- </child>
398- <child>
399- <object class="GtkLabel" id="label7">
400- <property name="visible">True</property>
401- <property name="can_focus">False</property>
402- <property name="xalign">0</property>
403- <property name="yalign">0</property>
404- <property name="label" translatable="yes">&lt;span font_size="small"&gt;Bookmarks sync works with Firefox&lt;/span&gt;</property>
405- <property name="use_markup">True</property>
406- <property name="wrap">True</property>
407- <property name="width_chars">30</property>
408- </object>
409- <packing>
410- <property name="left_attach">2</property>
411- <property name="right_attach">3</property>
412- <property name="top_attach">1</property>
413- <property name="bottom_attach">2</property>
414- </packing>
415- </child>
416- <child>
417- <placeholder/>
418- </child>
419- <child>
420- <placeholder/>
421- </child>
422- </object>
423- <packing>
424- <property name="expand">False</property>
425- <property name="fill">True</property>
426- <property name="position">0</property>
427- </packing>
428- </child>
429- </object>
430- <packing>
431- <property name="expand">False</property>
432- <property name="fill">True</property>
433- <property name="pack_type">end</property>
434- <property name="position">1</property>
435- </packing>
436+ <placeholder/>
437 </child>
438 </object>
439 </child>
440
441=== modified file 'data/qt/account.ui'
442--- data/qt/account.ui 2011-08-25 19:37:14 +0000
443+++ data/qt/account.ui 2011-09-26 18:38:27 +0000
444@@ -35,7 +35,16 @@
445 <string>Your services</string>
446 </property>
447 <layout class="QVBoxLayout" name="verticalLayout_3">
448- <property name="margin">
449+ <property name="leftMargin">
450+ <number>10</number>
451+ </property>
452+ <property name="topMargin">
453+ <number>0</number>
454+ </property>
455+ <property name="rightMargin">
456+ <number>0</number>
457+ </property>
458+ <property name="bottomMargin">
459 <number>0</number>
460 </property>
461 <item>
462@@ -61,11 +70,23 @@
463 <string>Personal details</string>
464 </property>
465 <layout class="QVBoxLayout" name="verticalLayout_4">
466- <property name="margin">
467+ <property name="leftMargin">
468+ <number>10</number>
469+ </property>
470+ <property name="topMargin">
471+ <number>0</number>
472+ </property>
473+ <property name="rightMargin">
474+ <number>0</number>
475+ </property>
476+ <property name="bottomMargin">
477 <number>0</number>
478 </property>
479 <item>
480 <layout class="QVBoxLayout" name="verticalLayout_2">
481+ <property name="leftMargin">
482+ <number>6</number>
483+ </property>
484 <item>
485 <widget class="QLabel" name="name_label">
486 <property name="text">
487
488=== modified file 'data/qt/controlpanel.ui'
489--- data/qt/controlpanel.ui 2011-08-25 19:37:14 +0000
490+++ data/qt/controlpanel.ui 2011-09-26 18:38:27 +0000
491@@ -6,8 +6,8 @@
492 <rect>
493 <x>0</x>
494 <y>0</y>
495- <width>367</width>
496- <height>142</height>
497+ <width>387</width>
498+ <height>168</height>
499 </rect>
500 </property>
501 <property name="sizePolicy">
502@@ -20,215 +20,247 @@
503 <string notr="true">Form</string>
504 </property>
505 <layout class="QVBoxLayout" name="verticalLayout">
506- <property name="spacing">
507- <number>3</number>
508- </property>
509 <property name="margin">
510 <number>0</number>
511 </property>
512 <item>
513- <widget class="QFrame" name="frame_header">
514- <layout class="QHBoxLayout" name="horizontalLayout_2">
515- <property name="spacing">
516- <number>5</number>
517- </property>
518- <property name="margin">
519- <number>0</number>
520- </property>
521- <item>
522- <widget class="QFrame" name="frame_greeting">
523- <layout class="QVBoxLayout" name="verticalLayout_4">
524- <property name="margin">
525- <number>0</number>
526- </property>
527- <item>
528- <widget class="QLabel" name="greeting_label">
529- <property name="alignment">
530- <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
531- </property>
532- </widget>
533- </item>
534- </layout>
535- </widget>
536- </item>
537- <item>
538- <spacer name="horizontalSpacer">
539- <property name="orientation">
540- <enum>Qt::Horizontal</enum>
541- </property>
542- <property name="sizeType">
543- <enum>QSizePolicy::Fixed</enum>
544- </property>
545- <property name="sizeHint" stdset="0">
546- <size>
547- <width>15</width>
548- <height>20</height>
549- </size>
550- </property>
551- </spacer>
552- </item>
553- <item>
554- <widget class="QFrame" name="frame_storage">
555- <property name="sizePolicy">
556- <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
557- <horstretch>0</horstretch>
558- <verstretch>0</verstretch>
559- </sizepolicy>
560- </property>
561- <property name="minimumSize">
562- <size>
563- <width>165</width>
564- <height>0</height>
565- </size>
566- </property>
567- <property name="maximumSize">
568- <size>
569- <width>165</width>
570- <height>16777215</height>
571- </size>
572- </property>
573- <layout class="QVBoxLayout" name="vLayoutStorage">
574- <property name="spacing">
575- <number>6</number>
576- </property>
577- <property name="sizeConstraint">
578- <enum>QLayout::SetDefaultConstraint</enum>
579- </property>
580- <property name="margin">
581- <number>0</number>
582- </property>
583- <item>
584- <widget class="QFrame" name="frame_quota">
585- <layout class="QVBoxLayout" name="verticalLayout_3">
586+ <widget class="QStackedWidget" name="switcher">
587+ <property name="currentIndex">
588+ <number>1</number>
589+ </property>
590+ <widget class="QWidget" name="management">
591+ <property name="sizePolicy">
592+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
593+ <horstretch>0</horstretch>
594+ <verstretch>0</verstretch>
595+ </sizepolicy>
596+ </property>
597+ <layout class="QVBoxLayout" name="verticalLayout_5">
598+ <property name="margin">
599+ <number>0</number>
600+ </property>
601+ <item>
602+ <layout class="QVBoxLayout" name="verticalLayout_2">
603+ <item>
604+ <widget class="QFrame" name="frame_header">
605+ <layout class="QHBoxLayout" name="horizontalLayout_2">
606 <property name="spacing">
607- <number>2</number>
608+ <number>5</number>
609 </property>
610 <property name="margin">
611 <number>0</number>
612 </property>
613 <item>
614- <widget class="QLabel" name="percentage_usage_label">
615- <property name="sizePolicy">
616- <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
617- <horstretch>0</horstretch>
618- <verstretch>0</verstretch>
619- </sizepolicy>
620- </property>
621- <property name="text">
622- <string notr="true"/>
623- </property>
624- </widget>
625- </item>
626- <item>
627- <widget class="QLabel" name="quota_usage_label">
628- <property name="font">
629- <font>
630- <pointsize>8</pointsize>
631- </font>
632- </property>
633- <property name="text">
634- <string/>
635- </property>
636+ <widget class="QFrame" name="frame_greeting">
637+ <layout class="QVBoxLayout" name="verticalLayout_4">
638+ <property name="margin">
639+ <number>0</number>
640+ </property>
641+ <item>
642+ <widget class="QLabel" name="greeting_label">
643+ <property name="alignment">
644+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
645+ </property>
646+ </widget>
647+ </item>
648+ </layout>
649+ </widget>
650+ </item>
651+ <item>
652+ <spacer name="horizontalSpacer">
653+ <property name="orientation">
654+ <enum>Qt::Horizontal</enum>
655+ </property>
656+ <property name="sizeType">
657+ <enum>QSizePolicy::Fixed</enum>
658+ </property>
659+ <property name="sizeHint" stdset="0">
660+ <size>
661+ <width>15</width>
662+ <height>20</height>
663+ </size>
664+ </property>
665+ </spacer>
666+ </item>
667+ <item>
668+ <widget class="QFrame" name="frame_storage">
669+ <property name="sizePolicy">
670+ <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
671+ <horstretch>0</horstretch>
672+ <verstretch>0</verstretch>
673+ </sizepolicy>
674+ </property>
675+ <property name="minimumSize">
676+ <size>
677+ <width>165</width>
678+ <height>0</height>
679+ </size>
680+ </property>
681+ <property name="maximumSize">
682+ <size>
683+ <width>165</width>
684+ <height>16777215</height>
685+ </size>
686+ </property>
687+ <layout class="QVBoxLayout" name="vLayoutStorage">
688+ <property name="spacing">
689+ <number>6</number>
690+ </property>
691+ <property name="sizeConstraint">
692+ <enum>QLayout::SetFixedSize</enum>
693+ </property>
694+ <property name="margin">
695+ <number>0</number>
696+ </property>
697+ <item>
698+ <widget class="QFrame" name="frame_quota">
699+ <layout class="QVBoxLayout" name="verticalLayout_3">
700+ <property name="spacing">
701+ <number>2</number>
702+ </property>
703+ <property name="margin">
704+ <number>0</number>
705+ </property>
706+ <item>
707+ <widget class="QLabel" name="percentage_usage_label">
708+ <property name="sizePolicy">
709+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
710+ <horstretch>0</horstretch>
711+ <verstretch>0</verstretch>
712+ </sizepolicy>
713+ </property>
714+ <property name="text">
715+ <string notr="true"/>
716+ </property>
717+ <property name="OverQuota" stdset="0">
718+ <bool>false</bool>
719+ </property>
720+ </widget>
721+ </item>
722+ <item>
723+ <widget class="QLabel" name="quota_usage_label">
724+ <property name="font">
725+ <font>
726+ <pointsize>8</pointsize>
727+ </font>
728+ </property>
729+ <property name="text">
730+ <string/>
731+ </property>
732+ <property name="OverQuota" stdset="0">
733+ <bool>false</bool>
734+ </property>
735+ </widget>
736+ </item>
737+ </layout>
738+ <zorder>quota_usage_label</zorder>
739+ <zorder>percentage_usage_label</zorder>
740+ </widget>
741+ </item>
742+ <item>
743+ <widget class="GoToWebButton" name="get_more_space_button">
744+ <property name="text">
745+ <string>Get more storage</string>
746+ </property>
747+ </widget>
748+ </item>
749+ </layout>
750+ </widget>
751+ </item>
752+ <item>
753+ <widget class="QFrame" name="frame_status">
754+ <property name="sizePolicy">
755+ <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
756+ <horstretch>0</horstretch>
757+ <verstretch>0</verstretch>
758+ </sizepolicy>
759+ </property>
760+ <property name="minimumSize">
761+ <size>
762+ <width>165</width>
763+ <height>0</height>
764+ </size>
765+ </property>
766+ <property name="maximumSize">
767+ <size>
768+ <width>165</width>
769+ <height>16777215</height>
770+ </size>
771+ </property>
772+ <layout class="QHBoxLayout" name="horizontalLayout_8">
773+ <property name="sizeConstraint">
774+ <enum>QLayout::SetMinimumSize</enum>
775+ </property>
776+ <property name="margin">
777+ <number>0</number>
778+ </property>
779+ <item>
780+ <widget class="FileSyncStatus" name="file_sync_status" native="true">
781+ <property name="sizePolicy">
782+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
783+ <horstretch>0</horstretch>
784+ <verstretch>0</verstretch>
785+ </sizepolicy>
786+ </property>
787+ <property name="minimumSize">
788+ <size>
789+ <width>0</width>
790+ <height>0</height>
791+ </size>
792+ </property>
793+ <property name="maximumSize">
794+ <size>
795+ <width>165</width>
796+ <height>16777215</height>
797+ </size>
798+ </property>
799+ </widget>
800+ </item>
801+ </layout>
802 </widget>
803 </item>
804 </layout>
805- <zorder>quota_usage_label</zorder>
806- <zorder>percentage_usage_label</zorder>
807- </widget>
808- </item>
809- <item>
810- <widget class="GoToWebButton" name="get_more_space_button">
811- <property name="text">
812- <string>Get more storage</string>
813- </property>
814- </widget>
815- </item>
816- </layout>
817- </widget>
818- </item>
819- <item>
820- <widget class="QFrame" name="frame_status">
821- <property name="sizePolicy">
822- <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
823- <horstretch>0</horstretch>
824- <verstretch>0</verstretch>
825- </sizepolicy>
826- </property>
827- <property name="minimumSize">
828- <size>
829- <width>165</width>
830- <height>0</height>
831- </size>
832- </property>
833- <property name="maximumSize">
834- <size>
835- <width>165</width>
836- <height>16777215</height>
837- </size>
838- </property>
839- <layout class="QHBoxLayout" name="horizontalLayout_8">
840- <property name="margin">
841- <number>0</number>
842- </property>
843- <item>
844- <widget class="FileSyncStatus" name="file_sync_status" native="true">
845+ </widget>
846+ </item>
847+ <item>
848+ <widget class="QTabWidget" name="tab_widget">
849 <property name="sizePolicy">
850- <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
851+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
852 <horstretch>0</horstretch>
853 <verstretch>0</verstretch>
854 </sizepolicy>
855 </property>
856- <property name="minimumSize">
857- <size>
858- <width>0</width>
859- <height>0</height>
860- </size>
861- </property>
862- <property name="maximumSize">
863- <size>
864- <width>165</width>
865- <height>16777215</height>
866- </size>
867- </property>
868+ <property name="currentIndex">
869+ <number>0</number>
870+ </property>
871+ <widget class="FoldersPanel" name="folders_tab">
872+ <attribute name="title">
873+ <string>Folders</string>
874+ </attribute>
875+ </widget>
876+ <widget class="DevicesPanel" name="devices_tab">
877+ <attribute name="title">
878+ <string>Devices</string>
879+ </attribute>
880+ </widget>
881+ <widget class="PreferencesPanel" name="preferences_tab">
882+ <attribute name="title">
883+ <string>Settings</string>
884+ </attribute>
885+ </widget>
886+ <widget class="AccountPanel" name="account_tab">
887+ <attribute name="title">
888+ <string>Account information</string>
889+ </attribute>
890+ </widget>
891 </widget>
892 </item>
893 </layout>
894- </widget>
895- </item>
896- </layout>
897- </widget>
898- </item>
899- <item>
900- <widget class="QTabWidget" name="tab_widget">
901- <property name="sizePolicy">
902- <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
903- <horstretch>0</horstretch>
904- <verstretch>0</verstretch>
905- </sizepolicy>
906- </property>
907- <property name="currentIndex">
908- <number>0</number>
909- </property>
910- <widget class="FoldersPanel" name="folders_tab">
911- <attribute name="title">
912- <string>Folders</string>
913- </attribute>
914- </widget>
915- <widget class="DevicesPanel" name="devices_tab">
916- <attribute name="title">
917- <string>Devices</string>
918- </attribute>
919- </widget>
920- <widget class="PreferencesPanel" name="preferences_tab">
921- <attribute name="title">
922- <string>Settings</string>
923- </attribute>
924- </widget>
925- <widget class="AccountPanel" name="account_tab">
926- <attribute name="title">
927- <string>Account information</string>
928- </attribute>
929- </widget>
930+ </item>
931+ </layout>
932+ </widget>
933+ <widget class="QWidget" name="empty"/>
934+ <widget class="SignInPanel" name="signin"/>
935 </widget>
936 </item>
937 <item>
938@@ -371,9 +403,14 @@
939 <header>ubuntuone.controlpanel.gui.qt.account</header>
940 <container>1</container>
941 </customwidget>
942+ <customwidget>
943+ <class>SignInPanel</class>
944+ <extends>QWidget</extends>
945+ <header>ubuntuone.controlpanel.gui.qt.signin</header>
946+ <container>1</container>
947+ </customwidget>
948 </customwidgets>
949 <tabstops>
950- <tabstop>tab_widget</tabstop>
951 <tabstop>help_button</tabstop>
952 <tabstop>twitter_button</tabstop>
953 <tabstop>facebook_button</tabstop>
954
955=== modified file 'data/qt/images.qrc'
956--- data/qt/images.qrc 2011-08-25 19:37:14 +0000
957+++ data/qt/images.qrc 2011-09-26 18:38:27 +0000
958@@ -15,6 +15,7 @@
959 <file>../facebook.png</file>
960 <file>../external_icon_white.png</file>
961 <file>../Ubuntu-R.ttf</file>
962+ <file>../Ubuntu-B.ttf</file>
963 <file>ubuntuone.qss</file>
964 </qresource>
965 </RCC>
966
967=== modified file 'data/qt/preferences.ui'
968--- data/qt/preferences.ui 2011-08-25 19:37:14 +0000
969+++ data/qt/preferences.ui 2011-09-26 18:38:27 +0000
970@@ -6,7 +6,7 @@
971 <rect>
972 <x>0</x>
973 <y>0</y>
974- <width>433</width>
975+ <width>512</width>
976 <height>328</height>
977 </rect>
978 </property>
979@@ -138,14 +138,14 @@
980 <item>
981 <widget class="QCheckBox" name="udf_autosubscribe_checkbox">
982 <property name="text">
983- <string>Automatically sync all selected folders on this computer to the cloud</string>
984+ <string>Automatically sync all new cloud folders to this computer</string>
985 </property>
986 </widget>
987 </item>
988 <item>
989 <widget class="QCheckBox" name="share_autosubscribe_checkbox">
990 <property name="text">
991- <string>Automatically sync all folders shared with me by other to this computer</string>
992+ <string>Automatically sync all folders shared with me to this computer</string>
993 </property>
994 </widget>
995 </item>
996
997=== added file 'data/qt/signin.ui'
998--- data/qt/signin.ui 1970-01-01 00:00:00 +0000
999+++ data/qt/signin.ui 2011-09-26 18:38:27 +0000
1000@@ -0,0 +1,203 @@
1001+<?xml version="1.0" encoding="UTF-8"?>
1002+<ui version="4.0">
1003+ <class>Form</class>
1004+ <widget class="QWidget" name="Form">
1005+ <property name="geometry">
1006+ <rect>
1007+ <x>0</x>
1008+ <y>0</y>
1009+ <width>344</width>
1010+ <height>312</height>
1011+ </rect>
1012+ </property>
1013+ <property name="windowTitle">
1014+ <string>Form</string>
1015+ </property>
1016+ <layout class="QHBoxLayout" name="horizontalLayout_3">
1017+ <property name="margin">
1018+ <number>0</number>
1019+ </property>
1020+ <item>
1021+ <widget class="QFrame" name="signin">
1022+ <layout class="QVBoxLayout" name="sign_in">
1023+ <property name="spacing">
1024+ <number>15</number>
1025+ </property>
1026+ <property name="margin">
1027+ <number>3</number>
1028+ </property>
1029+ <item>
1030+ <widget class="QLabel" name="sign_in_label">
1031+ <property name="text">
1032+ <string>Sign in to Ubuntu One</string>
1033+ </property>
1034+ </widget>
1035+ </item>
1036+ <item>
1037+ <widget class="QLabel" name="description_label">
1038+ <property name="text">
1039+ <string>Sign in with yur existing Ubuntu One username and password.</string>
1040+ </property>
1041+ </widget>
1042+ </item>
1043+ <item>
1044+ <layout class="QHBoxLayout" name="horizontalLayout">
1045+ <item>
1046+ <layout class="QVBoxLayout" name="verticalLayout_2">
1047+ <property name="spacing">
1048+ <number>15</number>
1049+ </property>
1050+ <item>
1051+ <layout class="QVBoxLayout" name="verticalLayout_4">
1052+ <property name="spacing">
1053+ <number>0</number>
1054+ </property>
1055+ <item>
1056+ <widget class="QLabel" name="email_label">
1057+ <property name="text">
1058+ <string>Email address</string>
1059+ </property>
1060+ </widget>
1061+ </item>
1062+ <item>
1063+ <widget class="QLineEdit" name="email_entry">
1064+ <property name="text">
1065+ <string/>
1066+ </property>
1067+ </widget>
1068+ </item>
1069+ </layout>
1070+ </item>
1071+ <item>
1072+ <layout class="QVBoxLayout" name="verticalLayout_3">
1073+ <property name="spacing">
1074+ <number>0</number>
1075+ </property>
1076+ <item>
1077+ <widget class="QLabel" name="password_label">
1078+ <property name="text">
1079+ <string>Password</string>
1080+ </property>
1081+ </widget>
1082+ </item>
1083+ <item>
1084+ <widget class="QLineEdit" name="password_entry">
1085+ <property name="echoMode">
1086+ <enum>QLineEdit::Password</enum>
1087+ </property>
1088+ </widget>
1089+ </item>
1090+ </layout>
1091+ </item>
1092+ <item>
1093+ <layout class="QHBoxLayout" name="horizontalLayout_2">
1094+ <item>
1095+ <widget class="QPushButton" name="signin_button">
1096+ <property name="text">
1097+ <string>Sign in</string>
1098+ </property>
1099+ <property name="default">
1100+ <bool>true</bool>
1101+ </property>
1102+ </widget>
1103+ </item>
1104+ <item>
1105+ <widget class="QPushButton" name="cancel_button">
1106+ <property name="text">
1107+ <string>Cancel</string>
1108+ </property>
1109+ <property name="secondary" stdset="0">
1110+ <bool>true</bool>
1111+ </property>
1112+ </widget>
1113+ </item>
1114+ <item>
1115+ <spacer name="horizontalSpacer">
1116+ <property name="orientation">
1117+ <enum>Qt::Horizontal</enum>
1118+ </property>
1119+ <property name="sizeHint" stdset="0">
1120+ <size>
1121+ <width>40</width>
1122+ <height>20</height>
1123+ </size>
1124+ </property>
1125+ </spacer>
1126+ </item>
1127+ </layout>
1128+ </item>
1129+ <item>
1130+ <layout class="QHBoxLayout" name="horizontalLayout_4">
1131+ <item>
1132+ <widget class="GoToWebButton" name="forgot_password_button">
1133+ <property name="sizePolicy">
1134+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1135+ <horstretch>0</horstretch>
1136+ <verstretch>0</verstretch>
1137+ </sizepolicy>
1138+ </property>
1139+ <property name="text">
1140+ <string>Forgot your password?</string>
1141+ </property>
1142+ </widget>
1143+ </item>
1144+ <item>
1145+ <spacer name="horizontalSpacer_3">
1146+ <property name="orientation">
1147+ <enum>Qt::Horizontal</enum>
1148+ </property>
1149+ <property name="sizeHint" stdset="0">
1150+ <size>
1151+ <width>40</width>
1152+ <height>20</height>
1153+ </size>
1154+ </property>
1155+ </spacer>
1156+ </item>
1157+ </layout>
1158+ </item>
1159+ </layout>
1160+ </item>
1161+ <item>
1162+ <spacer name="horizontalSpacer_2">
1163+ <property name="orientation">
1164+ <enum>Qt::Horizontal</enum>
1165+ </property>
1166+ <property name="sizeHint" stdset="0">
1167+ <size>
1168+ <width>40</width>
1169+ <height>20</height>
1170+ </size>
1171+ </property>
1172+ </spacer>
1173+ </item>
1174+ </layout>
1175+ </item>
1176+ <item>
1177+ <spacer name="verticalSpacer">
1178+ <property name="orientation">
1179+ <enum>Qt::Vertical</enum>
1180+ </property>
1181+ <property name="sizeHint" stdset="0">
1182+ <size>
1183+ <width>20</width>
1184+ <height>40</height>
1185+ </size>
1186+ </property>
1187+ </spacer>
1188+ </item>
1189+ </layout>
1190+ </widget>
1191+ </item>
1192+ </layout>
1193+ </widget>
1194+ <customwidgets>
1195+ <customwidget>
1196+ <class>GoToWebButton</class>
1197+ <extends>QPushButton</extends>
1198+ <header>ubuntuone.controlpanel.gui.qt.gotoweb</header>
1199+ </customwidget>
1200+ </customwidgets>
1201+ <resources/>
1202+ <connections/>
1203+</ui>
1204
1205=== modified file 'data/qt/ubuntuone.qss'
1206--- data/qt/ubuntuone.qss 2011-08-25 19:37:14 +0000
1207+++ data/qt/ubuntuone.qss 2011-09-26 18:38:27 +0000
1208@@ -1,5 +1,5 @@
1209 QMainWindow {
1210- background-color: #dd4814;
1211+ background-color: #aea79f;
1212 }
1213
1214 QWidget {
1215@@ -12,6 +12,7 @@
1216 border: none;
1217 }
1218
1219+QFrame#signin,
1220 QFrame#frame_header {
1221 background: #ffffff;
1222 border-radius: 5px;
1223@@ -137,6 +138,7 @@
1224 border: none;
1225 }
1226
1227+GoToWebButton#forgot_password_button,
1228 GoToWebButton#share_publish_button {
1229 background: transparent;
1230 border: none;
1231@@ -216,11 +218,8 @@
1232 padding-top: 30px;
1233 border: none;
1234 margin-top: 1ex;
1235-}
1236-
1237-QGroupBox::title {
1238 color: #333333;
1239- font: bold 15px;
1240+ font: bold 14px;
1241 }
1242
1243 QGroupBox#profile,
1244@@ -228,6 +227,11 @@
1245 padding-left: 5px;
1246 }
1247
1248+QGroupBox#bandwidth_settings,
1249+QGroupBox#file_sync_settings {
1250+ padding-left: 20px;
1251+}
1252+
1253 QListWidget {
1254 background: #f7f6f5;
1255 alternate-background-color: #efedec;
1256@@ -237,6 +241,10 @@
1257 min-height: 48px;
1258 }
1259
1260+QLabel[OverQuota="false"] {
1261+ color: #333333;
1262+}
1263+
1264 QLabel#other_devices_label {
1265 font: bold 16px;
1266 }
1267@@ -249,6 +257,20 @@
1268 color: white;
1269 }
1270
1271+QLabel#sign_in_label {
1272+ font: 16px;
1273+}
1274+
1275+QLabel#email_label,
1276+QLabel#password_label {
1277+ font-size: 10px;
1278+}
1279+
1280+QLabel[OverQuota="true"],
1281+QLabel#warning_label {
1282+ color: #df2d1f;
1283+}
1284+
1285 QAbstractItemView {
1286 border-style: solid;
1287 border-color: #898989;
1288
1289=== modified file 'debian/changelog'
1290--- debian/changelog 2011-09-07 13:35:18 +0000
1291+++ debian/changelog 2011-09-26 18:38:27 +0000
1292@@ -1,3 +1,32 @@
1293+ubuntuone-control-panel (2.0.0-0ubuntu1) UNRELEASED; urgency=low
1294+
1295+ * New upstream release:
1296+
1297+ [ Alejandro J. Cura <alecu@canonical.com>]
1298+ - Do not throw a webclient error when closing
1299+ (LP: #845105).
1300+ [ Natalia B. Bidart <natalia.bidart@canonical.com> ]
1301+ - Removed all code related to Bookmarks (LP: #850142).
1302+ - Replaces references to "Evolution" by "Thunderbird" (LP: #849494).
1303+ [ Rodney Dawes <rodney.dawes@canonical.com> ]
1304+ - Don't install a .desktop file for control panel
1305+ (part of LP: #838778).
1306+ - Point the indicator/Unity API at the installer .desktop file
1307+ (part of LP: #838778).
1308+ - Set the WMCLASS so Unity will fall back properly
1309+ (part of LP: #838778).
1310+ - Fix a few grammar mistakes (LP: #835093).
1311+ - Don't show the "Get NGB free!" label on "Join now" button at all
1312+ (LP: #819955).
1313+
1314+ * debian/control:
1315+ - ubuntuone-control-panel-gtk depends now on ubuntuone-installer >= 2.0.0.
1316+ - require ubuntuone-client >= 2.0.0.
1317+ - require ubuntu-sso-client >= 1.4.0.
1318+ - no longer install a .desktop file (will be installed by ubuntuone-installer).
1319+
1320+ -- Natalia Bidart (nessita) <natalia.bidart@canonical.com> Mon, 26 Sep 2011 14:55:15 -0300
1321+
1322 ubuntuone-control-panel (1.1.3-0ubuntu2) UNRELEASED; urgency=low
1323
1324 [ Paul Stewart ]
1325
1326=== modified file 'debian/control'
1327--- debian/control 2011-08-26 21:22:36 +0000
1328+++ debian/control 2011-09-26 18:38:27 +0000
1329@@ -17,7 +17,7 @@
1330 ${python:Depends},
1331 python,
1332 python-ubuntuone-control-panel (= ${binary:Version}),
1333- ubuntuone-client (>= 1.7.1),
1334+ ubuntuone-client (>= 2.0.0),
1335 Recommends: ubuntuone-control-panel-gui
1336 Description: Ubuntu One Control Panel
1337 Desktop application to manage an Ubuntu One account.
1338@@ -37,8 +37,8 @@
1339 python-simplejson,
1340 python-twisted-core,
1341 python-twisted-web,
1342- python-ubuntuone-client (>= 1.7.1),
1343- ubuntu-sso-client (>= 1.2.0),
1344+ python-ubuntuone-client (>= 2.0.0),
1345+ ubuntu-sso-client (>= 1.4.0),
1346 Description: Ubuntu One Control Panel Python Libraries
1347 Ubuntu One Control Panel provides a Python library to manage an Ubuntu One
1348 account.
1349@@ -55,11 +55,11 @@
1350 python-defer | python-aptdaemon,
1351 python-gobject (>= 2.21.5),
1352 python-gtk2,
1353- python-ubuntuone-client (>= 1.7.1),
1354- ubuntu-sso-client (>= 1.2.0),
1355- ubuntuone-client (>= 1.7.1),
1356+ python-ubuntuone-client (>= 2.0.0),
1357+ ubuntu-sso-client (>= 1.4.0),
1358+ ubuntuone-client (>= 2.0.0),
1359 ubuntuone-control-panel (= ${binary:Version}),
1360+ ubuntuone-installer (>= 2.0.0),
1361 Provides: ubuntuone-control-panel-gui
1362 Description: Ubuntu One Control Panel
1363 GTK+ desktop application to manage an Ubuntu One account.
1364-
1365
1366=== modified file 'debian/ubuntuone-control-panel-gtk.install'
1367--- debian/ubuntuone-control-panel-gtk.install 2011-07-22 21:45:30 +0000
1368+++ debian/ubuntuone-control-panel-gtk.install 2011-09-26 18:38:27 +0000
1369@@ -1,7 +1,6 @@
1370 debian/tmp/usr/bin/ubuntuone-control-panel-gtk
1371 debian/tmp/usr/share/dbus-1/services/com.ubuntuone.controlpanel.gui.service
1372 debian/tmp/usr/share/indicators/messages/applications/ubuntuone-control-panel
1373-debian/tmp/usr/share/applications/ubuntuone-control-panel-gtk.desktop
1374 debian/tmp/usr/share/ubuntuone-control-panel/gtk/*.ui
1375 debian/tmp/usr/share/ubuntuone-control-panel/*.png
1376 debian/tmp/usr/share/man/man1/ubuntuone-control-panel-gtk.*
1377
1378=== modified file 'docs/ubuntuone-control-panel-gtk.1'
1379--- docs/ubuntuone-control-panel-gtk.1 2010-12-06 12:27:11 +0000
1380+++ docs/ubuntuone-control-panel-gtk.1 2011-09-26 18:38:27 +0000
1381@@ -1,7 +1,7 @@
1382 .TH UBUNTUONE-CONTROL-PANEL-GTK 1
1383
1384 .SH NAME
1385-ubuntuone-control-panel-gtk \- A GTK UI for managing a Ubuntu One account
1386+ubuntuone-control-panel-gtk \- A GTK UI for managing an Ubuntu One account
1387
1388 .SH SYNOPSYS
1389 .B ubutuone-control-panel-gtk
1390@@ -9,7 +9,7 @@
1391 .SH DESCRIPTION
1392 This manual page briefly documents the
1393 .BR ubuntuone-control-panel-gtk
1394-process, which provides a desktop application to manage a Ubuntu One account.
1395+process, which provides a desktop application to manage an Ubuntu One account.
1396
1397 .SH AUTHOR
1398 This manual page was written by Natalia Bidart <natalia.bidart@canonical.com>
1399
1400=== modified file 'po/POTFILES.in'
1401--- po/POTFILES.in 2011-07-22 21:26:48 +0000
1402+++ po/POTFILES.in 2011-09-26 18:38:27 +0000
1403@@ -1,4 +1,3 @@
1404-ubuntuone-control-panel-gtk.desktop.in
1405 ubuntuone/controlpanel/gui/__init__.py
1406 [type: gettext/glade] data/gtk/dashboard.ui
1407 [type: gettext/glade] data/gtk/device.ui
1408
1409=== removed file 'po/ubuntuone-control-panel.pot'
1410--- po/ubuntuone-control-panel.pot 2011-08-25 19:37:14 +0000
1411+++ po/ubuntuone-control-panel.pot 1970-01-01 00:00:00 +0000
1412@@ -1,486 +0,0 @@
1413-# SOME DESCRIPTIVE TITLE.
1414-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
1415-# This file is distributed under the same license as the PACKAGE package.
1416-# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
1417-#
1418-#, fuzzy
1419-msgid ""
1420-msgstr ""
1421-"Project-Id-Version: PACKAGE VERSION\n"
1422-"Report-Msgid-Bugs-To: \n"
1423-"POT-Creation-Date: 2011-08-25 15:26-0400\n"
1424-"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
1425-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1426-"Language-Team: LANGUAGE <LL@li.org>\n"
1427-"Language: \n"
1428-"MIME-Version: 1.0\n"
1429-"Content-Type: text/plain; charset=CHARSET\n"
1430-"Content-Transfer-Encoding: 8bit\n"
1431-
1432-#: ../ubuntuone-control-panel-gtk.desktop.in.h:1
1433-msgid "Configure and manage your Ubuntu One account"
1434-msgstr ""
1435-
1436-#: ../ubuntuone/controlpanel/gui/__init__.py:72
1437-msgid "Always in sync"
1438-msgstr ""
1439-
1440-#: ../ubuntuone/controlpanel/gui/__init__.py:73
1441-msgid "Firefox extension"
1442-msgstr ""
1443-
1444-#: ../ubuntuone/controlpanel/gui/__init__.py:74
1445-msgid "Connect to Ubuntu One"
1446-msgstr ""
1447-
1448-#: ../ubuntuone/controlpanel/gui/__init__.py:75
1449-msgid "Evolution plug-in"
1450-msgstr ""
1451-
1452-#: ../ubuntuone/controlpanel/gui/__init__.py:76
1453-msgid "There was a problem while retrieving the credentials."
1454-msgstr ""
1455-
1456-#: ../ubuntuone/controlpanel/gui/__init__.py:77
1457-msgid "View your personal details and service summary"
1458-msgstr ""
1459-
1460-#: ../ubuntuone/controlpanel/gui/__init__.py:79
1461-msgid "Welcome to Ubuntu One!"
1462-msgstr ""
1463-
1464-#: ../ubuntuone/controlpanel/gui/__init__.py:80
1465-msgid ""
1466-"The information could not be retrieved. Maybe your internet connection is "
1467-"down?"
1468-msgstr ""
1469-
1470-#: ../ubuntuone/controlpanel/gui/__init__.py:83
1471-#: ../ubuntuone/controlpanel/gui/__init__.py:156
1472-msgid ""
1473-"The settings could not be changed,\n"
1474-"previous values were restored."
1475-msgstr ""
1476-
1477-#: ../ubuntuone/controlpanel/gui/__init__.py:85
1478-msgid "Are you sure you want to remove this device from Ubuntu One?"
1479-msgstr ""
1480-
1481-#: ../ubuntuone/controlpanel/gui/__init__.py:88
1482-msgid "The device could not be removed."
1483-msgstr ""
1484-
1485-#: ../ubuntuone/controlpanel/gui/__init__.py:89
1486-msgid "Manage devices registered with your personal cloud"
1487-msgstr ""
1488-
1489-#: ../ubuntuone/controlpanel/gui/__init__.py:91
1490-msgid "The devices connected with your personal cloud are listed below."
1491-msgstr ""
1492-
1493-#: ../ubuntuone/controlpanel/gui/__init__.py:93
1494-msgid "Explore"
1495-msgstr ""
1496-
1497-#: ../ubuntuone/controlpanel/gui/__init__.py:94
1498-#, python-format
1499-msgid "<i>%(package_name)s</i> could not be installed"
1500-msgstr ""
1501-
1502-#: ../ubuntuone/controlpanel/gui/__init__.py:95
1503-msgid "Sync another folder with your cloud"
1504-msgstr ""
1505-
1506-#: ../ubuntuone/controlpanel/gui/__init__.py:96
1507-#, python-format
1508-msgid ""
1509-"The chosen directory \"%(folder_path)s\" is not valid. \n"
1510-"\n"
1511-"Please choose a folder inside your \"%(home_folder)s\" directory, and not "
1512-"overlapping with any existing cloud folder."
1513-msgstr ""
1514-
1515-#: ../ubuntuone/controlpanel/gui/__init__.py:101
1516-msgid "My personal folders"
1517-msgstr ""
1518-
1519-#: ../ubuntuone/controlpanel/gui/__init__.py:102
1520-#, python-format
1521-msgid "Shared by %(other_user_display_name)s"
1522-msgstr ""
1523-
1524-#: ../ubuntuone/controlpanel/gui/__init__.py:103
1525-#, python-format
1526-msgid ""
1527-"The contents of your cloud folder will be merged with your local folder "
1528-"\"%(folder_path)s\" when subscribing.\n"
1529-"Do you want to subscribe to this cloud folder?"
1530-msgstr ""
1531-
1532-#: ../ubuntuone/controlpanel/gui/__init__.py:107
1533-msgid "Manage your cloud folders"
1534-msgstr ""
1535-
1536-#: ../ubuntuone/controlpanel/gui/__init__.py:109
1537-msgid ""
1538-"Select which folders from your cloud you want to sync with this computer"
1539-msgstr ""
1540-
1541-#: ../ubuntuone/controlpanel/gui/__init__.py:111
1542-msgid "Connect"
1543-msgstr ""
1544-
1545-#: ../ubuntuone/controlpanel/gui/__init__.py:112
1546-msgid "Connect the file sync service with your personal cloud"
1547-msgstr ""
1548-
1549-#: ../ubuntuone/controlpanel/gui/__init__.py:114
1550-msgid "File Sync is disabled."
1551-msgstr ""
1552-
1553-#: ../ubuntuone/controlpanel/gui/__init__.py:115
1554-msgid "Disconnect"
1555-msgstr ""
1556-
1557-#: ../ubuntuone/controlpanel/gui/__init__.py:116
1558-msgid "Disconnect the file sync service from your personal cloud"
1559-msgstr ""
1560-
1561-#: ../ubuntuone/controlpanel/gui/__init__.py:118
1562-msgid "File Sync is disconnected."
1563-msgstr ""
1564-
1565-#: ../ubuntuone/controlpanel/gui/__init__.py:119
1566-msgid "Enable"
1567-msgstr ""
1568-
1569-#: ../ubuntuone/controlpanel/gui/__init__.py:120
1570-msgid "Enable the file sync service"
1571-msgstr ""
1572-
1573-#: ../ubuntuone/controlpanel/gui/__init__.py:121
1574-msgid "File Sync error."
1575-msgstr ""
1576-
1577-#: ../ubuntuone/controlpanel/gui/__init__.py:122
1578-msgid "File Sync is up-to-date."
1579-msgstr ""
1580-
1581-#: ../ubuntuone/controlpanel/gui/__init__.py:123
1582-msgid "Restart"
1583-msgstr ""
1584-
1585-#: ../ubuntuone/controlpanel/gui/__init__.py:124
1586-msgid "Restart the file sync service"
1587-msgstr ""
1588-
1589-#: ../ubuntuone/controlpanel/gui/__init__.py:125
1590-msgid "File Sync"
1591-msgstr ""
1592-
1593-#: ../ubuntuone/controlpanel/gui/__init__.py:126
1594-msgid "Start"
1595-msgstr ""
1596-
1597-#: ../ubuntuone/controlpanel/gui/__init__.py:127
1598-msgid "Start the file sync service"
1599-msgstr ""
1600-
1601-#: ../ubuntuone/controlpanel/gui/__init__.py:128
1602-msgid "File Sync starting..."
1603-msgstr ""
1604-
1605-#: ../ubuntuone/controlpanel/gui/__init__.py:129
1606-msgid "Stop"
1607-msgstr ""
1608-
1609-#: ../ubuntuone/controlpanel/gui/__init__.py:130
1610-msgid "Stop the file sync service"
1611-msgstr ""
1612-
1613-#: ../ubuntuone/controlpanel/gui/__init__.py:131
1614-msgid "File Sync is stopped."
1615-msgstr ""
1616-
1617-#: ../ubuntuone/controlpanel/gui/__init__.py:132
1618-msgid "File Sync in progress..."
1619-msgstr ""
1620-
1621-#: ../ubuntuone/controlpanel/gui/__init__.py:133
1622-#, python-format
1623-msgid "%(free_space)s available storage"
1624-msgstr ""
1625-
1626-#: ../ubuntuone/controlpanel/gui/__init__.py:134
1627-#, python-format
1628-msgid "Hi %(user_display_name)s"
1629-msgstr ""
1630-
1631-#: ../ubuntuone/controlpanel/gui/__init__.py:135
1632-#, python-format
1633-msgid ""
1634-"You need to install the package <i>%(package_name)s</i> in order to enable "
1635-"more sync services."
1636-msgstr ""
1637-
1638-#: ../ubuntuone/controlpanel/gui/__init__.py:137
1639-#, python-format
1640-msgid "Install the %(plugin_name)s for the sync service: %(service_name)s"
1641-msgstr ""
1642-
1643-#: ../ubuntuone/controlpanel/gui/__init__.py:139
1644-#, python-format
1645-msgid "Installation of <i>%(package_name)s</i> in progress"
1646-msgstr ""
1647-
1648-#: ../ubuntuone/controlpanel/gui/__init__.py:140
1649-msgid "Loading..."
1650-msgstr ""
1651-
1652-#: ../ubuntuone/controlpanel/gui/__init__.py:141
1653-#, python-format
1654-msgid "%(app_name)s Control Panel"
1655-msgstr ""
1656-
1657-#: ../ubuntuone/controlpanel/gui/__init__.py:142
1658-msgid "My folders"
1659-msgstr ""
1660-
1661-#: ../ubuntuone/controlpanel/gui/__init__.py:143
1662-msgid "[unknown user name]"
1663-msgstr ""
1664-
1665-#: ../ubuntuone/controlpanel/gui/__init__.py:144
1666-msgid "Purchased Music"
1667-msgstr ""
1668-
1669-#: ../ubuntuone/controlpanel/gui/__init__.py:146
1670-#, python-format
1671-msgid "An internet connection is required to join or sign in to %(app_name)s."
1672-msgstr ""
1673-
1674-#: ../ubuntuone/controlpanel/gui/__init__.py:148
1675-msgid "No devices to show."
1676-msgstr ""
1677-
1678-#: ../ubuntuone/controlpanel/gui/__init__.py:149
1679-msgid "No folders to show."
1680-msgstr ""
1681-
1682-#: ../ubuntuone/controlpanel/gui/__init__.py:150
1683-msgid "There is no Ubuntu One pairing record."
1684-msgstr ""
1685-
1686-#: ../ubuntuone/controlpanel/gui/__init__.py:151
1687-#, python-format
1688-msgid "%(percentage)s used"
1689-msgstr ""
1690-
1691-#: ../ubuntuone/controlpanel/gui/__init__.py:152
1692-#, python-format
1693-msgid "Using %(used)s of %(total)s (%(percentage).0f%%)"
1694-msgstr ""
1695-
1696-#: ../ubuntuone/controlpanel/gui/__init__.py:153
1697-#, python-format
1698-msgid "%(used)s of %(total)s"
1699-msgstr ""
1700-
1701-#: ../ubuntuone/controlpanel/gui/__init__.py:154
1702-msgid "Manage the sync services"
1703-msgstr ""
1704-
1705-#: ../ubuntuone/controlpanel/gui/__init__.py:155
1706-msgid "Enable the sync services for this computer."
1707-msgstr ""
1708-
1709-#: ../ubuntuone/controlpanel/gui/__init__.py:158
1710-msgid "Manage the shares offered to others"
1711-msgstr ""
1712-
1713-#: ../ubuntuone/controlpanel/gui/__init__.py:159
1714-msgid "Manage permissions for shares made to other users."
1715-msgstr ""
1716-
1717-#: ../ubuntuone/controlpanel/gui/__init__.py:160
1718-#, python-format
1719-msgid "<i>%(package_name)s</i> was successfully installed"
1720-msgstr ""
1721-
1722-#: ../ubuntuone/controlpanel/gui/__init__.py:161 ../data/gtk/volumes.ui.h:1
1723-msgid "Sync locally?"
1724-msgstr ""
1725-
1726-#: ../ubuntuone/controlpanel/gui/__init__.py:162
1727-msgid "Value could not be retrieved."
1728-msgstr ""
1729-
1730-#: ../ubuntuone/controlpanel/gui/__init__.py:163
1731-msgid "Unknown error"
1732-msgstr ""
1733-
1734-#: ../data/gtk/dashboard.ui.h:1
1735-msgid "<b>Personal details</b>"
1736-msgstr ""
1737-
1738-#: ../data/gtk/dashboard.ui.h:2
1739-msgid "<b>Your services</b>"
1740-msgstr ""
1741-
1742-#: ../data/gtk/dashboard.ui.h:3
1743-msgid "Buy storage and plans"
1744-msgstr ""
1745-
1746-#: ../data/gtk/dashboard.ui.h:4
1747-msgid "Edit account details"
1748-msgstr ""
1749-
1750-#: ../data/gtk/device.ui.h:1
1751-msgid "KiB/s"
1752-msgstr ""
1753-
1754-#: ../data/gtk/device.ui.h:2
1755-msgid "Limit file sync bandwidth usage"
1756-msgstr ""
1757-
1758-#: ../data/gtk/device.ui.h:3
1759-msgid "Max download speed:"
1760-msgstr ""
1761-
1762-#: ../data/gtk/device.ui.h:4
1763-msgid "Max upload speed:"
1764-msgstr ""
1765-
1766-#: ../data/gtk/device.ui.h:5
1767-msgid "Show activity notifications"
1768-msgstr ""
1769-
1770-#: ../data/gtk/install.ui.h:1
1771-msgid "_Install now"
1772-msgstr ""
1773-
1774-#: ../data/gtk/management.ui.h:1
1775-msgid "Account"
1776-msgstr ""
1777-
1778-#: ../data/gtk/management.ui.h:2
1779-msgid "Cloud Folders"
1780-msgstr ""
1781-
1782-#: ../data/gtk/management.ui.h:3
1783-msgid "Community Support"
1784-msgstr ""
1785-
1786-#: ../data/gtk/management.ui.h:4
1787-msgid "Devices"
1788-msgstr ""
1789-
1790-#: ../data/gtk/management.ui.h:5
1791-msgid "Official Support"
1792-msgstr ""
1793-
1794-#: ../data/gtk/management.ui.h:6
1795-msgid "Services"
1796-msgstr ""
1797-
1798-#: ../data/gtk/management.ui.h:7
1799-msgid "Shares"
1800-msgstr ""
1801-
1802-#: ../data/gtk/management.ui.h:8
1803-msgid "Talk to us on:"
1804-msgstr ""
1805-
1806-#: ../data/gtk/management.ui.h:9
1807-msgid "http://twitter.com/ubuntuone"
1808-msgstr ""
1809-
1810-#: ../data/gtk/management.ui.h:10
1811-msgid "http://www.facebook.com/ubuntuone"
1812-msgstr ""
1813-
1814-#: ../data/gtk/overview.ui.h:1
1815-msgid ""
1816-"<span font=\"24\" foreground=\"#4d4d4d\">The Power of Your Personal Cloud</"
1817-"span>"
1818-msgstr ""
1819-
1820-#: ../data/gtk/overview.ui.h:2
1821-msgid "<span font_size=\"xx-large\" foreground=\"#4d4d4d\">Join now</span>"
1822-msgstr ""
1823-
1824-#: ../data/gtk/overview.ui.h:3
1825-msgid "<span foreground=\"#909090\">2GB of free storage</span>"
1826-msgstr ""
1827-
1828-#: ../data/gtk/overview.ui.h:4
1829-msgid "<span foreground=\"#909090\">Learn More</span>"
1830-msgstr ""
1831-
1832-#: ../data/gtk/overview.ui.h:5
1833-msgid ""
1834-"Files Anywhere\n"
1835-"<span foreground=\"#909090\">Back up and access your files from Ubuntu, "
1836-"Windows, Web or Mobile</span>"
1837-msgstr ""
1838-
1839-#: ../data/gtk/overview.ui.h:7
1840-msgid "I already have an account!"
1841-msgstr ""
1842-
1843-#: ../data/gtk/overview.ui.h:8
1844-msgid ""
1845-"Keep Connected\n"
1846-"<span foreground=\"#909090\">Unify your contacts across Desktop, Mobile and "
1847-"Web</span>"
1848-msgstr ""
1849-
1850-#: ../data/gtk/overview.ui.h:10
1851-msgid ""
1852-"Rock Out\n"
1853-"<span foreground=\"#909090\">Your entire collection follows you around with "
1854-"music streaming to Android and iPhone</span>"
1855-msgstr ""
1856-
1857-#: ../data/gtk/overview.ui.h:12
1858-msgid ""
1859-"Stay Productive\n"
1860-"<span foreground=\"#909090\">Keep your Firefox bookmarks and Tomboy notes "
1861-"synced</span>"
1862-msgstr ""
1863-
1864-#: ../data/gtk/services.ui.h:1
1865-msgid "<span font_size=\"small\">Bookmarks sync works with Firefox</span>"
1866-msgstr ""
1867-
1868-#: ../data/gtk/services.ui.h:2
1869-msgid ""
1870-"<span font_size=\"small\">Enable and then choose which folders you want to "
1871-"access from the Web or any device you connected to Ubuntu One\n"
1872-"\n"
1873-"Simply drag and drop any file or folder to your Ubuntu One folder on this "
1874-"computer</span>"
1875-msgstr ""
1876-
1877-#: ../data/gtk/services.ui.h:5
1878-msgid ""
1879-"<span font_size=\"small\">Once enabled, visit the <a href=\"https://one."
1880-"ubuntu.com\">Ubuntu One website</a> to manage your contacts, including Gmail "
1881-"and Facebook import</span>"
1882-msgstr ""
1883-
1884-#: ../data/gtk/services.ui.h:6
1885-msgid "Enable Bookmarks Sync"
1886-msgstr ""
1887-
1888-#: ../data/gtk/services.ui.h:7
1889-msgid "Enable Contacts Sync"
1890-msgstr ""
1891-
1892-#: ../data/gtk/services.ui.h:8
1893-msgid "Enable File Sync"
1894-msgstr ""
1895-
1896-#: ../data/gtk/services.ui.h:9
1897-msgid "_Show me my Ubuntu One folder"
1898-msgstr ""
1899
1900=== modified file 'setup.py'
1901--- setup.py 2011-08-25 19:37:14 +0000
1902+++ setup.py 2011-09-26 18:38:27 +0000
1903@@ -229,12 +229,12 @@
1904
1905 DistUtilsExtra.auto.setup(
1906 name='ubuntuone-control-panel',
1907- version='1.1.3',
1908+ version='2.0.0',
1909 license='GPL v3',
1910 author='Natalia Bidart',
1911 author_email='natalia.bidart@canonical.com',
1912 description='Ubuntu One Control Panel',
1913- long_description='Application to manage a Ubuntu One account. Provides' \
1914+ long_description='Application to manage an Ubuntu One account. Provides' \
1915 ' a DBus service to query/modify all the Ubuntu One bits.',
1916 url='https://launchpad.net/ubuntuone-control-panel',
1917 packages=[
1918
1919=== removed file 'ubuntuone-control-panel-gtk.desktop.in'
1920--- ubuntuone-control-panel-gtk.desktop.in 2011-07-22 21:26:48 +0000
1921+++ ubuntuone-control-panel-gtk.desktop.in 1970-01-01 00:00:00 +0000
1922@@ -1,11 +0,0 @@
1923-[Desktop Entry]
1924-Name=Ubuntu One
1925-_Comment=Configure and manage your Ubuntu One account
1926-Exec=ubuntuone-control-panel-gtk
1927-Icon=ubuntuone
1928-Terminal=false
1929-Type=Application
1930-Categories=GNOME;GTK;Settings;DesktopSettings;X-GNOME-Settings-Panel;X-GNOME-PersonalSettings
1931-StartupNotify=true
1932-X-Ayatana-Appmenu-Show-Stubs=False
1933-X-GNOME-Settings-Panel=ubuntuone
1934
1935=== modified file 'ubuntuone-control-panel.in'
1936--- ubuntuone-control-panel.in 2011-01-27 22:10:30 +0000
1937+++ ubuntuone-control-panel.in 2011-09-26 18:38:27 +0000
1938@@ -1,1 +1,1 @@
1939-@prefix@/share/applications/ubuntuone-control-panel-gtk.desktop
1940+@prefix@/share/applications/ubuntuone-installer.desktop
1941
1942=== modified file 'ubuntuone/controlpanel/backend.py'
1943--- ubuntuone/controlpanel/backend.py 2011-08-25 19:37:14 +0000
1944+++ ubuntuone/controlpanel/backend.py 2011-09-26 18:38:27 +0000
1945@@ -67,8 +67,7 @@
1946 MSG_KEY = 'message'
1947 STATUS_KEY = 'status'
1948
1949-BOOKMARKS_PKG = 'xul-ext-bindwood'
1950-CONTACTS_PKG = 'evolution-couchdb'
1951+CONTACTS_PKG = 'thunderbird-couchdb'
1952
1953
1954 def append_path_sep(path):
1955@@ -99,7 +98,7 @@
1956 result = yield f(instance, *args, **kwargs)
1957 except UnauthorizedError, e:
1958 logger.exception('process_unauthorized (clearing credentials):')
1959- yield instance.login_client.clear_credentials()
1960+ yield instance.clear_credentials()
1961 raise e
1962
1963 returnValue(result)
1964@@ -138,6 +137,8 @@
1965 self.sd_client = sd_client.SyncDaemonClient()
1966 self.wc = web_client_factory(self.get_credentials)
1967
1968+ logger.info('ControlBackend: instance started.')
1969+
1970 def _process_file_sync_status(self, status):
1971 """Process raw file sync status into custom format.
1972
1973@@ -297,16 +298,32 @@
1974 @inlineCallbacks
1975 def get_credentials(self):
1976 """Find credentials."""
1977- if self._credentials is None:
1978+ if not self._credentials:
1979 self._credentials = yield self.login_client.find_credentials()
1980 returnValue(self._credentials)
1981
1982 @inlineCallbacks
1983+ def clear_credentials(self):
1984+ """Clear the credentials."""
1985+ self._credentials = None
1986+ yield self.login_client.clear_credentials()
1987+
1988+ @inlineCallbacks
1989 def get_token(self):
1990 """Return the token from the credentials."""
1991 credentials = yield self.get_credentials()
1992 returnValue(credentials["token"])
1993
1994+ @log_call(logger.debug, with_args=False)
1995+ @inlineCallbacks
1996+ def login(self, email, password):
1997+ """Login using 'email' and 'password'."""
1998+ result = yield self.login_client.login_email_password(
1999+ email=email, password=password)
2000+ # cache credentils
2001+ self._credentials = result
2002+ returnValue(result)
2003+
2004 @inlineCallbacks
2005 def device_is_local(self, device_id):
2006 """Return whether 'device_id' is the local devicew or not."""
2007@@ -480,7 +497,7 @@
2008 if is_local:
2009 logger.warning('remove_device: device is local! removing and '
2010 'clearing credentials.')
2011- yield self.login_client.clear_credentials()
2012+ yield self.clear_credentials()
2013
2014 returnValue(device_id)
2015
2016@@ -580,6 +597,7 @@
2017 'list (%r).', vid, self._volumes[vid])
2018 self._volumes[vid] = share
2019
2020+ share[u'realpath'] = share[u'path']
2021 nicer_path = share[u'path'].replace(shares_dir, shares_dir_link)
2022 share[u'path'] = nicer_path
2023 share[u'subscribed'] = bool(share[u'subscribed'])
2024@@ -690,9 +708,7 @@
2025 result = []
2026 for rep in replications:
2027 dependency = ''
2028- if rep == replication_client.BOOKMARKS:
2029- dependency = BOOKMARKS_PKG
2030- elif rep == replication_client.CONTACTS:
2031+ if rep == replication_client.CONTACTS:
2032 dependency = CONTACTS_PKG
2033
2034 repd = {
2035@@ -717,18 +733,6 @@
2036 returnValue(replication_id)
2037
2038 @log_call(logger.debug)
2039- def query_bookmark_extension(self):
2040- """True if the bookmark extension has been installed."""
2041- # still pending (LP: #673672)
2042- returnValue(False)
2043-
2044- @log_call(logger.debug)
2045- def install_bookmarks_extension(self):
2046- """Install the extension to sync bookmarks."""
2047- # still pending (LP: #673673)
2048- returnValue(None)
2049-
2050- @log_call(logger.debug)
2051 @inlineCallbacks
2052 def file_sync_settings_info(self):
2053 """Get the file sync settings info."""
2054
2055=== added file 'ubuntuone/controlpanel/cache.py'
2056--- ubuntuone/controlpanel/cache.py 1970-01-01 00:00:00 +0000
2057+++ ubuntuone/controlpanel/cache.py 2011-09-26 18:38:27 +0000
2058@@ -0,0 +1,50 @@
2059+# -*- coding: utf-8 -*-
2060+
2061+# Authors: Natalia B Bidart <natalia.bidart@canonical.com>
2062+#
2063+# Copyright 2011 Canonical Ltd.
2064+#
2065+# This program is free software: you can redistribute it and/or modify it
2066+# under the terms of the GNU General Public License version 3, as published
2067+# by the Free Software Foundation.
2068+#
2069+# This program is distributed in the hope that it will be useful, but
2070+# WITHOUT ANY WARRANTY; without even the implied warranties of
2071+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2072+# PURPOSE. See the GNU General Public License for more details.
2073+#
2074+# You should have received a copy of the GNU General Public License along
2075+# with this program. If not, see <http://www.gnu.org/licenses/>.
2076+
2077+"""The base object that holds a backend instance."""
2078+
2079+from ubuntuone.controlpanel import backend
2080+
2081+
2082+class Cache(object):
2083+ """The base object that caches stuff."""
2084+
2085+ logger = None
2086+ _shared_objects = {}
2087+
2088+ def __init__(self, *args, **kwargs):
2089+ """Initialize the object using 'backend' as backend."""
2090+ super(Cache, self).__init__()
2091+ if self.logger is not None:
2092+ self.logger.debug('%s: started.', self.__class__.__name__)
2093+
2094+ def get_backend(self):
2095+ """A cached ControlBackend instance."""
2096+ if not self._shared_objects:
2097+ self._shared_objects['backend'] = backend.ControlBackend()
2098+ return self._shared_objects['backend']
2099+
2100+ def set_backend(self, new_value):
2101+ """Set a new ControlBackend instance."""
2102+ self._shared_objects['backend'] = new_value
2103+
2104+ backend = property(fget=get_backend, fset=set_backend)
2105+
2106+ def clear(self):
2107+ """Clear all cached objects."""
2108+ self._shared_objects = {}
2109
2110=== modified file 'ubuntuone/controlpanel/dbus_service.py'
2111--- ubuntuone/controlpanel/dbus_service.py 2011-08-25 19:37:14 +0000
2112+++ ubuntuone/controlpanel/dbus_service.py 2011-09-26 18:38:27 +0000
2113@@ -563,46 +563,6 @@
2114
2115 #---
2116
2117- @log_call(logger.debug)
2118- @method(dbus_interface=DBUS_PREFERENCES_IFACE, in_signature="")
2119- def query_bookmark_extension(self):
2120- """Check if the extension to sync bookmarks is installed."""
2121- d = self.backend.query_bookmark_extension()
2122- d.addCallback(self.QueryBookmarksResult)
2123- d.addErrback(self.transform(self.QueryBookmarksError))
2124-
2125- @log_call(logger.debug)
2126- @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="b")
2127- def QueryBookmarksResult(self, enabled):
2128- """The bookmark extension is or is not installed."""
2129-
2130- @log_call(logger.error)
2131- @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="a{ss}")
2132- def QueryBookmarksError(self, error):
2133- """Problem getting the status of the extension."""
2134-
2135- #---
2136-
2137- @log_call(logger.info)
2138- @method(dbus_interface=DBUS_PREFERENCES_IFACE, in_signature="")
2139- def install_bookmarks_extension(self):
2140- """Install the extension to sync bookmarks."""
2141- d = self.backend.install_bookmarks_extension()
2142- d.addCallback(lambda _: self.InstallBookmarksSuccess())
2143- d.addErrback(self.transform(self.InstallBookmarksError))
2144-
2145- @log_call(logger.info)
2146- @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="")
2147- def InstallBookmarksSuccess(self):
2148- """The extension to sync bookmarks has been installed."""
2149-
2150- @log_call(logger.error)
2151- @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="a{ss}")
2152- def InstallBookmarksError(self, error):
2153- """Problem installing the extension to sync bookmarks."""
2154-
2155- #---
2156-
2157 @log_call(logger.info)
2158 @method(dbus_interface=DBUS_PREFERENCES_IFACE, in_signature="")
2159 def shutdown(self):
2160
2161=== modified file 'ubuntuone/controlpanel/dbustests/test_dbus_service.py'
2162--- ubuntuone/controlpanel/dbustests/test_dbus_service.py 2011-08-25 19:37:14 +0000
2163+++ ubuntuone/controlpanel/dbustests/test_dbus_service.py 2011-09-26 18:38:27 +0000
2164@@ -50,7 +50,7 @@
2165 "limit_bandwidth": True,
2166 "max_upload_speed": 12345,
2167 "max_download_speed": 54321,
2168- "available_services": "files, contacts, music, bookmarks",
2169+ "available_services": "files, contacts, music",
2170 "enabled_services": "files, music",
2171 },
2172 {
2173@@ -59,8 +59,8 @@
2174 "date_added": "2010-09-22T20:45:38.0",
2175 "type": "computer",
2176 "configurable": False,
2177- "available_services": "files, contacts, bookmarks",
2178- "enabled_services": "files, bookmarks",
2179+ "available_services": "files, contacts",
2180+ "enabled_services": "files",
2181 },
2182 ]
2183
2184@@ -244,14 +244,6 @@
2185 """Configure a given replication."""
2186 return self._process(replication_id)
2187
2188- def query_bookmark_extension(self):
2189- """True if the bookmark extension has been installed."""
2190- return self._process(False)
2191-
2192- def install_bookmarks_extension(self):
2193- """Install the extension to sync bookmarks."""
2194- return self._process(None)
2195-
2196 def shutdown(self):
2197 """Stop this service."""
2198 self.shutdown_func()
2199@@ -616,29 +608,6 @@
2200 expected_replication_id, {'enabled': ''})
2201 return self.assert_correct_method_call(*args)
2202
2203- def test_query_bookmarks_extension(self):
2204- """The bookmarks extension is queried."""
2205-
2206- def got_signal(enabled):
2207- """The correct status was received."""
2208- self.assertEqual(enabled, False)
2209- self.deferred.callback("success")
2210-
2211- args = ("QueryBookmarksResult", "QueryBookmarksError", got_signal,
2212- self.backend.query_bookmark_extension)
2213- return self.assert_correct_method_call(*args)
2214-
2215- def test_install_bookmarks_extension(self):
2216- """The bookmarks extension is installed."""
2217-
2218- def got_signal():
2219- """The extension was installed."""
2220- self.deferred.callback("success")
2221-
2222- args = ("InstallBookmarksSuccess", "InstallBookmarksError", got_signal,
2223- self.backend.install_bookmarks_extension)
2224- return self.assert_correct_method_call(*args)
2225-
2226
2227 class OperationsErrorTestCase(OperationsTestCase):
2228 """Test for the DBus service operations when there is an error."""
2229
2230=== modified file 'ubuntuone/controlpanel/gui/__init__.py'
2231--- ubuntuone/controlpanel/gui/__init__.py 2011-08-12 19:12:08 +0000
2232+++ ubuntuone/controlpanel/gui/__init__.py 2011-09-26 18:38:27 +0000
2233@@ -51,28 +51,29 @@
2234 MUSIC_STORE_ICON = 'music-store.png'
2235 MUSIC_STREAM_ICON = 'music-stream.png'
2236 NOTES_ICON = 'notes.png'
2237-SERVICES_BOOKMARKS_ICON = 'services-bookmarks.png'
2238 SERVICES_CONTACTS_ICON = 'services-contacts.png'
2239 SERVICES_FILES_EXAMPLE = 'services-files-example.png'
2240 SERVICES_FILES_ICON = 'services-files.png'
2241
2242 FILE_URI_PREFIX = 'file://'
2243+UBUNTUONE_FROM_OAUTH = 'https://one.ubuntu.com/api/1.0/from_oauth/'
2244 UBUNTUONE_LINK = 'https://one.ubuntu.com/'
2245
2246 CONTACTS_LINK = UBUNTUONE_LINK
2247 EDIT_ACCOUNT_LINK = UBUNTUONE_LINK + 'account/'
2248 EDIT_DEVICES_LINK = EDIT_ACCOUNT_LINK + 'machines/'
2249 EDIT_PROFILE_LINK = 'https://login.ubuntu.com/'
2250+EDIT_SERVICES_LINK = UBUNTUONE_LINK + 'services'
2251 FACEBOOK_LINK = 'http://www.facebook.com/ubuntuone/'
2252 GET_SUPPORT_LINK = UBUNTUONE_LINK + 'support/'
2253 LEARN_MORE_LINK = UBUNTUONE_LINK
2254 MANAGE_FILES_LINK = UBUNTUONE_LINK + 'files/'
2255+RESET_PASSWORD_LINK = EDIT_PROFILE_LINK + '+forgot_password'
2256 TWITTER_LINK = 'http://twitter.com/ubuntuone/'
2257
2258 ALWAYS_SUBSCRIBED = _('Always in sync')
2259-BOOKMARKS = _('Firefox extension')
2260 CONNECT_BUTTON_LABEL = _('Connect to Ubuntu One')
2261-CONTACTS = _('Evolution plug-in')
2262+CONTACTS = _('Thunderbird plug-in')
2263 CREDENTIALS_ERROR = _('There was a problem while retrieving the credentials.')
2264 DASHBOARD_BUTTON_TOOLTIP = _('View your personal details and service '
2265 'summary')
2266@@ -188,9 +189,8 @@
2267 credentials["consumer_secret"])
2268 token = oauth.OAuthToken(credentials["token"],
2269 credentials["token_secret"])
2270- uri = 'https://one.ubuntu.com/api/1.0/from_oauth/'
2271 request = oauth.OAuthRequest.from_consumer_and_token(
2272- http_url=uri, http_method='GET',
2273+ http_url=UBUNTUONE_FROM_OAUTH, http_method='GET',
2274 oauth_consumer=consumer, token=token,
2275 parameters={'next': url})
2276 sig_method = oauth.OAuthSignatureMethod_HMAC_SHA1()
2277
2278=== modified file 'ubuntuone/controlpanel/gui/gtk/gui.py'
2279--- ubuntuone/controlpanel/gui/gtk/gui.py 2011-08-25 19:37:14 +0000
2280+++ ubuntuone/controlpanel/gui/gtk/gui.py 2011-09-26 18:38:27 +0000
2281@@ -62,7 +62,7 @@
2282 try:
2283 from gi.repository import Unity # pylint: disable=E0611
2284 USE_LIBUNITY = True
2285- U1_DOTDESKTOP = "ubuntuone-control-panel-gtk.desktop"
2286+ U1_DOTDESKTOP = "ubuntuone-installer.desktop"
2287 except ImportError:
2288 USE_LIBUNITY = False
2289
2290@@ -71,6 +71,9 @@
2291
2292 WARNING_MARKUP = '<span foreground="%s"><b>%%s</b></span>' % ERROR_COLOR
2293
2294+CP_WMCLASS_NAME = 'ubuntuone-control-panel-gtk'
2295+CP_WMCLASS_CLASS = 'ubuntuone-installer'
2296+
2297
2298 def error_handler(*args, **kwargs):
2299 """Log errors when calling D-Bus methods in a async way."""
2300@@ -1118,11 +1121,8 @@
2301 self.files_icon.set_from_file(get_data_file(SERVICES_FILES_ICON))
2302 self.files_example.set_from_file(get_data_file(SERVICES_FILES_EXAMPLE))
2303 self.contacts_icon.set_from_file(get_data_file(SERVICES_CONTACTS_ICON))
2304- icon = get_data_file(SERVICES_BOOKMARKS_ICON)
2305- self.bookmarks_icon.set_from_file(icon)
2306
2307- self.plugin_names = {'contacts': CONTACTS,
2308- 'bookmarks': BOOKMARKS}
2309+ self.plugin_names = {'contacts': CONTACTS}
2310
2311 self.package_manager = package_manager.PackageManager()
2312 self.install_box = None
2313@@ -1161,13 +1161,6 @@
2314 """
2315 uri_hook(None, CONTACTS)
2316
2317- def on_bookmarks_button_clicked(self, *args, **kwargs):
2318- """The bookmarks button was clicked.
2319-
2320- XXX: this should be part of the DesktopcouchService widget.
2321-
2322- """
2323-
2324 @log_call(logger.debug)
2325 def load(self):
2326 """Load info."""
2327@@ -1614,6 +1607,10 @@
2328 def __init__(self, switch_to='', alert=False):
2329 super(ControlPanelWindow, self).__init__()
2330
2331+ # We need to set WMCLASS so Unity falls back and we only get one
2332+ # launcher on the launcher panel
2333+ self.set_wmclass(CP_WMCLASS_NAME, CP_WMCLASS_CLASS)
2334+
2335 self.connect('focus-in-event', self.remove_urgency)
2336 self.set_title(MAIN_WINDOW_TITLE % {'app_name': U1_APP_NAME})
2337 self.set_position(gtk.WIN_POS_CENTER_ALWAYS)
2338
2339=== modified file 'ubuntuone/controlpanel/gui/gtk/tests/__init__.py'
2340--- ubuntuone/controlpanel/gui/gtk/tests/__init__.py 2011-08-25 19:37:14 +0000
2341+++ ubuntuone/controlpanel/gui/gtk/tests/__init__.py 2011-09-26 18:38:27 +0000
2342@@ -109,8 +109,8 @@
2343 ]
2344
2345
2346-class FakeControlPanelBackend(FakedDBusBackend):
2347- """Fake a Control Panel Service, act as a dbus.Interface."""
2348+class FakedGUIBackend(FakedDBusBackend):
2349+ """Fake a Control Panel GUI Service, act as a dbus.Interface."""
2350
2351 bus_name = gui.DBUS_BUS_NAME_GUI
2352 object_path = gui.DBUS_PATH_GUI
2353@@ -135,7 +135,7 @@
2354 return FakedControlPanelBackend(obj, dbus_interface,
2355 *args, **kwargs)
2356 if dbus_interface == gui.DBUS_IFACE_GUI:
2357- return FakeControlPanelBackend(
2358+ return FakedGUIBackend(
2359 obj, dbus_interface, *args, **kwargs)
2360
2361
2362
2363=== modified file 'ubuntuone/controlpanel/gui/gtk/tests/test_gui_basic.py'
2364--- ubuntuone/controlpanel/gui/gtk/tests/test_gui_basic.py 2011-08-25 19:37:14 +0000
2365+++ ubuntuone/controlpanel/gui/gtk/tests/test_gui_basic.py 2011-09-26 18:38:27 +0000
2366@@ -122,6 +122,14 @@
2367 self.assertEqual(
2368 self._called, ((), {}), 'gtk.main_quit was not called.')
2369
2370+ def test_wmclass_is_correct(self):
2371+ """Test that the WMCLASS is set."""
2372+ self.patch(gui.ControlPanelWindow, 'set_wmclass', self._set_called)
2373+ cp = gui.ControlPanelWindow()
2374+ cp.destroy()
2375+ expected = ((gui.CP_WMCLASS_NAME, gui.CP_WMCLASS_CLASS), {})
2376+ self.assertEqual(self._called, expected)
2377+
2378 def test_title_is_correct(self):
2379 """The window title is correct."""
2380 expected = gui.MAIN_WINDOW_TITLE % {'app_name': gui.U1_APP_NAME}
2381@@ -315,7 +323,7 @@
2382
2383
2384 class UbuntuOneBinTestCase(BaseTestCase):
2385- """The test suite for a Ubuntu One panel."""
2386+ """The test suite for the Ubuntu One panel."""
2387
2388 klass = gui.UbuntuOneBin
2389 kwargs = {'title': 'Something old, something new and something blue.'}
2390
2391=== modified file 'ubuntuone/controlpanel/gui/qt/__init__.py'
2392--- ubuntuone/controlpanel/gui/qt/__init__.py 2011-07-22 21:26:48 +0000
2393+++ ubuntuone/controlpanel/gui/qt/__init__.py 2011-09-26 18:38:27 +0000
2394@@ -18,13 +18,25 @@
2395
2396 """The Qt graphical interface for the control panel for Ubuntu One."""
2397
2398+import collections
2399+import logging
2400+
2401+from functools import wraps
2402+
2403 from PyQt4 import QtGui, QtCore
2404+from twisted.internet import defer
2405+
2406+from ubuntuone.controlpanel.gui import FILE_URI_PREFIX
2407
2408
2409 def uri_hook(uri):
2410 """Open an URI using the default browser/file manager."""
2411- url = QtCore.QString(uri)
2412- QtGui.QDesktopServices.openUrl(QtCore.QUrl(url))
2413+ if uri.startswith(FILE_URI_PREFIX):
2414+ url = QtCore.QUrl(uri)
2415+ else:
2416+ url = QtCore.QUrl()
2417+ url.setEncodedUrl(uri)
2418+ QtGui.QDesktopServices.openUrl(url)
2419
2420
2421 def pixmap_from_name(icon_name):
2422@@ -52,3 +64,46 @@
2423 icon.icon_name = icon_name
2424
2425 return icon
2426+
2427+
2428+def handle_errors(error_handler=None, logger=None):
2429+ """Decorator to handle errors when calling a function.
2430+
2431+ if 'error_handler' is not None, it will be yielded on if any error happens.
2432+
2433+ """
2434+ if logger is None:
2435+ logger = logging.getLogger()
2436+
2437+ def middle(f):
2438+ """Decorator to handle errors when calling 'f'."""
2439+
2440+ @defer.inlineCallbacks
2441+ @wraps(f)
2442+ def inner(*args, **kwargs):
2443+ """Call 'f' passing 'args' and 'kwargs'.
2444+
2445+ Catch any error and show a error message.
2446+
2447+ """
2448+ try:
2449+ res = yield f(*args, **kwargs)
2450+ except Exception, e: # pylint: disable=W0703
2451+ logger.exception(f.__name__)
2452+ else:
2453+ defer.returnValue(res)
2454+
2455+ # this code will only be executed if there was an exception
2456+ if error_handler is not None:
2457+ yield error_handler()
2458+
2459+ if len(e.args) > 0 and isinstance(e.args[0], collections.Mapping):
2460+ msgs = e.args[0].itervalues()
2461+ else:
2462+ msgs = [e.__class__.__name__] + map(repr, e.args)
2463+ msg = '\n'.join(msgs)
2464+ QtGui.QMessageBox.warning(None, '', msg, QtGui.QMessageBox.Close)
2465+
2466+ return inner
2467+
2468+ return middle
2469
2470=== modified file 'ubuntuone/controlpanel/gui/qt/account.py'
2471--- ubuntuone/controlpanel/gui/qt/account.py 2011-07-22 21:26:48 +0000
2472+++ ubuntuone/controlpanel/gui/qt/account.py 2011-09-26 18:38:27 +0000
2473@@ -37,12 +37,15 @@
2474 """The Account Tab Panel widget"""
2475
2476 ui_class = account_ui
2477+ logger = logger
2478
2479 def _setup(self):
2480 """Do some extra setupping for the UI."""
2481+ super(AccountPanel, self)._setup()
2482 self.ui.edit_profile_button.uri = EDIT_PROFILE_LINK
2483 self.ui.edit_services_button.uri = EDIT_ACCOUNT_LINK
2484
2485+ # pylint: disable=E0202
2486 @defer.inlineCallbacks
2487 def load(self):
2488 """Load info."""
2489
2490=== modified file 'ubuntuone/controlpanel/gui/qt/addfolder.py'
2491--- ubuntuone/controlpanel/gui/qt/addfolder.py 2011-07-22 21:26:48 +0000
2492+++ ubuntuone/controlpanel/gui/qt/addfolder.py 2011-09-26 18:38:27 +0000
2493@@ -25,11 +25,10 @@
2494 from PyQt4 import QtGui, QtCore
2495 from twisted.internet import defer
2496
2497-from ubuntuone.controlpanel import backend
2498+from ubuntuone.controlpanel import cache
2499 from ubuntuone.controlpanel.logger import setup_logging
2500-from ubuntuone.controlpanel.gui import (
2501- FOLDER_INVALID_PATH,
2502-)
2503+from ubuntuone.controlpanel.gui import FOLDER_INVALID_PATH
2504+from ubuntuone.controlpanel.gui.qt import handle_errors
2505
2506
2507 logger = setup_logging('qt.addfolder')
2508@@ -37,9 +36,11 @@
2509 CLOSE = QtGui.QMessageBox.Close
2510
2511
2512-class AddFolderButton(QtGui.QPushButton):
2513+class AddFolderButton(cache.Cache, QtGui.QPushButton):
2514 """The AddFolderButton widget"""
2515
2516+ logger = logger
2517+
2518 folderCreated = QtCore.pyqtSignal(unicode)
2519 folderCreationCanceled = QtCore.pyqtSignal()
2520
2521@@ -47,16 +48,17 @@
2522 """Initialize the UI of the widget."""
2523 super(AddFolderButton, self).__init__(*args, **kwargs)
2524 self.cloud_folders = []
2525- self.backend = backend.ControlBackend()
2526 self.clicked.connect(self.on_clicked)
2527- logger.debug('%s: started.', self.__class__.__name__)
2528
2529 @QtCore.pyqtSlot()
2530+ @handle_errors(logger=logger)
2531 @defer.inlineCallbacks
2532 def on_clicked(self):
2533 """The 'Sync another folder' button was clicked."""
2534- folder = QtGui.QFileDialog.getExistingDirectory(parent=self)
2535- folder = unicode(folder)
2536+ # The options argument is because of LP: #835013
2537+ folder = QtGui.QFileDialog.getExistingDirectory(
2538+ parent=self, options=QtGui.QFileDialog.DontUseNativeDialog)
2539+ folder = unicode(QtCore.QDir.toNativeSeparators(folder))
2540 logger.debug('on_add_folder_button_clicked: user requested folder '
2541 'creation for path %r', folder)
2542 if folder == '':
2543
2544=== modified file 'ubuntuone/controlpanel/gui/qt/controlpanel.py'
2545--- ubuntuone/controlpanel/gui/qt/controlpanel.py 2011-08-12 19:12:08 +0000
2546+++ ubuntuone/controlpanel/gui/qt/controlpanel.py 2011-09-26 18:38:27 +0000
2547@@ -21,62 +21,79 @@
2548
2549 from __future__ import division
2550
2551-from PyQt4 import QtGui, QtCore
2552+from PyQt4 import QtCore
2553 from twisted.internet import defer
2554
2555-from ubuntuone.controlpanel import backend
2556+from ubuntuone.controlpanel.backend import AUTOCONNECT_KEY
2557 from ubuntuone.controlpanel.logger import setup_logging, log_call
2558 from ubuntuone.controlpanel.gui import (
2559 humanize,
2560- EDIT_ACCOUNT_LINK,
2561+ EDIT_SERVICES_LINK,
2562 FACEBOOK_LINK,
2563 GET_SUPPORT_LINK,
2564 GREETING,
2565 PERCENTAGE_LABEL,
2566+ QUOTA_THRESHOLD,
2567 TWITTER_LINK,
2568 USAGE_LABEL,
2569 )
2570-from ubuntuone.controlpanel.gui.qt import uri_hook
2571+from ubuntuone.controlpanel.gui import qt
2572+from ubuntuone.controlpanel.gui.qt.ubuntuonebin import UbuntuOneBin
2573 from ubuntuone.controlpanel.gui.qt.ui import controlpanel_ui
2574
2575
2576 logger = setup_logging('qt.controlpanel')
2577
2578-NAME_STYLE = '<br><span style=" font-size:24pt;">%s!</span>'
2579+NAME_STYLE = '<br><span style=" font-size:16pt;">%s!</span>'
2580 PERCENTAGE_STYLE = '<span style=" font-size:16pt;">%.0f%%</span>'
2581
2582
2583-class ControlPanel(QtGui.QWidget):
2584+class ControlPanel(UbuntuOneBin):
2585 """The Control Panel widget"""
2586
2587- def __init__(self, parent=None):
2588- """Initialize the UI of the widget."""
2589- QtGui.QWidget.__init__(self, parent)
2590- self.ui = controlpanel_ui.Ui_Form()
2591- self.ui.setupUi(self)
2592-
2593- self.backend = backend.ControlBackend()
2594- self._setup()
2595- logger.debug('%s: started.', self.__class__.__name__)
2596+ ui_class = controlpanel_ui
2597+ logger = logger
2598
2599 def _setup(self):
2600 """Do some extra setupping for the UI."""
2601- self.ui.get_more_space_button.uri = EDIT_ACCOUNT_LINK
2602+ self.ui.get_more_space_button.uri = EDIT_SERVICES_LINK
2603 self.ui.help_button.uri = GET_SUPPORT_LINK
2604-
2605- # Invalid name "showEvent"
2606- # pylint: disable=C0103
2607-
2608- def showEvent(self, event):
2609- """Load info."""
2610- self.load()
2611- event.accept()
2612-
2613+ self.ui.devices_tab.localDeviceRemoved.connect(
2614+ self.on_credentials_not_found)
2615+ self.ui.signin.credentialsFound.connect(lambda creds: self.load())
2616+
2617+ @defer.inlineCallbacks
2618+ def connect_file_sync(self):
2619+ """Connect file sync service if the setting autoconnect is enabled."""
2620+ settings = yield self.backend.file_sync_settings_info()
2621+ if AUTOCONNECT_KEY in settings and settings[AUTOCONNECT_KEY]:
2622+ yield self.backend.connect_files()
2623+
2624+ @log_call(logger.debug)
2625+ def on_credentials_not_found(self):
2626+ """Credentials are not found or were removed."""
2627+ self.ui.switcher.setCurrentWidget(self.ui.signin)
2628+ self.is_processing = False
2629+
2630+ @log_call(logger.debug)
2631+ def on_credentials_found(self):
2632+ """Credentials are not found or were removed."""
2633+ self.ui.switcher.setCurrentWidget(self.ui.management)
2634+ self.connect_file_sync()
2635+ self.is_processing = False
2636+
2637+ # pylint: disable=E0202
2638 @defer.inlineCallbacks
2639 def load(self):
2640 """Load info."""
2641- info = yield self.backend.account_info()
2642- self.process_info(info)
2643+ self.is_processing = True
2644+ credentials = yield self.backend.get_credentials()
2645+ if not credentials:
2646+ self.on_credentials_not_found()
2647+ else:
2648+ self.on_credentials_found()
2649+ info = yield self.backend.account_info()
2650+ self.process_info(info)
2651
2652 @log_call(logger.debug)
2653 def process_info(self, info):
2654@@ -86,17 +103,43 @@
2655
2656 used = int(info['quota_used'])
2657 total = int(info['quota_total'])
2658- percentage = {'percentage': PERCENTAGE_STYLE % ((used / total) * 100)}
2659+ percentage_value = ((used / total) * 100)
2660+ percentage = {'percentage': PERCENTAGE_STYLE % percentage_value}
2661 data = {'used': humanize(used), 'total': humanize(total)}
2662 self.ui.percentage_usage_label.setText(PERCENTAGE_LABEL % percentage)
2663 self.ui.quota_usage_label.setText(USAGE_LABEL % data)
2664+ self._update_quota({'percentage': percentage_value})
2665+
2666+ @log_call(logger.debug)
2667+ def _update_quota(self, data=None):
2668+ """Update the quota info."""
2669+ fraction = 0.0
2670+ if data is not None:
2671+ fraction = data.get('percentage', 0.0) / 100
2672+ if fraction > 0 and fraction < 0.05:
2673+ fraction = 0.05
2674+ else:
2675+ fraction = round(fraction, 2)
2676+
2677+ logger.debug('ManagementPanel: updating quota to %r.', fraction)
2678+ self.ui.percentage_usage_label.setProperty("OverQuota",
2679+ fraction >= QUOTA_THRESHOLD)
2680+ self.ui.quota_usage_label.setProperty("OverQuota",
2681+ fraction >= QUOTA_THRESHOLD)
2682+ self.ui.percentage_usage_label.style().unpolish(
2683+ self.ui.percentage_usage_label)
2684+ self.ui.percentage_usage_label.style().polish(
2685+ self.ui.percentage_usage_label)
2686+ self.ui.quota_usage_label.style().unpolish(
2687+ self.ui.quota_usage_label)
2688+ self.ui.quota_usage_label.style().polish(self.ui.quota_usage_label)
2689
2690 @QtCore.pyqtSlot()
2691 def on_twitter_button_clicked(self):
2692 """The twitter button was clicked."""
2693- uri_hook(TWITTER_LINK)
2694+ qt.uri_hook(TWITTER_LINK)
2695
2696 @QtCore.pyqtSlot()
2697 def on_facebook_button_clicked(self):
2698 """The facebook button was clicked."""
2699- uri_hook(FACEBOOK_LINK)
2700+ qt.uri_hook(FACEBOOK_LINK)
2701
2702=== modified file 'ubuntuone/controlpanel/gui/qt/device.py'
2703--- ubuntuone/controlpanel/gui/qt/device.py 2011-07-22 21:26:48 +0000
2704+++ ubuntuone/controlpanel/gui/qt/device.py 2011-09-26 18:38:27 +0000
2705@@ -25,9 +25,15 @@
2706 DEVICE_TYPE_COMPUTER,
2707 DEVICE_TYPE_PHONE,
2708 )
2709+from ubuntuone.controlpanel import cache
2710 from ubuntuone.controlpanel.gui import DEVICE_CONFIRM_REMOVE
2711-from ubuntuone.controlpanel.gui.qt import icon_from_name, pixmap_from_name
2712+from ubuntuone.controlpanel.gui.qt import (
2713+ handle_errors,
2714+ icon_from_name,
2715+ pixmap_from_name,
2716+)
2717 from ubuntuone.controlpanel.gui.qt.ui import device_ui
2718+from ubuntuone.controlpanel.logger import setup_logging
2719
2720 COMPUTER_ICON = "computer"
2721 PHONE_ICON = "phone"
2722@@ -43,25 +49,27 @@
2723 YES = QtGui.QMessageBox.Yes
2724
2725
2726+logger = setup_logging('qt.device')
2727+
2728+
2729 def icon_name_from_type(device_type):
2730 """Get the icon name for the device."""
2731 icon_name = DEVICE_TYPE_TO_ICON_MAP.get(device_type, DEFAULT_ICON)
2732 return icon_name
2733
2734
2735-class DeviceWidget(QtGui.QWidget):
2736+class DeviceWidget(cache.Cache, QtGui.QWidget):
2737 """The widget for each device in the control panel."""
2738
2739 removed = QtCore.pyqtSignal()
2740 removeCanceled = QtCore.pyqtSignal()
2741
2742- def __init__(self, backend, device_id, **kwargs):
2743+ def __init__(self, device_id, *args, **kwargs):
2744 """Initialize the UI of the widget."""
2745- QtGui.QWidget.__init__(self, **kwargs)
2746+ super(DeviceWidget, self).__init__(*args, **kwargs)
2747 self.ui = device_ui.Ui_Form()
2748 self.ui.setupUi(self)
2749 self.id = device_id
2750- self.backend = backend
2751
2752 def update_device_info(self, device_info):
2753 """Update the device info."""
2754@@ -70,8 +78,9 @@
2755 pixmap = pixmap_from_name(icon_name)
2756 self.ui.device_icon_label.setPixmap(pixmap)
2757
2758+ @QtCore.pyqtSlot()
2759+ @handle_errors(logger=logger)
2760 @defer.inlineCallbacks
2761- @QtCore.pyqtSlot()
2762 def on_remove_device_button_clicked(self):
2763 """The user wants to remove this device."""
2764 msg = DEVICE_CONFIRM_REMOVE
2765
2766=== modified file 'ubuntuone/controlpanel/gui/qt/devices.py'
2767--- ubuntuone/controlpanel/gui/qt/devices.py 2011-07-22 21:26:48 +0000
2768+++ ubuntuone/controlpanel/gui/qt/devices.py 2011-09-26 18:38:27 +0000
2769@@ -18,8 +18,6 @@
2770
2771 """The user interface for the control panel for Ubuntu One."""
2772
2773-from gettext import gettext as _
2774-
2775 # Unused import QtGui
2776 # pylint: disable=W0611
2777 from PyQt4 import QtGui, QtCore
2778@@ -39,13 +37,17 @@
2779 class DevicesPanel(UbuntuOneBin):
2780 """The DevicesFolders Tab Panel widget"""
2781
2782- title = _('This device')
2783 ui_class = devices_ui
2784+ logger = logger
2785+
2786+ localDeviceRemoved = QtCore.pyqtSignal()
2787
2788 def _setup(self):
2789 """Do some extra setupping for the UI."""
2790+ super(DevicesPanel, self)._setup()
2791 self.ui.manage_devices_button.uri = EDIT_DEVICES_LINK
2792
2793+ # pylint: disable=E0202
2794 @defer.inlineCallbacks
2795 def load(self):
2796 """Load info."""
2797@@ -64,6 +66,11 @@
2798
2799 self.is_processing = False
2800
2801+ def on_local_device_removed(self):
2802+ """When the local device is removed, clear the box and emit signal."""
2803+ self.clear_device_info(self.ui.local_device_box)
2804+ self.localDeviceRemoved.emit()
2805+
2806 def clear_device_info(self, box):
2807 """Clear all the device info."""
2808 children = box.count()
2809@@ -84,11 +91,9 @@
2810
2811 def update_local_device(self, device_info):
2812 """Update the info for the local device."""
2813- device_widget = device.DeviceWidget(backend=self.backend,
2814- device_id=device_info['device_id'])
2815+ device_widget = device.DeviceWidget(device_id=device_info['device_id'])
2816 device_widget.update_device_info(device_info)
2817- f = lambda: self.clear_device_info(self.ui.local_device_box)
2818- device_widget.removed.connect(f)
2819+ device_widget.removed.connect(self.on_local_device_removed)
2820
2821 self.ui.local_device_box.addWidget(device_widget)
2822
2823
2824=== modified file 'ubuntuone/controlpanel/gui/qt/filesyncstatus.py'
2825--- ubuntuone/controlpanel/gui/qt/filesyncstatus.py 2011-08-25 19:37:14 +0000
2826+++ ubuntuone/controlpanel/gui/qt/filesyncstatus.py 2011-09-26 18:38:27 +0000
2827@@ -21,7 +21,7 @@
2828 from PyQt4 import QtGui, QtCore
2829 from twisted.internet import defer
2830
2831-from ubuntuone.controlpanel import backend
2832+from ubuntuone.controlpanel import backend, cache
2833 from ubuntuone.controlpanel.logger import setup_logging, log_call
2834 from ubuntuone.controlpanel.gui import (
2835 ERROR_COLOR,
2836@@ -108,27 +108,26 @@
2837 return icon_name
2838
2839
2840-class FileSyncStatus(QtGui.QWidget):
2841+class FileSyncStatus(cache.Cache, QtGui.QWidget):
2842 """The FileSyncStatus widget"""
2843
2844- def __init__(self, parent=None):
2845+ def __init__(self, *args, **kwargs):
2846 """Initialize the UI of the widget."""
2847- QtGui.QWidget.__init__(self, parent)
2848+ super(FileSyncStatus, self).__init__(*args, **kwargs)
2849 self.ui = filesyncstatus_ui.Ui_Form()
2850 self.ui.setupUi(self)
2851
2852 self._backend_method = None
2853- self.backend = backend.ControlBackend()
2854-
2855- logger.debug('%s: started.', self.__class__.__name__)
2856
2857 # Invalid name "showEvent"
2858 # pylint: disable=C0103
2859
2860 def showEvent(self, event):
2861 """Load info."""
2862+ super(FileSyncStatus, self).showEvent(event)
2863 self.load()
2864- event.accept()
2865+
2866+ # pylint: enable=C0103
2867
2868 @defer.inlineCallbacks
2869 def load(self):
2870
2871=== modified file 'ubuntuone/controlpanel/gui/qt/folders.py'
2872--- ubuntuone/controlpanel/gui/qt/folders.py 2011-08-12 19:12:08 +0000
2873+++ ubuntuone/controlpanel/gui/qt/folders.py 2011-09-26 18:38:27 +0000
2874@@ -29,7 +29,6 @@
2875 from ubuntuone.controlpanel.gui import (
2876 ALWAYS_SUBSCRIBED,
2877 EXPLORE,
2878- FILE_URI_PREFIX,
2879 FOLDER_ICON_NAME,
2880 FOLDER_OWNED_BY,
2881 FOLDER_SHARED_BY,
2882@@ -65,11 +64,12 @@
2883 """The Folders Tab Panel widget"""
2884
2885 ui_class = folders_ui
2886+ logger = logger
2887
2888 def _setup(self):
2889 """Do some extra setupping for the UI."""
2890- load_info = lambda *a, **kw: self.load()
2891- self.ui.add_folder_button.folderCreated.connect(load_info)
2892+ super(FoldersPanel, self)._setup()
2893+ self.ui.add_folder_button.folderCreated.connect(self.on_folder_created)
2894
2895 headers = self.ui.folders.header()
2896 headers.setResizeMode(FOLDER_NAME_COL, headers.Stretch)
2897@@ -81,8 +81,16 @@
2898 icon = icon_from_name('external_icon_orange')
2899 self.ui.share_publish_button.setIcon(icon)
2900
2901- logger.debug('%s: started.', self.__class__.__name__)
2902+ @log_call(logger.info)
2903+ def on_folder_created(self, new_folder):
2904+ """Reload folder info after folder creation."""
2905+ self.is_processing = True
2906+ # hack to ensure that syncdaemon updates the folder list.
2907+ # pylint: disable=W0404, E1101
2908+ from twisted.internet import reactor
2909+ reactor.callLater(2, self.load)
2910
2911+ # pylint: disable=E0202
2912 @defer.inlineCallbacks
2913 def load(self):
2914 """Load specific tab info."""
2915@@ -124,9 +132,15 @@
2916 self.ui.folders.addTopLevelItem(item)
2917
2918 for volume in volumes:
2919+ is_root = volume[u'type'] == self.backend.ROOT_TYPE
2920+ is_share = volume[u'type'] == self.backend.SHARE_TYPE
2921+
2922 child = QtGui.QTreeWidgetItem()
2923 child.setSizeHint(FOLDER_NAME_COL, QtCore.QSize(-1, 35))
2924- child.volume_path = volume['path']
2925+ if is_share and 'realpath' in volume:
2926+ child.volume_path = volume['realpath']
2927+ else:
2928+ child.volume_path = volume['path']
2929 child.volume_id = volume['volume_id']
2930
2931 name = self._process_name(volume[u'display_name'])
2932@@ -134,9 +148,6 @@
2933 child.setToolTip(FOLDER_NAME_COL, name)
2934 child.setToolTip(EXPLORE_COL, EXPLORE)
2935
2936- is_root = volume[u'type'] == self.backend.ROOT_TYPE
2937- is_share = volume[u'type'] == self.backend.SHARE_TYPE
2938-
2939 icon_name = FOLDER_ICON_NAME
2940 if is_share:
2941 icon_name = SHARE_ICON_NAME
2942@@ -173,12 +184,14 @@
2943 policy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed,
2944 QtGui.QSizePolicy.Fixed)
2945 button.setSizePolicy(policy)
2946+ button.setEnabled(bool(volume[u'subscribed']))
2947
2948 # Operator not preceded by a space
2949 # pylint: disable=C0322
2950- cb = lambda item=child: self.on_folders_itemActivated(item)
2951+ cb = lambda checked, item=child: \
2952+ self.on_folders_itemActivated(item)
2953 # pylint: enable=C0322
2954- QtCore.QObject.connect(button, QtCore.SIGNAL('clicked()'), cb)
2955+ button.clicked.connect(cb)
2956 self.ui.folders.setIndexWidget(model_index, button)
2957
2958 self.ui.folders.expandAll()
2959@@ -196,7 +209,7 @@
2960 logger.warning('on_folders_itemActivated: stored path %r '
2961 'does not exist.', volume_path)
2962 else:
2963- uri = FILE_URI_PREFIX + volume_path
2964+ uri = unicode(QtCore.QUrl.fromLocalFile(volume_path).toString())
2965 uri_hook(uri)
2966
2967 @defer.inlineCallbacks
2968@@ -230,6 +243,7 @@
2969 # user accepted, merge the folder content
2970 yield self.backend.change_volume_settings(volume_id,
2971 {'subscribed': subscribed})
2972+ self.load()
2973 else:
2974 # restore old value
2975 old = UNCHECKED if subscribed else CHECKED
2976
2977=== modified file 'ubuntuone/controlpanel/gui/qt/gotoweb.py'
2978--- ubuntuone/controlpanel/gui/qt/gotoweb.py 2011-07-22 21:26:48 +0000
2979+++ ubuntuone/controlpanel/gui/qt/gotoweb.py 2011-09-26 18:38:27 +0000
2980@@ -20,10 +20,13 @@
2981
2982 from PyQt4 import QtGui, QtCore
2983
2984-from ubuntuone.controlpanel.gui import qt
2985-
2986-
2987-class GoToWebButton(QtGui.QPushButton):
2988+from twisted.internet import defer
2989+
2990+from ubuntuone.controlpanel import cache
2991+from ubuntuone.controlpanel.gui import qt, sign_url, UBUNTUONE_LINK
2992+
2993+
2994+class GoToWebButton(cache.Cache, QtGui.QPushButton):
2995 """The GoToWebButton widget"""
2996
2997 def __init__(self, *args, **kwargs):
2998@@ -33,9 +36,20 @@
2999 self.setIcon(qt.icon_from_name('external_icon_white'))
3000 self.setLayoutDirection(QtCore.Qt.RightToLeft)
3001 self.clicked.connect(self.on_clicked)
3002+ self.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
3003
3004+ @defer.inlineCallbacks
3005 @QtCore.pyqtSlot()
3006 def on_clicked(self):
3007 """Open self.uri if not None, do nothing otherwise."""
3008 if self.uri is not None:
3009- qt.uri_hook(self.uri)
3010+
3011+ credentials = None
3012+ if self.uri.startswith(UBUNTUONE_LINK):
3013+ credentials = yield self.backend.get_credentials()
3014+
3015+ uri = self.uri
3016+ if credentials:
3017+ uri = sign_url(uri, credentials)
3018+
3019+ qt.uri_hook(uri)
3020
3021=== modified file 'ubuntuone/controlpanel/gui/qt/gui.py'
3022--- ubuntuone/controlpanel/gui/qt/gui.py 2011-07-22 21:26:48 +0000
3023+++ ubuntuone/controlpanel/gui/qt/gui.py 2011-09-26 18:38:27 +0000
3024@@ -21,22 +21,24 @@
3025
3026 from PyQt4 import QtGui
3027
3028-from ubuntuone.controlpanel.logger import setup_logging
3029+from ubuntuone.controlpanel.gui.qt.systray import TrayIcon
3030 from ubuntuone.controlpanel.gui.qt.ui import mainwindow_ui
3031
3032
3033-logger = setup_logging('qt.gui')
3034-
3035-
3036 class MainWindow(QtGui.QMainWindow):
3037 """The Main Window of the Control Panel."""
3038
3039 def __init__(self, close_callback=None):
3040 """Initialize this instance with the UI layout."""
3041- QtGui.QMainWindow.__init__(self)
3042+ super(MainWindow, self).__init__()
3043 self.ui = mainwindow_ui.Ui_MainWindow()
3044 self.ui.setupUi(self)
3045 self.close_callback = close_callback
3046+ self._setup()
3047+
3048+ def _setup(self):
3049+ """Do some extra setupping for the UI."""
3050+ self.ui.control_panel.ui.signin.signinCanceled.connect(self.close)
3051
3052 # Invalid name "closeEvent"
3053 # pylint: disable=C0103
3054@@ -46,3 +48,24 @@
3055 if self.close_callback is not None:
3056 self.close_callback()
3057 event.accept()
3058+
3059+ # pylint: enable=C0103
3060+
3061+
3062+def start(stop, minimized=False, with_icon=False):
3063+ """Show the UI elements."""
3064+ # pylint: disable=W0404, F0401
3065+ if not minimized:
3066+ if with_icon or minimized:
3067+ window = MainWindow()
3068+ else:
3069+ window = MainWindow(close_callback=stop)
3070+ window.show()
3071+ else:
3072+ window = None
3073+ if with_icon or minimized:
3074+ QtGui.QApplication.instance().setQuitOnLastWindowClosed(False)
3075+ icon = TrayIcon(window=window)
3076+ else:
3077+ icon = None
3078+ return icon, window
3079
3080=== modified file 'ubuntuone/controlpanel/gui/qt/loadingoverlay.py'
3081--- ubuntuone/controlpanel/gui/qt/loadingoverlay.py 2011-08-12 19:12:08 +0000
3082+++ ubuntuone/controlpanel/gui/qt/loadingoverlay.py 2011-09-26 18:38:27 +0000
3083@@ -18,8 +18,7 @@
3084
3085 """Loading animation over a widget."""
3086
3087-from PyQt4 import QtGui
3088-from PyQt4 import QtCore
3089+from PyQt4 import QtGui, QtCore
3090
3091 from ubuntuone.controlpanel.gui.qt.ui import loadingoverlay_ui
3092
3093@@ -29,12 +28,15 @@
3094
3095 In order to have this working, the Widget which is going to use this
3096 overlay has to reimplement the resizeEvent as follows:
3097+
3098 def resizeEvent(self, event):
3099 self.overlay.resize(event.size())
3100- event.accept()"""
3101+ event.accept()
3102+
3103+ """
3104
3105 def __init__(self, parent=None):
3106- QtGui.QFrame.__init__(self, parent)
3107+ super(LoadingOverlay, self).__init__(parent=parent)
3108 self.ui = loadingoverlay_ui.Ui_Form()
3109 self.ui.setupUi(self)
3110
3111
3112=== modified file 'ubuntuone/controlpanel/gui/qt/main/linux.py'
3113--- ubuntuone/controlpanel/gui/qt/main/linux.py 2011-07-22 21:26:48 +0000
3114+++ ubuntuone/controlpanel/gui/qt/main/linux.py 2011-09-26 18:38:27 +0000
3115@@ -29,7 +29,7 @@
3116 # pylint: enable=W0611
3117
3118
3119-def main(switch_to='', alert=False):
3120+def main(switch_to='', alert=False, minimized=False, with_icon=False):
3121 """Start the Qt reactor and open the main window."""
3122
3123 # The DBus main loop MUST be initialized before importing the reactor
3124@@ -49,11 +49,9 @@
3125 from qtreactor import qt4reactor
3126 qt4reactor.install()
3127 from twisted.internet import reactor
3128+ from ubuntuone.controlpanel.gui.qt.gui import start
3129
3130- # pylint believes that reactor has no run nor stop methods. Silence it.
3131 # pylint: disable=E1101
3132- from ubuntuone.controlpanel.gui.qt.gui import MainWindow
3133- window = MainWindow(close_callback=reactor.stop)
3134- window.show()
3135-
3136+ icon, window = start(reactor.stop,
3137+ minimized=minimized, with_icon=with_icon)
3138 reactor.run()
3139
3140=== modified file 'ubuntuone/controlpanel/gui/qt/main/windows.py'
3141--- ubuntuone/controlpanel/gui/qt/main/windows.py 2011-07-22 21:26:48 +0000
3142+++ ubuntuone/controlpanel/gui/qt/main/windows.py 2011-09-26 18:38:27 +0000
3143@@ -27,7 +27,7 @@
3144 # pylint: enable=W0611
3145
3146
3147-def main(switch_to='', alert=False):
3148+def main(switch_to='', alert=False, minimized=False, with_icon=False):
3149 """Start the Qt reactor and open the main window."""
3150
3151 # The following cannot be imported outside this function
3152@@ -35,25 +35,22 @@
3153 # pylint: disable=F0401, W0404
3154 import qtreactor.qt4reactor
3155 qtreactor.qt4reactor.install()
3156+ from twisted.internet import reactor
3157+ from ubuntuone.controlpanel.gui.qt.gui import start
3158
3159 # The main loop MUST be initialized before importing the reactor
3160 # pylint: disable=W0612
3161 app = Qt.QApplication(sys.argv)
3162+ # Apply font to the entire application
3163+ QtGui.QFontDatabase.addApplicationFont(':/Ubuntu-R.ttf')
3164+ QtGui.QFontDatabase.addApplicationFont(':/Ubuntu-B.ttf')
3165 # Apply Style Sheet
3166 qss_file = QtCore.QFile(":/ubuntuone.qss")
3167 qss_file.open(QtCore.QFile.ReadOnly)
3168 stylesheet = QtCore.QLatin1String(qss_file.readAll())
3169 app.setStyleSheet(stylesheet)
3170
3171- # Apply font to the entire application
3172- QtGui.QFontDatabase.addApplicationFont(':/Ubuntu-R.ttf')
3173-
3174- # pylint: disable=W0404, F0401
3175- from twisted.internet import reactor
3176-
3177- from ubuntuone.controlpanel.gui.qt.gui import MainWindow
3178- window = MainWindow(close_callback=reactor.stop)
3179- window.show()
3180-
3181+ icon, window = start(reactor.stop,
3182+ minimized=minimized, with_icon=with_icon)
3183 # pylint: disable=E1101
3184 reactor.run()
3185
3186=== modified file 'ubuntuone/controlpanel/gui/qt/preferences.py'
3187--- ubuntuone/controlpanel/gui/qt/preferences.py 2011-07-22 21:26:48 +0000
3188+++ ubuntuone/controlpanel/gui/qt/preferences.py 2011-09-26 18:38:27 +0000
3189@@ -62,7 +62,9 @@
3190 """The Preferences Tab Panel widget"""
3191
3192 ui_class = preferences_ui
3193+ logger = logger
3194
3195+ # pylint: disable=E0202
3196 @defer.inlineCallbacks
3197 def load(self):
3198 """Load info."""
3199
3200=== added file 'ubuntuone/controlpanel/gui/qt/signin.py'
3201--- ubuntuone/controlpanel/gui/qt/signin.py 1970-01-01 00:00:00 +0000
3202+++ ubuntuone/controlpanel/gui/qt/signin.py 2011-09-26 18:38:27 +0000
3203@@ -0,0 +1,89 @@
3204+# -*- coding: utf-8 -*-
3205+
3206+# Authors: Natalia B Bidart <natalia.bidart@canonical.com>
3207+#
3208+# Copyright 2011 Canonical Ltd.
3209+#
3210+# This program is free software: you can redistribute it and/or modify it
3211+# under the terms of the GNU General Public License version 3, as published
3212+# by the Free Software Foundation.
3213+#
3214+# This program is distributed in the hope that it will be useful, but
3215+# WITHOUT ANY WARRANTY; without even the implied warranties of
3216+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
3217+# PURPOSE. See the GNU General Public License for more details.
3218+#
3219+# You should have received a copy of the GNU General Public License along
3220+# with this program. If not, see <http://www.gnu.org/licenses/>.
3221+
3222+"""The signin page."""
3223+
3224+from PyQt4 import QtCore
3225+from twisted.internet import defer
3226+
3227+from ubuntuone.controlpanel.gui import RESET_PASSWORD_LINK
3228+from ubuntuone.controlpanel.gui.qt import icon_from_name, handle_errors
3229+from ubuntuone.controlpanel.gui.qt.ubuntuonebin import UbuntuOneBin
3230+from ubuntuone.controlpanel.gui.qt.ui import signin_ui
3231+from ubuntuone.controlpanel.logger import setup_logging, log_call
3232+
3233+
3234+logger = setup_logging('qt.signin')
3235+
3236+
3237+class SignInPanel(UbuntuOneBin):
3238+ """The widget for signing in."""
3239+
3240+ ui_class = signin_ui
3241+ logger = logger
3242+
3243+ signinCanceled = QtCore.pyqtSignal()
3244+ credentialsFound = QtCore.pyqtSignal(dict)
3245+
3246+ def _setup(self):
3247+ """Do some extra setupping for the UI."""
3248+ super(SignInPanel, self)._setup()
3249+
3250+ self.ui.forgot_password_button.uri = RESET_PASSWORD_LINK
3251+ icon = icon_from_name('external_icon_orange')
3252+ self.ui.forgot_password_button.setIcon(icon)
3253+
3254+ self.ui.signin_button.setEnabled(False)
3255+ for entry in (self.ui.email_entry, self.ui.password_entry):
3256+ entry.textChanged.connect(self.validate)
3257+ entry.returnPressed.connect(self.ui.signin_button.click)
3258+
3259+ def validate(self, *a, **kw):
3260+ """Enable sign in button only if email and password are non empty."""
3261+ email = unicode(self.ui.email_entry.text())
3262+ password = unicode(self.ui.password_entry.text())
3263+ self.ui.signin_button.setEnabled(bool(email and password))
3264+ self.ui.signin_button.style().unpolish(self.ui.signin_button)
3265+ self.ui.signin_button.style().polish(self.ui.signin_button)
3266+
3267+ # pylint: disable=E0202
3268+ @defer.inlineCallbacks
3269+ def load(self):
3270+ """Load specific tab info."""
3271+ yield self.backend.get_credentials()
3272+
3273+ @QtCore.pyqtSlot()
3274+ @handle_errors(logger=logger)
3275+ @log_call(logger.debug)
3276+ @defer.inlineCallbacks
3277+ def on_signin_button_clicked(self):
3278+ """The 'Sign in' button was clicked."""
3279+ email = unicode(self.ui.email_entry.text())
3280+ password = unicode(self.ui.password_entry.text())
3281+ self.is_processing = True
3282+ try:
3283+ result = yield self.backend.login(email=email, password=password)
3284+ logger.info('Emitting credentialsFound for email %r.', email)
3285+ self.credentialsFound.emit(result)
3286+ finally:
3287+ self.is_processing = False
3288+
3289+ @QtCore.pyqtSlot()
3290+ def on_cancel_button_clicked(self):
3291+ """The 'Cancel' button was clicked."""
3292+ self.signinCanceled.emit()
3293
3294=== added file 'ubuntuone/controlpanel/gui/qt/systray.py'
3295--- ubuntuone/controlpanel/gui/qt/systray.py 1970-01-01 00:00:00 +0000
3296+++ ubuntuone/controlpanel/gui/qt/systray.py 2011-09-26 18:38:27 +0000
3297@@ -0,0 +1,68 @@
3298+# -*- coding: utf-8 -*-
3299+
3300+# Authors: Roberto Alsina <roberto.alsina@canonical.com>
3301+#
3302+# Copyright 2011 Canonical Ltd.
3303+#
3304+# This program is free software: you can redistribute it and/or modify it
3305+# under the terms of the GNU General Public License version 3, as published
3306+# by the Free Software Foundation.
3307+#
3308+# This program is distributed in the hope that it will be useful, but
3309+# WITHOUT ANY WARRANTY; without even the implied warranties of
3310+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
3311+# PURPOSE. See the GNU General Public License for more details.
3312+#
3313+# You should have received a copy of the GNU General Public License along
3314+# with this program. If not, see <http://www.gnu.org/licenses/>.
3315+"""System notification area icon."""
3316+
3317+from PyQt4 import QtGui
3318+
3319+
3320+class TrayIcon(QtGui.QSystemTrayIcon):
3321+
3322+ """System notification icon."""
3323+
3324+ def __init__(self, window=None):
3325+ super(TrayIcon, self).__init__(None)
3326+ self.setIcon(QtGui.QIcon(":/u1icon.png"))
3327+ self.setVisible(True)
3328+ self.window = window
3329+ self.activated.connect(self.on_activated)
3330+ self.context_menu = QtGui.QMenu()
3331+ self.restore = QtGui.QAction("Restore", self,
3332+ triggered=self.restore_window)
3333+ self.quit = QtGui.QAction("Quit Ubuntu One", self,
3334+ triggered=self.stop)
3335+ self.context_menu.addAction(self.restore)
3336+ self.context_menu.addSeparator()
3337+ self.context_menu.addAction(self.quit)
3338+ self.setContextMenu(self.context_menu)
3339+
3340+ def on_activated(self, reason):
3341+ """The user activated the icon."""
3342+ if reason == self.Trigger: # Left-click
3343+ self.restore_window()
3344+
3345+ def restore_window(self):
3346+ """Show the main window."""
3347+ if self.window is None:
3348+ # pylint: disable=W0404
3349+ from ubuntuone.controlpanel.gui.qt.gui import MainWindow
3350+ # pylint: enable=W0404
3351+ self.window = MainWindow(close_callback=self.delete_window)
3352+ self.window.show()
3353+
3354+ def delete_window(self):
3355+ """Close and remove the main window."""
3356+ if self.window is not None:
3357+ self.window.close()
3358+ self.window = None
3359+
3360+ def stop(self):
3361+ """Stop the application."""
3362+ # pylint: disable=W0404
3363+ from twisted.internet import reactor
3364+ # pylint: enable=W0404
3365+ reactor.stop()
3366
3367=== modified file 'ubuntuone/controlpanel/gui/qt/tests/__init__.py'
3368--- ubuntuone/controlpanel/gui/qt/tests/__init__.py 2011-07-22 21:26:48 +0000
3369+++ ubuntuone/controlpanel/gui/qt/tests/__init__.py 2011-09-26 18:38:27 +0000
3370@@ -18,12 +18,16 @@
3371
3372 """The test suite for the Qt UI for the control panel for Ubuntu One."""
3373
3374+import logging
3375 import os
3376-
3377-from PyQt4 import QtCore
3378-
3379-from ubuntuone.controlpanel import backend
3380-from ubuntuone.controlpanel.tests import TestCase, EXPECTED_ACCOUNT_INFO
3381+import urllib
3382+
3383+from PyQt4 import QtGui, QtCore
3384+from ubuntuone.devtools.handlers import MementoHandler
3385+
3386+from ubuntuone.controlpanel import backend, cache
3387+from ubuntuone.controlpanel.tests import TestCase, EXPECTED_ACCOUNT_INFO, TOKEN
3388+from ubuntuone.controlpanel.gui import qt, UBUNTUONE_FROM_OAUTH
3389 from ubuntuone.controlpanel.gui.tests import FakedObject, USER_HOME
3390
3391 # Attribute 'yyy' defined outside __init__, access to a protected member
3392@@ -76,21 +80,6 @@
3393 return folder
3394
3395
3396-def skip_if_abstract_class(test):
3397- """Decorator to skip a test if is an abstract class."""
3398-
3399- def inner(instance):
3400- """Skip a test if is an abstract class."""
3401- abstract = instance.class_ui is None
3402- result = None
3403- if not abstract:
3404- result = test(instance)
3405-
3406- return result
3407-
3408- return inner
3409-
3410-
3411 class FakeUi(FakedObject):
3412 """A fake Ui object."""
3413
3414@@ -114,7 +103,7 @@
3415 backend.UPLOAD_KEY: -1, # no limit
3416 }
3417
3418- next_result = object()
3419+ next_result = []
3420 exposed_methods = [
3421 'account_info', # account
3422 'devices_info', 'device_names_info', # devices
3423@@ -128,13 +117,41 @@
3424 'file_sync_settings_info',
3425 'change_file_sync_settings',
3426 'restore_file_sync_settings',
3427- 'shutdown',
3428+ 'shutdown', 'login',
3429 ]
3430
3431-
3432-class FakedConfirmDialog(object):
3433+ def get_credentials(self):
3434+ """Fake credentials retrieval."""
3435+ self._called['get_credentials'] = ((), {})
3436+ return TOKEN
3437+
3438+
3439+class CrashyBackendException(Exception):
3440+ """A faked backend crash."""
3441+
3442+
3443+class CrashyBackend(FakedControlPanelBackend):
3444+ """A faked backend that crashes."""
3445+
3446+ def __init__(self, *args, **kwargs):
3447+ super(CrashyBackend, self).__init__(*args, **kwargs)
3448+ for i in self.exposed_methods + ['get_credentials']:
3449+ setattr(self, i, self._fail(i))
3450+
3451+ def _fail(self, f):
3452+ """Crash boom bang."""
3453+
3454+ def inner(*args, **kwargs):
3455+ """Raise a custom exception."""
3456+ raise CrashyBackendException(f)
3457+
3458+ return inner
3459+
3460+
3461+class FakedDialog(object):
3462 """Fake a confirmation dialog."""
3463
3464+ Close = 0
3465 response = args = kwargs = None
3466
3467 @classmethod
3468@@ -149,6 +166,7 @@
3469 """Fake a file chooser dialog."""
3470
3471 response = args = kwargs = None
3472+ DontUseNativeDialog = 4
3473
3474 # Invalid name "getExistingDirectory"
3475 # pylint: disable=C0103
3476@@ -168,19 +186,43 @@
3477 innerclass_name = None
3478 class_ui = None
3479 kwargs = {}
3480+ logger = None
3481
3482- @skip_if_abstract_class
3483 def setUp(self):
3484+ cache.Cache._shared_objects = {}
3485 super(BaseTestCase, self).setUp()
3486 self.patch(backend, 'ControlBackend', FakedControlPanelBackend)
3487- # self.class_ui is not callable
3488- # pylint: disable=E1102
3489- self.ui = self.class_ui(**self.kwargs)
3490- self.addCleanup(self.ui.destroy)
3491-
3492- if hasattr(self.ui, 'backend'):
3493- # clean backend calls
3494- self.ui.backend._called.clear()
3495+
3496+ self.ui = None
3497+ if self.class_ui is not None:
3498+ # self.class_ui is not callable
3499+ # pylint: disable=E1102
3500+ self.ui = self.class_ui(**self.kwargs)
3501+ # pylint: enable=E1102
3502+ self.addCleanup(self.ui.destroy)
3503+
3504+ if getattr(self.ui, 'backend', None) is not None:
3505+ self.addCleanup(self.ui.backend._called.clear)
3506+
3507+ logger = self.logger if self.logger is not None else \
3508+ getattr(self.ui, 'logger', None)
3509+ self.memento = None
3510+ if logger is not None:
3511+ self.memento = MementoHandler()
3512+ self.memento.setLevel(logging.DEBUG)
3513+ logger.addHandler(self.memento)
3514+ self.addCleanup(logger.removeHandler, self.memento)
3515+
3516+ # default response if user does nothing
3517+ FakedFileDialog.response = QtCore.QString('')
3518+ FakedFileDialog.args = None
3519+ FakedFileDialog.kwargs = None
3520+ self.patch(QtGui, 'QFileDialog', FakedFileDialog)
3521+
3522+ FakedDialog.response = None
3523+ FakedDialog.args = None
3524+ FakedDialog.kwargs = None
3525+ self.patch(QtGui, 'QMessageBox', FakedDialog)
3526
3527 def get_pixmap_data(self, pixmap):
3528 """Get the raw data of a QPixmap."""
3529@@ -202,6 +244,19 @@
3530 self.assertIn(method_name, self.ui.backend._called)
3531 self.assertEqual(self.ui.backend._called[method_name], (args, kwargs))
3532
3533+ def assert_uri_hook_called(self, button, url):
3534+ """Check that uri_hook was called with 'url' when clicking 'button'."""
3535+ self.patch(qt, 'uri_hook', self._set_called)
3536+ button.click()
3537+
3538+ self.assertEqual(len(self._called), 2, 'uri_hook must be called.')
3539+ self.assertEqual(len(self._called[0]), 1, 'uri_hook must be called.')
3540+ actual_url = self._called[0][0]
3541+ if actual_url.startswith(UBUNTUONE_FROM_OAUTH):
3542+ self.assertIn(urllib.urlencode({'next': url}), actual_url)
3543+ else:
3544+ self.assertEqual(actual_url, url)
3545+
3546 def test_init_loads_ui(self, expected_setup_ui=None):
3547 """The __init__ method loads the ui."""
3548 if self.innerclass_ui is None:
3549@@ -219,3 +274,8 @@
3550 expected_setup_ui = expected_setup_ui(self.ui)
3551 self.assertEqual(self.ui.ui._called,
3552 {'setupUi': ((expected_setup_ui,), {})})
3553+
3554+ def test_backend_is_correct(self):
3555+ """The backend instance is correct."""
3556+ if getattr(self.ui, 'backend', None) is not None:
3557+ self.assertIsInstance(self.ui.backend, FakedControlPanelBackend)
3558
3559=== modified file 'ubuntuone/controlpanel/gui/qt/tests/test_account.py'
3560--- ubuntuone/controlpanel/gui/qt/tests/test_account.py 2011-07-22 21:26:48 +0000
3561+++ ubuntuone/controlpanel/gui/qt/tests/test_account.py 2011-09-26 18:38:27 +0000
3562@@ -20,7 +20,6 @@
3563
3564 from twisted.internet import defer
3565
3566-from ubuntuone.controlpanel.gui import qt
3567 from ubuntuone.controlpanel.gui.qt import account as gui
3568 from ubuntuone.controlpanel.gui.qt.tests import (
3569 SAMPLE_ACCOUNT_INFO, SAMPLE_EMAIL, SAMPLE_NAME, SAMPLE_PLAN,
3570@@ -75,14 +74,10 @@
3571
3572 def test_edit_account_button(self):
3573 """When clicking the edit account button, the proper url is opened."""
3574- self.patch(qt, 'uri_hook', self._set_called)
3575- self.ui.ui.edit_profile_button.click()
3576-
3577- self.assertEqual(self._called, ((gui.EDIT_PROFILE_LINK,), {}))
3578+ self.assert_uri_hook_called(self.ui.ui.edit_profile_button,
3579+ gui.EDIT_PROFILE_LINK)
3580
3581 def test_edit_services_button(self):
3582 """When clicking the mobile plan button, the proper url is opened."""
3583- self.patch(qt, 'uri_hook', self._set_called)
3584- self.ui.ui.edit_services_button.click()
3585-
3586- self.assertEqual(self._called, ((gui.EDIT_ACCOUNT_LINK,), {}))
3587+ self.assert_uri_hook_called(self.ui.ui.edit_services_button,
3588+ gui.EDIT_ACCOUNT_LINK)
3589
3590=== modified file 'ubuntuone/controlpanel/gui/qt/tests/test_addfolder.py'
3591--- ubuntuone/controlpanel/gui/qt/tests/test_addfolder.py 2011-07-22 21:26:48 +0000
3592+++ ubuntuone/controlpanel/gui/qt/tests/test_addfolder.py 2011-09-26 18:38:27 +0000
3593@@ -29,7 +29,9 @@
3594 from ubuntuone.controlpanel.gui.qt import addfolder as gui
3595 from ubuntuone.controlpanel.gui.qt.tests import (
3596 BaseTestCase,
3597- FakedConfirmDialog,
3598+ CrashyBackend,
3599+ CrashyBackendException,
3600+ FakedDialog,
3601 FakedFileDialog,
3602 set_path_on_file_dialog,
3603 )
3604@@ -53,20 +55,6 @@
3605 os.environ['HOME'] = USER_HOME
3606 self.addCleanup(lambda: os.environ.__setitem__('HOME', old_home))
3607
3608- # default response if user does nothing
3609- FakedFileDialog.response = gui.QtCore.QString('')
3610- FakedFileDialog.args = None
3611- FakedFileDialog.kwargs = None
3612- self.patch(gui.QtGui, 'QFileDialog', FakedFileDialog)
3613-
3614- FakedConfirmDialog.response = None
3615- FakedConfirmDialog.args = None
3616- FakedConfirmDialog.kwargs = None
3617- self.patch(gui.QtGui, 'QMessageBox', FakedConfirmDialog)
3618-
3619- # reset backend state
3620- self.ui.backend._called.clear()
3621-
3622 @defer.inlineCallbacks
3623 def assert_does_nothing(self, method_call):
3624 """Nothing happens.
3625@@ -92,7 +80,10 @@
3626 yield self.ui.click()
3627
3628 self.assertEqual(FakedFileDialog.args, ())
3629- self.assertEqual(FakedFileDialog.kwargs, {'parent': self.ui})
3630+ self.assertEqual(FakedFileDialog.kwargs, {
3631+ 'options': gui.QtGui.QFileDialog.DontUseNativeDialog,
3632+ 'parent': self.ui,
3633+ })
3634
3635 @defer.inlineCallbacks
3636 def test_does_nothing_if_user_closes_file_chooser(self):
3637@@ -100,8 +91,8 @@
3638 yield self.assert_does_nothing(self.ui.click)
3639
3640 # the warning dialog was not opened
3641- self.assertEqual(FakedConfirmDialog.args, None)
3642- self.assertEqual(FakedConfirmDialog.kwargs, None)
3643+ self.assertEqual(FakedDialog.args, None)
3644+ self.assertEqual(FakedDialog.kwargs, None)
3645
3646 @defer.inlineCallbacks
3647 def test_opens_warning_if_folder_path_not_valid(self):
3648@@ -113,9 +104,9 @@
3649
3650 args = {'folder_path': folder_path, 'home_folder': USER_HOME}
3651 msg = gui.FOLDER_INVALID_PATH % args
3652- self.assertEqual(FakedConfirmDialog.args,
3653+ self.assertEqual(FakedDialog.args,
3654 (self.ui, '', msg, gui.CLOSE))
3655- self.assertEqual(FakedConfirmDialog.kwargs, {})
3656+ self.assertEqual(FakedDialog.kwargs, {})
3657
3658 yield self.assert_does_nothing(self.ui.click)
3659
3660@@ -126,8 +117,8 @@
3661 yield self.ui.click()
3662
3663 # no warning
3664- self.assertEqual(FakedConfirmDialog.args, None)
3665- self.assertEqual(FakedConfirmDialog.kwargs, None)
3666+ self.assertEqual(FakedDialog.args, None)
3667+ self.assertEqual(FakedDialog.kwargs, None)
3668 # backend called
3669 self.assert_backend_called('create_folder', folder_path=folder)
3670
3671@@ -138,8 +129,8 @@
3672 yield self.ui.click()
3673
3674 # no warning
3675- self.assertEqual(FakedConfirmDialog.args, None)
3676- self.assertEqual(FakedConfirmDialog.kwargs, None)
3677+ self.assertEqual(FakedDialog.args, None)
3678+ self.assertEqual(FakedDialog.kwargs, None)
3679 # backend called
3680 self.assert_backend_called('create_folder', folder_path=folder)
3681
3682@@ -167,3 +158,12 @@
3683 yield self.ui.click()
3684
3685 self.assertEqual(self._called, ((), {}))
3686+
3687+ @defer.inlineCallbacks
3688+ def test_backend_error_is_handled(self):
3689+ """Any error from the backend is properly handled."""
3690+ set_path_on_file_dialog()
3691+ self.patch(self.ui, 'backend', CrashyBackend())
3692+ yield self.ui.click()
3693+
3694+ self.assertTrue(self.memento.check_exception(CrashyBackendException))
3695
3696=== added file 'ubuntuone/controlpanel/gui/qt/tests/test_common.py'
3697--- ubuntuone/controlpanel/gui/qt/tests/test_common.py 1970-01-01 00:00:00 +0000
3698+++ ubuntuone/controlpanel/gui/qt/tests/test_common.py 2011-09-26 18:38:27 +0000
3699@@ -0,0 +1,210 @@
3700+# -*- coding: utf-8 -*-
3701+
3702+# Authors: Natalia B Bidart <natalia.bidart@canonical.com>
3703+#
3704+# Copyright 2011 Canonical Ltd.
3705+#
3706+# This program is free software: you can redistribute it and/or modify it
3707+# under the terms of the GNU General Public License version 3, as published
3708+# by the Free Software Foundation.
3709+#
3710+# This program is distributed in the hope that it will be useful, but
3711+# WITHOUT ANY WARRANTY; without even the implied warranties of
3712+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
3713+# PURPOSE. See the GNU General Public License for more details.
3714+#
3715+# You should have received a copy of the GNU General Public License along
3716+# with this program. If not, see <http://www.gnu.org/licenses/>.
3717+
3718+"""Tests for the uri_hook helper."""
3719+
3720+import logging
3721+
3722+from PyQt4 import QtGui, QtCore
3723+from twisted.internet import defer
3724+
3725+from ubuntuone.controlpanel.logger import setup_logging
3726+from ubuntuone.controlpanel.gui.qt import uri_hook, handle_errors
3727+from ubuntuone.controlpanel.gui.qt.tests import (
3728+ BaseTestCase,
3729+ FakedDialog,
3730+)
3731+
3732+
3733+class UriHookTestCase(BaseTestCase):
3734+ """The test suite for the uri_hook."""
3735+
3736+ @defer.inlineCallbacks
3737+ def setUp(self):
3738+ yield super(UriHookTestCase, self).setUp()
3739+ self.patch(QtGui.QDesktopServices, 'openUrl', self._set_called)
3740+
3741+ def test_opens_url(self):
3742+ """The url is opened properly."""
3743+ expected = 'foo bar'
3744+ uri_hook(expected)
3745+ self.assertEqual(self._called, ((QtCore.QUrl(expected),), {}))
3746+
3747+ def test_opens_url_encoded(self):
3748+ """The encoded url is opened properly."""
3749+ expected = 'http://foo.bar/?next=https://one.ubuntu.com/'
3750+ uri_hook(expected)
3751+ self.assertEqual(self._called, ((QtCore.QUrl(expected),), {}))
3752+
3753+
3754+class HandleErrorTestCase(BaseTestCase):
3755+ """Test suite for the generic error handler."""
3756+
3757+ error_handler = None
3758+ use_logger = False
3759+ logger = logging.getLogger() # root logger
3760+
3761+ @defer.inlineCallbacks
3762+ def setUp(self):
3763+ yield super(HandleErrorTestCase, self).setUp()
3764+ self.called = None
3765+ self.result = None
3766+ self.failure = None
3767+ self.error_handler_called = None
3768+
3769+ if self.use_logger:
3770+ logger = self.logger
3771+ else:
3772+ logger = None
3773+
3774+ self.decorate_me = handle_errors(error_handler=self.error_handler,
3775+ logger=logger)(self._decorate_me)
3776+
3777+ @defer.inlineCallbacks
3778+ def _decorate_me(self, *args, **kwargs):
3779+ """Helper to test thye decorator."""
3780+ if self.failure:
3781+ # Raising only classes, instances or string are allowed
3782+ # pylint: disable=E0702
3783+ raise self.failure
3784+
3785+ yield
3786+ self.called = (args, kwargs)
3787+ defer.returnValue(self.result)
3788+
3789+ @defer.inlineCallbacks
3790+ def test_is_decorator(self):
3791+ """Is a decorator."""
3792+ yield self.decorate_me()
3793+
3794+ @defer.inlineCallbacks
3795+ def test_params_are_passed(self):
3796+ """Named and unnamed arguments are passed."""
3797+ args = ({}, object(), 'foo')
3798+ kwargs = {'1': 1, 'test': None, '0': ['a']}
3799+ yield self.decorate_me(*args, **kwargs)
3800+
3801+ self.assertTrue(self.called, (args, kwargs))
3802+
3803+ @defer.inlineCallbacks
3804+ def test_result_is_returned(self):
3805+ """Result is returned."""
3806+ self.result = object()
3807+ result = yield self.decorate_me()
3808+
3809+ self.assertEqual(self.result, result)
3810+
3811+ def test_name_is_preserved(self):
3812+ """The method name is not masqueraded."""
3813+ self.assertEqual(self.decorate_me.__name__, self._decorate_me.__name__)
3814+
3815+ @defer.inlineCallbacks
3816+ def test_exeptions_are_handled(self):
3817+ """Any exception is handled and logged in the root logger."""
3818+ msg = 'This is me failing badly.'
3819+ self.failure = Exception(msg)
3820+
3821+ yield self.decorate_me()
3822+
3823+ logged = self.memento.check_exception(self.failure.__class__, msg)
3824+ recs = '\n'.join(rec.exc_text for rec in self.memento.records)
3825+ self.assertTrue(logged, 'Exception must be logged, got:\n%s' % recs)
3826+
3827+ @defer.inlineCallbacks
3828+ def test_show_error_message(self):
3829+ """On error, show an error message."""
3830+ self.failure = Exception()
3831+
3832+ yield self.decorate_me()
3833+
3834+ msg = self.failure.__class__.__name__
3835+ self.assertEqual(FakedDialog.args,
3836+ (None, '', msg, QtGui.QMessageBox.Close))
3837+ self.assertEqual(FakedDialog.kwargs, {})
3838+
3839+ @defer.inlineCallbacks
3840+ def test_show_error_message_if_args(self):
3841+ """On error, show an error message."""
3842+ msg1 = 'This is me failing badly.'
3843+ msg2 = 'More details about what went wrong.'
3844+ obj = object()
3845+ self.failure = Exception(msg1, msg2, obj)
3846+
3847+ yield self.decorate_me()
3848+
3849+ msg = '\n'.join(map(repr, (msg1, msg2, obj)))
3850+ msg = self.failure.__class__.__name__ + '\n' + msg
3851+ self.assertEqual(FakedDialog.args,
3852+ (None, '', msg, QtGui.QMessageBox.Close))
3853+ self.assertEqual(FakedDialog.kwargs, {})
3854+
3855+ @defer.inlineCallbacks
3856+ def test_show_error_message_if_mapping(self):
3857+ """On error, show an error message."""
3858+ msg1 = 'This is me failing badly.'
3859+ msg2 = 'More details about what went wrong.'
3860+ errdict = {'foo': msg1, 'bar': msg2}
3861+ self.failure = Exception(errdict)
3862+
3863+ yield self.decorate_me()
3864+
3865+ msg = '\n'.join((msg1, msg2))
3866+ self.assertEqual(FakedDialog.args,
3867+ (None, '', msg, QtGui.QMessageBox.Close))
3868+ self.assertEqual(FakedDialog.kwargs, {})
3869+
3870+ @defer.inlineCallbacks
3871+ def test_no_error_message_if_no_exception(self):
3872+ """On success, do not show an error message."""
3873+ yield self.decorate_me()
3874+
3875+ self.assertEqual(FakedDialog.args, None)
3876+ self.assertEqual(FakedDialog.kwargs, None)
3877+
3878+ @defer.inlineCallbacks
3879+ def test_call_error_handler(self):
3880+ """On success, do not execute error_handler."""
3881+ yield self.decorate_me()
3882+ self.assertFalse(self.error_handler_called)
3883+
3884+
3885+class HandleErrorWithCustomLoggerTestCase(HandleErrorTestCase):
3886+ """Test suite for the generic error handler."""
3887+
3888+ use_logger = True
3889+ logger = setup_logging('HandleErrorWithoutLoggerTestCase') # custom logger
3890+
3891+
3892+class HandleErrorWithHandlerTestCase(HandleErrorTestCase):
3893+ """Test suite for the generic error handler when using a custom handler."""
3894+
3895+ @defer.inlineCallbacks
3896+ def error_handler(self, *a, **kw):
3897+ """Implement an error handler."""
3898+ self.error_handler_called = (a, kw)
3899+ yield
3900+
3901+ @defer.inlineCallbacks
3902+ def test_call_error_handler(self):
3903+ """On error, execute error_handler."""
3904+ msg = 'This is me failing badly.'
3905+ self.failure = Exception(msg)
3906+
3907+ yield self.decorate_me()
3908+
3909+ self.assertEqual(self.error_handler_called, ((), {}))
3910
3911=== modified file 'ubuntuone/controlpanel/gui/qt/tests/test_controlpanel.py'
3912--- ubuntuone/controlpanel/gui/qt/tests/test_controlpanel.py 2011-07-22 21:26:48 +0000
3913+++ ubuntuone/controlpanel/gui/qt/tests/test_controlpanel.py 2011-09-26 18:38:27 +0000
3914@@ -22,14 +22,16 @@
3915
3916 from twisted.internet import defer
3917
3918-from ubuntuone.controlpanel.gui import qt
3919 from ubuntuone.controlpanel.gui.qt import controlpanel as gui
3920 from ubuntuone.controlpanel.gui.qt.tests import (
3921- BaseTestCase, SAMPLE_ACCOUNT_INFO, SAMPLE_NAME,
3922-)
3923-
3924-
3925-class ControlPanelTestCase(BaseTestCase):
3926+ SAMPLE_ACCOUNT_INFO, SAMPLE_NAME, TOKEN,
3927+)
3928+from ubuntuone.controlpanel.gui.qt.tests.test_ubuntuonebin import (
3929+ UbuntuOneBinTestCase,
3930+)
3931+
3932+
3933+class ControlPanelTestCase(UbuntuOneBinTestCase):
3934 """Test the qt control panel."""
3935
3936 innerclass_ui = gui.controlpanel_ui
3937@@ -39,18 +41,68 @@
3938 @defer.inlineCallbacks
3939 def setUp(self):
3940 yield super(ControlPanelTestCase, self).setUp()
3941+ self.patch(self.ui.ui.folders_tab, 'process_info', lambda info: None)
3942 self.ui.backend.next_result = SAMPLE_ACCOUNT_INFO
3943
3944- def test_backend(self):
3945- """Backend is created."""
3946- self.assertIsInstance(self.ui.backend,
3947- gui.backend.ControlBackend)
3948+ @defer.inlineCallbacks
3949+ def test_is_processing_while_asking_info(self):
3950+ """The ui is processing while the contents are loaded."""
3951+ def check():
3952+ """The ui must be is_processing."""
3953+ self.assertTrue(self.ui.is_processing, 'ui must be processing')
3954+ return {}
3955+
3956+ self.patch(self.ui.backend, 'get_credentials', check)
3957+
3958+ yield self.ui.load() # trigger the info request
3959+ self.assertFalse(self.ui.is_processing, 'ui must not be processing')
3960+
3961+ @defer.inlineCallbacks
3962+ def test_credentials_are_requested_on_load(self):
3963+ """The info is requested to the backend."""
3964+ yield self.ui.load()
3965+ self.assert_backend_called('get_credentials')
3966+
3967+ @defer.inlineCallbacks
3968+ def test_on_credentials_not_found_called(self):
3969+ """If no credentials, on_credentials_not_found is called."""
3970+ self.patch(self.ui, 'on_credentials_not_found', self._set_called)
3971+ self.patch(self.ui.backend, 'get_credentials', lambda: {})
3972+ yield self.ui.load()
3973+
3974+ self.assertEqual(self._called, ((), {}))
3975+
3976+ def test_on_credentials_not_found(self):
3977+ """The signin panel is shown."""
3978+ self.ui.on_credentials_found()
3979+ self.ui.on_credentials_not_found()
3980+ self.assertIs(self.ui.ui.switcher.currentWidget(), self.ui.ui.signin)
3981+
3982+ @defer.inlineCallbacks
3983+ def test_on_credentials_found_called(self):
3984+ """If credentials, on_credentials_not_found is called."""
3985+ self.patch(self.ui, 'on_credentials_found', self._set_called)
3986+ yield self.ui.load()
3987+
3988+ self.assertEqual(self._called, ((), {}))
3989+
3990+ def test_on_credentials_found(self):
3991+ """The management panel is shown."""
3992+ self.patch(self.ui, 'connect_file_sync', self._set_called)
3993+ self.ui.on_credentials_not_found()
3994+ self.ui.on_credentials_found()
3995+
3996+ self.assertIs(self.ui.ui.switcher.currentWidget(),
3997+ self.ui.ui.management)
3998+ self.assertEqual(self._called, ((), {}))
3999
4000 @defer.inlineCallbacks
4001 def test_info_is_requested_on_load(self):
4002 """The info is requested to the backend."""
4003+ self.patch(self.ui, 'process_info', self._set_called)
4004 yield self.ui.load()
4005 self.assert_backend_called('account_info')
4006+ self.assertEqual(self._called, ((SAMPLE_ACCOUNT_INFO,), {}))
4007
4008 def test_process_info(self):
4009 """The info is processed when ready."""
4010@@ -72,32 +124,85 @@
4011 msg = gui.PERCENTAGE_LABEL % percentage_usage
4012 self.assertEqual(self.ui.ui.percentage_usage_label.text(), msg)
4013
4014+ def test_update_over_quota(self):
4015+ """Check the labels state when the user exceed the quota."""
4016+ percentage_value = 100
4017+ # pylint: disable=W0212
4018+ self.ui._update_quota({'percentage': percentage_value})
4019+ # pylint: enable=W0212
4020+
4021+ self.assertTrue(
4022+ self.ui.ui.percentage_usage_label.property("OverQuota").toBool())
4023+ self.assertTrue(
4024+ self.ui.ui.quota_usage_label.property("OverQuota").toBool())
4025+
4026+ def test_update_quota_in_range(self):
4027+ """Check the labels state when the quota is under the threshold."""
4028+ percentage_value = 50
4029+ # pylint: disable=W0212
4030+ self.ui._update_quota({'percentage': percentage_value})
4031+ # pylint: enable=W0212
4032+
4033+ self.assertFalse(
4034+ self.ui.ui.percentage_usage_label.property("OverQuota").toBool())
4035+ self.assertFalse(
4036+ self.ui.ui.quota_usage_label.property("OverQuota").toBool())
4037+
4038+ def test_on_local_device_removed(self):
4039+ """On DeviesPanel's localDeviceRemoved, emit credentialsNotFound."""
4040+ self.ui.ui.devices_tab.localDeviceRemoved.emit()
4041+ self.assertIs(self.ui.ui.switcher.currentWidget(), self.ui.ui.signin)
4042+
4043+ def test_on_signin_credentials_found(self):
4044+ """On SignInPanel's credentialsFound, the management panel is shown."""
4045+ self.patch(self.ui, 'load', self._set_called)
4046+ self.ui.ui.signin.credentialsFound.emit(TOKEN)
4047+
4048+ self.assertEqual(self._called, ((), {}))
4049+
4050+ @defer.inlineCallbacks
4051+ def test_connect_file_sync_with_autoconnect(self):
4052+ """Connect is called if autoconnect is enabled."""
4053+ settings = {gui.AUTOCONNECT_KEY: True}
4054+ self.patch(self.ui.backend, 'file_sync_settings_info',
4055+ lambda: defer.succeed(settings))
4056+
4057+ yield self.ui.connect_file_sync()
4058+
4059+ self.assert_backend_called('connect_files')
4060+
4061+ @defer.inlineCallbacks
4062+ def test_connect_file_sync_without_autoconnect(self):
4063+ """Connect is called if autoconnect is disabled."""
4064+ settings = {gui.AUTOCONNECT_KEY: False}
4065+ self.patch(self.ui.backend, 'file_sync_settings_info',
4066+ lambda: defer.succeed(settings))
4067+
4068+ yield self.ui.connect_file_sync()
4069+
4070+ # pylint: disable=W0212
4071+ self.assertNotIn('connect_files', self.ui.backend._called)
4072+
4073
4074 class ExternalLinkButtonsTestCase(ControlPanelTestCase):
4075 """The link in the go-to-web buttons are correct."""
4076
4077- @defer.inlineCallbacks
4078- def setUp(self):
4079- self.patch(qt, 'uri_hook', self._set_called)
4080- self.patch(gui, 'uri_hook', self._set_called)
4081- yield super(ExternalLinkButtonsTestCase, self).setUp()
4082-
4083 def test_get_more_space_button(self):
4084 """When clicking the get more GB button, the proper url is opened."""
4085- self.ui.ui.get_more_space_button.click()
4086- self.assertEqual(self._called, ((gui.EDIT_ACCOUNT_LINK,), {}))
4087+ self.assert_uri_hook_called(self.ui.ui.get_more_space_button,
4088+ gui.EDIT_SERVICES_LINK)
4089
4090 def test_help_button(self):
4091 """When clicking the help button, the proper url is opened."""
4092- self.ui.ui.help_button.click()
4093- self.assertEqual(self._called, ((gui.GET_SUPPORT_LINK,), {}))
4094+ self.assert_uri_hook_called(self.ui.ui.help_button,
4095+ gui.GET_SUPPORT_LINK)
4096
4097 def test_twitter_button(self):
4098 """When clicking the twitter button, the proper url is opened."""
4099- self.ui.ui.twitter_button.click()
4100- self.assertEqual(self._called, ((gui.TWITTER_LINK,), {}))
4101+ self.assert_uri_hook_called(self.ui.ui.twitter_button,
4102+ gui.TWITTER_LINK)
4103
4104 def test_facebook_button(self):
4105 """When clicking the facebook button, the proper url is opened."""
4106- self.ui.ui.facebook_button.click()
4107- self.assertEqual(self._called, ((gui.FACEBOOK_LINK,), {}))
4108+ self.assert_uri_hook_called(self.ui.ui.facebook_button,
4109+ gui.FACEBOOK_LINK)
4110
4111=== modified file 'ubuntuone/controlpanel/gui/qt/tests/test_device.py'
4112--- ubuntuone/controlpanel/gui/qt/tests/test_device.py 2011-07-22 21:26:48 +0000
4113+++ ubuntuone/controlpanel/gui/qt/tests/test_device.py 2011-09-26 18:38:27 +0000
4114@@ -23,8 +23,9 @@
4115 from ubuntuone.controlpanel.gui.qt import device as gui
4116 from ubuntuone.controlpanel.gui.qt.tests import (
4117 BaseTestCase,
4118- FakedConfirmDialog,
4119- FakedControlPanelBackend,
4120+ CrashyBackend,
4121+ CrashyBackendException,
4122+ FakedDialog,
4123 SAMPLE_COMPUTER_INFO,
4124 SAMPLE_PHONE_INFO,
4125 )
4126@@ -40,18 +41,14 @@
4127 innerclass_ui = gui.device_ui
4128 innerclass_name = "Ui_Form"
4129 class_ui = gui.DeviceWidget
4130- backend = FakedControlPanelBackend()
4131 device_id = 'zaraza'
4132- kwargs = {'backend': backend, 'device_id': device_id}
4133+ kwargs = {'device_id': device_id}
4134+ logger = gui.logger
4135
4136 def test_has_id(self):
4137 """The device as an id, None by default."""
4138 self.assertEqual(self.ui.id, self.device_id)
4139
4140- def test_has_backend(self):
4141- """The device as a backend, None by default."""
4142- self.assertIs(self.ui.backend, self.backend)
4143-
4144 def test_update_device_info(self):
4145 """The widget is updated with the info."""
4146 info = SAMPLE_COMPUTER_INFO
4147@@ -104,10 +101,7 @@
4148 @defer.inlineCallbacks
4149 def setUp(self):
4150 yield super(RemoveDeviceTestCase, self).setUp()
4151- FakedConfirmDialog.response = gui.NO
4152- FakedConfirmDialog.args = None
4153- FakedConfirmDialog.kwargs = None
4154- self.patch(gui.QtGui, 'QMessageBox', FakedConfirmDialog)
4155+ FakedDialog.response = gui.YES
4156
4157 def test_remove_device_opens_confirmation_dialog(self):
4158 """A confirmation dialog is opened when user clicks 'delete device'."""
4159@@ -115,13 +109,13 @@
4160
4161 msg = gui.DEVICE_CONFIRM_REMOVE
4162 buttons = gui.YES | gui.NO
4163- self.assertEqual(FakedConfirmDialog.args,
4164+ self.assertEqual(FakedDialog.args,
4165 (self.ui, '', msg, buttons, gui.NO))
4166- self.assertEqual(FakedConfirmDialog.kwargs, {})
4167+ self.assertEqual(FakedDialog.kwargs, {})
4168
4169 def test_remove_device_does_not_remove_if_answer_is_no(self):
4170 """The device is not removed is answer is No."""
4171- FakedConfirmDialog.response = gui.NO
4172+ FakedDialog.response = gui.NO
4173 self.ui.removed.connect(self._set_called)
4174 self.ui.ui.remove_device_button.click()
4175
4176@@ -130,7 +124,7 @@
4177
4178 def test_remove_device_does_remove_if_answer_is_yes(self):
4179 """The device is removed is answer is Yes."""
4180- FakedConfirmDialog.response = gui.YES
4181+ FakedDialog.response = gui.YES
4182 self.ui.ui.remove_device_button.click()
4183
4184 self.assert_backend_called('remove_device', device_id=self.device_id)
4185@@ -144,7 +138,7 @@
4186 """Fire deferred when the device was removed."""
4187 d.callback(device_id)
4188
4189- FakedConfirmDialog.response = gui.YES
4190+ FakedDialog.response = gui.YES
4191 self.ui.removed.connect(self._set_called)
4192 self.patch(self.ui.backend, 'remove_device', check)
4193 self.ui.ui.remove_device_button.click()
4194@@ -154,8 +148,16 @@
4195
4196 def test_remove_device_emits_signal_when_not_removed(self):
4197 """The signal 'removeCanceled' is emitted when user cancels removal."""
4198- FakedConfirmDialog.response = gui.NO
4199+ FakedDialog.response = gui.NO
4200 self.ui.removeCanceled.connect(self._set_called)
4201 self.ui.ui.remove_device_button.click()
4202
4203 self.assertEqual(self._called, ((), {}))
4204+
4205+ @defer.inlineCallbacks
4206+ def test_backend_error_is_handled(self):
4207+ """Any error from the backend is properly handled."""
4208+ self.patch(self.ui, 'backend', CrashyBackend())
4209+ yield self.ui.ui.remove_device_button.click()
4210+
4211+ self.assertTrue(self.memento.check_exception(CrashyBackendException))
4212
4213=== modified file 'ubuntuone/controlpanel/gui/qt/tests/test_devices.py'
4214--- ubuntuone/controlpanel/gui/qt/tests/test_devices.py 2011-07-22 21:26:48 +0000
4215+++ ubuntuone/controlpanel/gui/qt/tests/test_devices.py 2011-09-26 18:38:27 +0000
4216@@ -20,10 +20,8 @@
4217
4218 from twisted.internet import defer
4219
4220-from ubuntuone.controlpanel.gui import qt
4221 from ubuntuone.controlpanel.gui.qt import devices as gui
4222 from ubuntuone.controlpanel.gui.qt.tests import (
4223- FakedConfirmDialog,
4224 SAMPLE_DEVICES_INFO,
4225 )
4226 from ubuntuone.controlpanel.gui.qt.tests.test_ubuntuonebin import (
4227@@ -42,7 +40,6 @@
4228 def setUp(self):
4229 yield super(DevicesPanelTestCase, self).setUp()
4230 self.ui.backend.next_result = SAMPLE_DEVICES_INFO
4231- self.patch(gui.QtGui, 'QMessageBox', FakedConfirmDialog)
4232
4233 def test_is_processing_while_asking_info(self):
4234 """The ui is processing while the contents are loaded."""
4235@@ -97,10 +94,8 @@
4236
4237 def test_manage_devices_button(self):
4238 """Clicking the manage devices button opens the proper url."""
4239- self.patch(qt, 'uri_hook', self._set_called)
4240- self.ui.ui.manage_devices_button.click()
4241-
4242- self.assertEqual(self._called, ((gui.EDIT_DEVICES_LINK,), {}))
4243+ self.assert_uri_hook_called(self.ui.ui.manage_devices_button,
4244+ gui.EDIT_DEVICES_LINK)
4245
4246 def test_remove_device_widget_after_removal(self):
4247 """When a device widget was deleted, remove it from the UI."""
4248@@ -110,3 +105,13 @@
4249 local_device.removed.emit()
4250
4251 self.assertTrue(self.ui.ui.local_device_box.itemAt(0) is None)
4252+
4253+ def test_local_device_removed_signal(self):
4254+ """When the local device is removed, emit localDeviceRemoved signal."""
4255+ self.ui.localDeviceRemoved.connect(self._set_called)
4256+ self.ui.process_info(SAMPLE_DEVICES_INFO)
4257+
4258+ local_device = self.ui.ui.local_device_box.itemAt(0).widget()
4259+ local_device.removed.emit()
4260+
4261+ self.assertEqual(self._called, ((), {}))
4262
4263=== modified file 'ubuntuone/controlpanel/gui/qt/tests/test_folders.py'
4264--- ubuntuone/controlpanel/gui/qt/tests/test_folders.py 2011-08-12 19:12:08 +0000
4265+++ ubuntuone/controlpanel/gui/qt/tests/test_folders.py 2011-09-26 18:38:27 +0000
4266@@ -31,11 +31,9 @@
4267 FAKE_VOLUMES_NO_FREE_SPACE_INFO,
4268 MUSIC_FOLDER, ROOT, USER_HOME,
4269 )
4270-from ubuntuone.controlpanel.gui import qt
4271 from ubuntuone.controlpanel.gui.qt import folders as gui
4272 from ubuntuone.controlpanel.gui.qt.tests import (
4273- FakedConfirmDialog,
4274- FakedFileDialog,
4275+ FakedDialog,
4276 )
4277 from ubuntuone.controlpanel.gui.qt.tests.test_ubuntuonebin import (
4278 UbuntuOneBinTestCase,
4279@@ -140,8 +138,10 @@
4280 self.assertTrue(item is not None)
4281
4282 name = volume['path'].replace(USER_HOME + os.path.sep, '')
4283+ expected_path = volume['path']
4284 if volume['type'] == self.ui.backend.SHARE_TYPE:
4285 name = volume['name']
4286+ expected_path = volume['realpath']
4287 label = item.text(gui.FOLDER_NAME_COL)
4288 self.assertEqual(label, name)
4289
4290@@ -161,7 +161,7 @@
4291 self.assertEqual(icon_name, gui.SHARE_ICON_NAME)
4292
4293 self.assertEqual(item.volume_id, volume['volume_id'])
4294- self.assertEqual(item.volume_path, volume['path'])
4295+ self.assertEqual(item.volume_path, expected_path)
4296
4297 # tooltips are correct
4298 self.assertEqual(item.toolTip(gui.FOLDER_NAME_COL), name)
4299@@ -175,6 +175,8 @@
4300 gui.FOLDER_ICON_NAME)
4301 self.assertEqual(button.iconSize().width(), 12)
4302 self.assertEqual(button.iconSize().height(), 12)
4303+ self.assertEqual(button.isEnabled(),
4304+ bool(volume['subscribed']))
4305
4306 treeiter += 1
4307 item = treeiter.value()
4308@@ -245,7 +247,8 @@
4309 self.ui.on_folders_itemActivated(item)
4310
4311 self.assertEqual(self._called,
4312- ((gui.FILE_URI_PREFIX + path,), {}))
4313+ ((unicode(gui.QtCore.QUrl.fromLocalFile(path).toString()),), {}))
4314+ self.assertTrue(gui.QtCore.QUrl(self._called[0][0]).isValid())
4315
4316 def test_clicking_on_row_handles_path_none(self):
4317 """None paths are properly handled."""
4318@@ -302,10 +305,8 @@
4319
4320 def test_share_publish_button(self):
4321 """When clicking the share/publish button, the proper url is opened."""
4322- self.patch(qt, 'uri_hook', self._set_called)
4323- self.ui.ui.share_publish_button.click()
4324-
4325- self.assertEqual(self._called, ((gui.MANAGE_FILES_LINK,), {}))
4326+ self.assert_uri_hook_called(self.ui.ui.share_publish_button,
4327+ gui.MANAGE_FILES_LINK)
4328
4329
4330 class FoldersPanelAddFolderTestCase(FoldersPanelTestCase):
4331@@ -322,17 +323,6 @@
4332 # reset backend state
4333 self.ui.backend._called.clear()
4334
4335- # default response if user does nothing
4336- FakedFileDialog.response = gui.QtCore.QString('')
4337- FakedFileDialog.args = None
4338- FakedFileDialog.kwargs = None
4339- self.patch(gui.QtGui, 'QFileDialog', FakedFileDialog)
4340-
4341- FakedConfirmDialog.response = None
4342- FakedConfirmDialog.args = None
4343- FakedConfirmDialog.kwargs = None
4344- self.patch(gui.QtGui, 'QMessageBox', FakedConfirmDialog)
4345-
4346 def test_not_is_processing(self):
4347 """Before clicking the add folder button, the UI is not processing."""
4348 self.assertFalse(self.ui.is_processing, 'ui must not be processing')
4349@@ -356,10 +346,7 @@
4350 def setUp(self):
4351 yield super(FoldersPanelSubscriptionTestCase, self).setUp()
4352 self.patch(gui.os.path, 'exists', lambda path: True)
4353- FakedConfirmDialog.response = gui.YES
4354- FakedConfirmDialog.args = None
4355- FakedConfirmDialog.kwargs = None
4356- self.patch(gui.QtGui, 'QMessageBox', FakedConfirmDialog)
4357+ FakedDialog.response = gui.YES
4358
4359 self.ui.process_info(FAKE_VOLUMES_MINIMAL_INFO)
4360 # the music folder
4361@@ -368,6 +355,7 @@
4362 @defer.inlineCallbacks
4363 def test_on_folders_item_changed(self):
4364 """Clicking on 'subscribed' updates the folder subscription."""
4365+ self.patch(self.ui, 'load', self._set_called)
4366 volume = MUSIC_FOLDER
4367 fid = volume['volume_id']
4368 subscribed = not bool(volume['subscribed'])
4369@@ -386,6 +374,9 @@
4370 value = self.item.checkState(gui.SUBSCRIPTION_COL) == gui.CHECKED
4371 self.assertEqual(value, bool(subscribed))
4372
4373+ # folder list was reloaded
4374+ self.assertEqual(self._called, ((), {}))
4375+
4376 @defer.inlineCallbacks
4377 def test_on_folders_item_changed_is_processing(self):
4378 """Clicking on 'subscribed' sets is_processing flag until done."""
4379@@ -438,9 +429,9 @@
4380 volume_path = self.item.volume_path
4381 msg = gui.FOLDERS_CONFIRM_MERGE % {'folder_path': volume_path}
4382 buttons = gui.YES | gui.NO | gui.CANCEL
4383- self.assertEqual(FakedConfirmDialog.args,
4384+ self.assertEqual(FakedDialog.args,
4385 (self.ui, '', msg, buttons, gui.YES))
4386- self.assertEqual(FakedConfirmDialog.kwargs, {})
4387+ self.assertEqual(FakedDialog.kwargs, {})
4388
4389 @defer.inlineCallbacks
4390 def test_confirm_dialog_if_path_does_not_exist(self):
4391@@ -454,14 +445,13 @@
4392
4393 yield self.ui.on_folders_itemChanged(self.item)
4394
4395- self.assertEqual(FakedConfirmDialog.args, None)
4396- self.assertEqual(FakedConfirmDialog.kwargs, None)
4397+ self.assertEqual(FakedDialog.args, None)
4398+ self.assertEqual(FakedDialog.kwargs, None)
4399
4400 @defer.inlineCallbacks
4401 def test_subscribe_does_not_call_backend_if_dialog_closed(self):
4402 """Backend is not called if users closes the confirmation dialog."""
4403- FakedConfirmDialog.response = gui.CANCEL
4404- self.patch(gui.QtGui, 'QMessageBox', FakedConfirmDialog)
4405+ FakedDialog.response = gui.CANCEL
4406
4407 # make sure the item is subscribed
4408 self.ui.is_processing = True
4409@@ -470,7 +460,7 @@
4410
4411 yield self.ui.on_folders_itemChanged(self.item)
4412
4413- self.assertFalse(FakedConfirmDialog.args is None, 'warning was called')
4414+ self.assertFalse(FakedDialog.args is None, 'warning was called')
4415 self.assertNotIn('change_volume_settings', self.ui.backend._called)
4416 self.assertFalse(self.ui.is_processing)
4417
4418@@ -480,8 +470,7 @@
4419 @defer.inlineCallbacks
4420 def test_subscribe_does_not_call_backend_if_answer_is_no(self):
4421 """Backend is not called if users clicks on 'No'."""
4422- FakedConfirmDialog.response = gui.NO
4423- self.patch(gui.QtGui, 'QMessageBox', FakedConfirmDialog)
4424+ FakedDialog.response = gui.NO
4425
4426 # make sure the item is subscribed
4427 self.ui.is_processing = True
4428@@ -490,7 +479,7 @@
4429
4430 yield self.ui.on_folders_itemChanged(self.item)
4431
4432- self.assertFalse(FakedConfirmDialog.args is None, 'warning was called')
4433+ self.assertFalse(FakedDialog.args is None, 'warning was called')
4434 self.assertNotIn('change_volume_settings', self.ui.backend._called)
4435 self.assertFalse(self.ui.is_processing)
4436
4437@@ -506,10 +495,10 @@
4438 self.ui.is_processing = False
4439
4440 # the confirm dialog was not called so far
4441- assert FakedConfirmDialog.args is None
4442- assert FakedConfirmDialog.kwargs is None
4443+ assert FakedDialog.args is None
4444+ assert FakedDialog.kwargs is None
4445
4446 yield self.ui.on_folders_itemChanged(self.item)
4447
4448- self.assertTrue(FakedConfirmDialog.args is None, 'dialog was not run')
4449- self.assertTrue(FakedConfirmDialog.kwargs is None, 'dialog was hid')
4450+ self.assertTrue(FakedDialog.args is None, 'dialog was not run')
4451+ self.assertTrue(FakedDialog.kwargs is None, 'dialog was hid')
4452
4453=== modified file 'ubuntuone/controlpanel/gui/qt/tests/test_gotoweb.py'
4454--- ubuntuone/controlpanel/gui/qt/tests/test_gotoweb.py 2011-07-22 21:26:48 +0000
4455+++ ubuntuone/controlpanel/gui/qt/tests/test_gotoweb.py 2011-09-26 18:38:27 +0000
4456@@ -20,7 +20,7 @@
4457
4458 from twisted.internet import defer
4459
4460-from ubuntuone.controlpanel.gui import qt
4461+from ubuntuone.controlpanel.gui import qt, UBUNTUONE_LINK
4462 from ubuntuone.controlpanel.gui.qt import gotoweb as gui
4463 from ubuntuone.controlpanel.gui.qt.tests import (
4464 BaseTestCase,
4465@@ -35,7 +35,6 @@
4466 @defer.inlineCallbacks
4467 def setUp(self):
4468 yield super(GoToWebButtonTestCase, self).setUp()
4469- self.patch(qt, 'uri_hook', self._set_called)
4470
4471 def test_uri_can_be_set(self):
4472 """The uri can be set."""
4473@@ -47,8 +46,14 @@
4474 """The layout direction is RightToLeft."""
4475 self.assertEqual(self.ui.layoutDirection(), gui.QtCore.Qt.RightToLeft)
4476
4477+ def test_cursor_pointer(self):
4478+ """The cursor is PointingHandCursor."""
4479+ self.assertEqual(self.ui.cursor().shape(),
4480+ gui.QtCore.Qt.PointingHandCursor)
4481+
4482 def test_open_uri_when_clicked(self):
4483 """When clicking the button, the uri is opened."""
4484+ self.patch(qt, 'uri_hook', self._set_called)
4485 self.ui.uri = 'yadda-yadda-yoo'
4486 self.ui.click()
4487
4488@@ -56,7 +61,54 @@
4489
4490 def test_do_nothing_on_clicked_if_uri_is_none(self):
4491 """When clicking the button, if the uri is None, do nothing."""
4492+ self.patch(qt, 'uri_hook', self._set_called)
4493 self.ui.uri = None
4494 self.ui.click()
4495
4496 self.assertEqual(self._called, False)
4497+
4498+
4499+class SignUrlTestCase(GoToWebButtonTestCase):
4500+ """The test suite for the sign url management."""
4501+
4502+ @defer.inlineCallbacks
4503+ def setUp(self):
4504+ yield super(SignUrlTestCase, self).setUp()
4505+ self.patch(qt, 'uri_hook', lambda url: None)
4506+ self.patch(gui, 'sign_url', self._set_called)
4507+ self.creds = yield self.ui.backend.get_credentials()
4508+ assert len(self.creds) > 0
4509+
4510+ def test_without_ubuntuone_prefix(self):
4511+ """If given url is not an ubuntuone url, don't sign it."""
4512+ self.ui.uri = 'bad_prefix' + UBUNTUONE_LINK
4513+ self.ui.click()
4514+
4515+ self.assertFalse(self._called)
4516+
4517+ def test_with_ubuntuone_prefix(self):
4518+ """If given url is an ubuntuone url, sign it."""
4519+ self.ui.uri = UBUNTUONE_LINK + 'foo'
4520+ self.ui.click()
4521+
4522+ self.assertEqual(self._called, ((self.ui.uri, self.creds,), {}))
4523+
4524+
4525+class SignUrlNoCredsTestCase(SignUrlTestCase):
4526+ """The test suite for the sign url management when there are no creds."""
4527+
4528+ @defer.inlineCallbacks
4529+ def setUp(self):
4530+ yield super(SignUrlNoCredsTestCase, self).setUp()
4531+ self.patch(self.ui.backend, 'get_credentials', lambda: {})
4532+
4533+ def test_with_ubuntuone_prefix(self):
4534+ """If given url is an ubuntuone url, don't sign it.
4535+
4536+ Since we have no credentials, the url should not be signed.
4537+
4538+ """
4539+ self.ui.uri = UBUNTUONE_LINK + 'foo'
4540+ self.ui.click()
4541+
4542+ self.assertFalse(self._called)
4543
4544=== modified file 'ubuntuone/controlpanel/gui/qt/tests/test_gui.py'
4545--- ubuntuone/controlpanel/gui/qt/tests/test_gui.py 2011-07-22 21:26:48 +0000
4546+++ ubuntuone/controlpanel/gui/qt/tests/test_gui.py 2011-09-26 18:38:27 +0000
4547@@ -41,3 +41,10 @@
4548 self.ui.close_callback = None
4549 self.ui.closeEvent(event=gui.QtGui.QCloseEvent())
4550 # world did not explode
4551+
4552+ def test_on_signin_canceled(self):
4553+ """On SigninPanel's signinCanceled, close."""
4554+ self.patch(self.ui, 'closeEvent', self._set_called)
4555+ self.ui.ui.control_panel.ui.signin.signinCanceled.emit()
4556+ self.assertEqual(len(self._called[0]), 1)
4557+ self.assertIsInstance(self._called[0][0], gui.QtGui.QCloseEvent)
4558
4559=== added file 'ubuntuone/controlpanel/gui/qt/tests/test_signin.py'
4560--- ubuntuone/controlpanel/gui/qt/tests/test_signin.py 1970-01-01 00:00:00 +0000
4561+++ ubuntuone/controlpanel/gui/qt/tests/test_signin.py 2011-09-26 18:38:27 +0000
4562@@ -0,0 +1,168 @@
4563+# -*- coding: utf-8 -*-
4564+
4565+# Authors: Natalia B Bidart <natalia.bidart@canonical.com>
4566+#
4567+# Copyright 2011 Canonical Ltd.
4568+#
4569+# This program is free software: you can redistribute it and/or modify it
4570+# under the terms of the GNU General Public License version 3, as published
4571+# by the Free Software Foundation.
4572+#
4573+# This program is distributed in the hope that it will be useful, but
4574+# WITHOUT ANY WARRANTY; without even the implied warranties of
4575+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
4576+# PURPOSE. See the GNU General Public License for more details.
4577+#
4578+# You should have received a copy of the GNU General Public License along
4579+# with this program. If not, see <http://www.gnu.org/licenses/>.
4580+
4581+"""Tests for the Sign In Panel."""
4582+
4583+from twisted.internet import defer
4584+
4585+from ubuntuone.controlpanel.gui import qt
4586+from ubuntuone.controlpanel.gui.qt import signin as gui
4587+from ubuntuone.controlpanel.gui.qt.tests import (
4588+ CrashyBackend,
4589+ CrashyBackendException,
4590+)
4591+from ubuntuone.controlpanel.gui.qt.tests.test_ubuntuonebin import (
4592+ UbuntuOneBinTestCase,
4593+)
4594+
4595+EMAIL = 'foo@bar.com'
4596+PASSWORD = 'h3ll0World'
4597+TOKEN = {'yadda': 'doo'}
4598+
4599+MSG = {u'errtype': u'AuthenticationError',
4600+ u'message': u'The authentication failed.'}
4601+
4602+
4603+def fail(*a, **kw):
4604+ """Emit CredentialsError."""
4605+ raise TypeError(MSG)
4606+
4607+
4608+class BaseSignInPanelTestCase(UbuntuOneBinTestCase):
4609+ """Test the signin panel."""
4610+
4611+ innerclass_ui = gui.signin_ui
4612+ innerclass_name = "Ui_Form"
4613+ class_ui = gui.SignInPanel
4614+ logger = gui.logger
4615+
4616+ @defer.inlineCallbacks
4617+ def setUp(self):
4618+ yield super(BaseSignInPanelTestCase, self).setUp()
4619+ self.ui.backend.next_result = TOKEN
4620+
4621+
4622+class SignInPanelTestCase(BaseSignInPanelTestCase):
4623+ """Test the signin panel."""
4624+
4625+ innerclass_ui = gui.signin_ui
4626+ innerclass_name = "Ui_Form"
4627+ class_ui = gui.SignInPanel
4628+
4629+ @defer.inlineCallbacks
4630+ def setUp(self):
4631+ yield super(SignInPanelTestCase, self).setUp()
4632+ self.ui.backend.next_result = TOKEN
4633+ self.ui.ui.email_entry.setText(gui.QtCore.QString(''))
4634+ self.ui.ui.password_entry.setText(gui.QtCore.QString(''))
4635+
4636+ @defer.inlineCallbacks
4637+ def test_is_processing_while_asking_info(self):
4638+ """The ui is processing while the contents are loaded."""
4639+ def check(email, password):
4640+ """The ui must be is_processing."""
4641+ self.assertTrue(self.ui.is_processing, 'ui must be processing')
4642+ return TOKEN
4643+
4644+ self.patch(self.ui.backend, 'login', check)
4645+
4646+ self.assertFalse(self.ui.is_processing)
4647+ yield self.ui.ui.signin_button.click()
4648+ self.assertFalse(self.ui.is_processing)
4649+
4650+ def test_signin_disabled_at_startup(self):
4651+ """The signin_button is disabled at startup."""
4652+ self.assertFalse(self.ui.ui.signin_button.isEnabled())
4653+
4654+ def test_signin_disabled_if_no_email_but_password(self):
4655+ """Disable signin_button if no email."""
4656+ self.ui.ui.password_entry.setText(gui.QtCore.QString('doo'))
4657+ self.assertFalse(self.ui.ui.signin_button.isEnabled())
4658+
4659+ def test_signin_disabled_if_no_password_but_email(self):
4660+ """Disable signin_button if no password."""
4661+ self.ui.ui.email_entry.setText(gui.QtCore.QString('duh'))
4662+ self.assertFalse(self.ui.ui.signin_button.isEnabled())
4663+
4664+ def test_cancel_button(self):
4665+ """Send a signal when the cancel button is clicked."""
4666+ self.ui.signinCanceled.connect(self._set_called)
4667+ self.ui.ui.cancel_button.click()
4668+ self.assertEqual(self._called, ((), {}))
4669+
4670+ def test_forgot_password_button(self):
4671+ """When clicking the forgot passsword btn, the proper url is opened."""
4672+ self.patch(qt, 'uri_hook', self._set_called)
4673+ self.ui.ui.forgot_password_button.click()
4674+
4675+ self.assertEqual(self._called, ((gui.RESET_PASSWORD_LINK,), {}))
4676+
4677+
4678+class SignInButtonPanelTestCase(BaseSignInPanelTestCase):
4679+ """Test the signin_button widget."""
4680+
4681+ @defer.inlineCallbacks
4682+ def setUp(self):
4683+ yield super(SignInButtonPanelTestCase, self).setUp()
4684+ self.ui.ui.email_entry.setText(gui.QtCore.QString(EMAIL))
4685+ self.ui.ui.password_entry.setText(gui.QtCore.QString(PASSWORD))
4686+
4687+ @defer.inlineCallbacks
4688+ def test_signin_button(self):
4689+ """Call the backend when the signin button is clicked."""
4690+ yield self.ui.ui.signin_button.click()
4691+
4692+ self.assert_backend_called('login', email=EMAIL, password=PASSWORD)
4693+ # pylint: disable=W0212
4694+ for arg in self.ui.backend._called['login'][1].itervalues():
4695+ self.assertIsInstance(arg, unicode) # make sure not send QString
4696+
4697+ @defer.inlineCallbacks
4698+ def test_signin_success(self):
4699+ """Emit credentialsFound on signin success."""
4700+ self.ui.credentialsFound.connect(self._set_called)
4701+ yield self.ui.ui.signin_button.click()
4702+
4703+ self.assertEqual(self._called, ((TOKEN,), {}))
4704+ self.assertFalse(self.ui.is_processing)
4705+
4706+ def test_signin_enabled_if_email_and_password(self):
4707+ """Enable signin_button if email and password are non empty."""
4708+ self.assertTrue(self.ui.ui.signin_button.isEnabled())
4709+
4710+ def test_return_pressed(self):
4711+ """On return pressed, click the signin_button."""
4712+ self.patch(self.ui.ui.signin_button, 'click', self._set_called)
4713+
4714+ for entry in (self.ui.ui.email_entry, self.ui.ui.password_entry):
4715+ entry.returnPressed.emit()
4716+
4717+ # This is failing, so we need to settle with counting recievers
4718+ #self.assertEqual(self._called, ((), {}))
4719+ receivers = entry.receivers(gui.QtCore.SIGNAL('returnPressed()'))
4720+ self.assertEqual(1, receivers)
4721+
4722+ self._called = False
4723+
4724+ @defer.inlineCallbacks
4725+ def test_backend_error_is_handled(self):
4726+ """Any error from the backend is properly handled."""
4727+ self.patch(self.ui, 'backend', CrashyBackend())
4728+ yield self.ui.ui.signin_button.click()
4729+
4730+ self.assertTrue(self.memento.check_exception(CrashyBackendException))
4731
4732=== added file 'ubuntuone/controlpanel/gui/qt/tests/test_start.py'
4733--- ubuntuone/controlpanel/gui/qt/tests/test_start.py 1970-01-01 00:00:00 +0000
4734+++ ubuntuone/controlpanel/gui/qt/tests/test_start.py 2011-09-26 18:38:27 +0000
4735@@ -0,0 +1,91 @@
4736+# -*- coding: utf-8 -*-
4737+
4738+# Author: Roberto Alsina <roberto.alsina@canonical.com>
4739+#
4740+# Copyright 2011 Canonical Ltd.
4741+#
4742+# This program is free software: you can redistribute it and/or modify it
4743+# under the terms of the GNU General Public License version 3, as published
4744+# by the Free Software Foundation.
4745+#
4746+# This program is distributed in the hope that it will be useful, but
4747+# WITHOUT ANY WARRANTY; without even the implied warranties of
4748+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
4749+# PURPOSE. See the GNU General Public License for more details.
4750+#
4751+# You should have received a copy of the GNU General Public License along
4752+# with this program. If not, see <http://www.gnu.org/licenses/>.
4753+
4754+"""Tests for the start function."""
4755+
4756+from twisted.internet import defer
4757+
4758+from ubuntuone.controlpanel.gui.qt import gui
4759+from ubuntuone.controlpanel.gui.qt.tests import NO_OP
4760+from ubuntuone.controlpanel.tests import TestCase
4761+
4762+
4763+class FakeThing(object):
4764+
4765+ """A fake thing."""
4766+
4767+ def __init__(self):
4768+ self.args = []
4769+ self.shown = False
4770+
4771+ def __call__(self, *args, **kwargs):
4772+ self.args.append((args, kwargs))
4773+ return self
4774+
4775+ def show(self):
4776+ """Show."""
4777+ self.shown = True
4778+
4779+
4780+class FakeReactor(object):
4781+ """A fake reactor."""
4782+
4783+ def run(self):
4784+ """Start."""
4785+
4786+ def stop(self):
4787+ """Stop."""
4788+
4789+
4790+class StartTestCase(TestCase):
4791+ """Test the qt control panel."""
4792+
4793+ @defer.inlineCallbacks
4794+ def setUp(self):
4795+ yield super(StartTestCase, self).setUp()
4796+ self.main_window = FakeThing()
4797+ self.tray_icon = FakeThing()
4798+ self.patch(gui, "MainWindow", self.main_window)
4799+ self.patch(gui, "TrayIcon", self.tray_icon)
4800+
4801+ def test_minimized(self):
4802+ """Test behaviour with minimized=True."""
4803+ gui.start(NO_OP, minimized=True, with_icon=True)
4804+ self.assertEqual(self.tray_icon.args, [((), {'window': None})])
4805+ self.assertEqual(self.main_window.args, [])
4806+
4807+ def test_with_icon(self):
4808+ """Test behaviour with with_icon=True."""
4809+ gui.start(NO_OP, with_icon=True, minimized=False)
4810+ self.assertEqual(self.main_window.args, [((), {})])
4811+ self.assertEqual(self.tray_icon.args, [((),
4812+ {'window': self.main_window})])
4813+
4814+ def test_both_false(self):
4815+ """Test behaviour when with_icon and minimized are False."""
4816+ gui.start(NO_OP, with_icon=False, minimized=False)
4817+ # Should be called
4818+ self.assertNotEqual(self.main_window.args, [])
4819+ # Should not be called
4820+ self.assertEqual(self.tray_icon.args, [])
4821+
4822+ def test_both_true(self):
4823+ """Test behaviour when with_icon and minimized are True."""
4824+ gui.start(NO_OP, with_icon=True, minimized=True)
4825+ self.assertEqual(self.tray_icon.args, [((), {'window': None})])
4826+ self.assertEqual(self.main_window.args, [])
4827
4828=== added file 'ubuntuone/controlpanel/gui/qt/tests/test_systray.py'
4829--- ubuntuone/controlpanel/gui/qt/tests/test_systray.py 1970-01-01 00:00:00 +0000
4830+++ ubuntuone/controlpanel/gui/qt/tests/test_systray.py 2011-09-26 18:38:27 +0000
4831@@ -0,0 +1,103 @@
4832+# -*- coding: utf-8 -*-
4833+
4834+# Author: Roberto Alsina <roberto.alsina@canonical.com>
4835+#
4836+# Copyright 2011 Canonical Ltd.
4837+#
4838+# This program is free software: you can redistribute it and/or modify it
4839+# under the terms of the GNU General Public License version 3, as published
4840+# by the Free Software Foundation.
4841+#
4842+# This program is distributed in the hope that it will be useful, but
4843+# WITHOUT ANY WARRANTY; without even the implied warranties of
4844+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
4845+# PURPOSE. See the GNU General Public License for more details.
4846+#
4847+# You should have received a copy of the GNU General Public License along
4848+# with this program. If not, see <http://www.gnu.org/licenses/>.
4849+
4850+"""Tests for the notification area icon."""
4851+
4852+from PyQt4 import QtGui
4853+
4854+from ubuntuone.controlpanel.gui.qt.systray import TrayIcon
4855+from ubuntuone.controlpanel.tests import TestCase
4856+import ubuntuone.controlpanel.gui.qt.gui
4857+
4858+
4859+class FakeMainWindow(QtGui.QWidget):
4860+
4861+ """Fake Main Window."""
4862+
4863+ def __init__(self, *args, **kwargs):
4864+ self.args = (args, kwargs)
4865+ super(FakeMainWindow, self).__init__()
4866+
4867+
4868+class SystrayTestCase(TestCase):
4869+
4870+ """Test the notification area icon."""
4871+
4872+ def test_quit(self):
4873+ """Test the quit option in the menu."""
4874+ # Not done on setup, because if I patch stop
4875+ # after instantiation, it doesn't get called.
4876+ self.patch(TrayIcon, "stop", self._set_called)
4877+ tray = TrayIcon()
4878+ tray.quit.trigger()
4879+ self.assertEqual(self._called, ((False,), {}))
4880+
4881+ def test_restore_no_window(self):
4882+ """Test the restore window option in the menu, with no window."""
4883+ self.patch(ubuntuone.controlpanel.gui.qt.gui,
4884+ "MainWindow", FakeMainWindow)
4885+ tray = TrayIcon()
4886+ self.assertEqual(tray.window, None)
4887+ tray.restore.trigger()
4888+ self.assertIsInstance(tray.window, FakeMainWindow)
4889+ self.assertTrue(tray.window.isVisible())
4890+ self.assertEqual(tray.window.args, ((),
4891+ {'close_callback': tray.delete_window}))
4892+
4893+ def test_activate(self):
4894+ """Test the icon activation."""
4895+ tray = TrayIcon()
4896+ window = FakeMainWindow()
4897+ tray.window = window
4898+ self.assertFalse(tray.window.isVisible())
4899+ tray.activated.emit(tray.Trigger)
4900+ self.assertEqual(tray.window, window)
4901+ self.assertTrue(tray.window.isVisible())
4902+
4903+ def test_restore_window(self):
4904+ """Test the restore window option in the menu, with a window."""
4905+ tray = TrayIcon()
4906+ window = FakeMainWindow()
4907+ tray.window = window
4908+ self.assertFalse(tray.window.isVisible())
4909+ tray.restore.trigger()
4910+ self.assertEqual(tray.window, window)
4911+ self.assertTrue(tray.window.isVisible())
4912+
4913+ def test_delete_window(self):
4914+ """Test deleting an existing window."""
4915+ tray = TrayIcon()
4916+ window = FakeMainWindow()
4917+ tray.window = window
4918+ tray.delete_window()
4919+ self.assertEqual(tray.window, None)
4920+ self.assertFalse(window.isVisible())
4921+
4922+ def test_delete_no_window(self):
4923+ """Test deleting without an existing window."""
4924+ tray = TrayIcon()
4925+ tray.delete_window()
4926+ self.assertEqual(tray.window, None)
4927+
4928+ def test_initialization(self):
4929+ """Test that everything initializes correctly."""
4930+ tray = TrayIcon()
4931+ self.assertTrue(tray.isVisible())
4932+ self.assertEqual(tray.window, None)
4933+ self.assertIsInstance(tray.context_menu, QtGui.QMenu)
4934+ self.assertFalse(tray.icon() == None)
4935
4936=== modified file 'ubuntuone/controlpanel/gui/qt/tests/test_ubuntuonebin.py'
4937--- ubuntuone/controlpanel/gui/qt/tests/test_ubuntuonebin.py 2011-07-22 21:26:48 +0000
4938+++ ubuntuone/controlpanel/gui/qt/tests/test_ubuntuonebin.py 2011-09-26 18:38:27 +0000
4939@@ -18,25 +18,24 @@
4940
4941 """Tests for the Ubuntu One Bin."""
4942
4943-from ubuntuone.devtools.testcase import skipIfOS
4944+from twisted.internet import defer
4945
4946 from ubuntuone.controlpanel.gui.qt import ubuntuonebin as gui
4947-from ubuntuone.controlpanel.gui.qt.tests import BaseTestCase
4948+from ubuntuone.controlpanel.gui.qt.tests import (
4949+ BaseTestCase,
4950+ CrashyBackend,
4951+ CrashyBackendException,
4952+)
4953
4954 # Attribute 'yyy' defined outside __init__, access to a protected member
4955 # pylint: disable=W0201, W0212
4956
4957
4958 class UbuntuOneBinTestCase(BaseTestCase):
4959- """Test the qt cloud folders tab."""
4960+ """Test the Ubuntu One base widget."""
4961
4962 class_ui = gui.UbuntuOneBin
4963
4964- def test_backend(self):
4965- """Backend is created."""
4966- self.assertIsInstance(self.ui.backend,
4967- gui.backend.ControlBackend)
4968-
4969 def test_is_not_processing_after_creation(self):
4970 """After creation, is not processing."""
4971 self.assertFalse(self.ui.is_processing)
4972@@ -46,22 +45,33 @@
4973 """The animation is active."""
4974 self.assertFalse(self.ui.overlay.isVisible())
4975
4976- @skipIfOS('win32', 'crashes under windows, see LP: #806154')
4977 def test_is_enabled_if_not_processing(self):
4978 """If not processing, the UI is enabled."""
4979 self.ui.show() # need to show to test widgets visibility
4980+ self.addCleanup(self.ui.hide)
4981
4982 self.ui.is_processing = False
4983
4984 self.assertTrue(self.ui.isEnabled())
4985 self.assertFalse(self.ui.overlay.isVisible())
4986
4987- @skipIfOS('win32', 'crashes under windows, see LP: #806154')
4988 def test_is_not_enabled_if_processing(self):
4989 """If processing, the UI is disabled."""
4990 self.ui.show() # need to show to test widgets visibility
4991+ self.addCleanup(self.ui.hide)
4992
4993 self.ui.is_processing = True
4994
4995 self.assertFalse(self.ui.isEnabled())
4996 self.assertTrue(self.ui.overlay.isVisible())
4997+
4998+ @defer.inlineCallbacks
4999+ def test_backend_error_is_handled(self):
5000+ """Any error from the backend is properly handled."""
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches