Merge lp:~dobey/ubuntu/natty/ubuntuone-control-panel/release-0-9-1 into lp:ubuntu/natty/ubuntuone-control-panel

Proposed by dobey
Status: Merged
Merged at revision: 15
Proposed branch: lp:~dobey/ubuntu/natty/ubuntuone-control-panel/release-0-9-1
Merge into: lp:ubuntu/natty/ubuntuone-control-panel
Diff against target: 4681 lines (+2189/-1405)
20 files modified
PKG-INFO (+1/-1)
bin/ubuntuone-control-panel-gtk (+4/-2)
data/dashboard.ui (+5/-138)
data/device.ui (+160/-122)
data/management.ui (+214/-101)
data/overview.ui (+292/-223)
data/services.ui (+309/-14)
data/volumes.ui (+20/-20)
debian/changelog (+24/-0)
debian/control (+4/-4)
pylintrc (+1/-1)
setup.py (+1/-1)
ubuntuone/__init__.py (+0/-1)
ubuntuone/controlpanel/backend.py (+5/-1)
ubuntuone/controlpanel/gtk/gui.py (+215/-90)
ubuntuone/controlpanel/gtk/tests/__init__.py (+92/-7)
ubuntuone/controlpanel/gtk/tests/test_gui.py (+235/-670)
ubuntuone/controlpanel/gtk/tests/test_gui_basic.py (+595/-0)
ubuntuone/controlpanel/tests/__init__.py (+11/-8)
ubuntuone/controlpanel/tests/test_backend.py (+1/-1)
To merge this branch: bzr merge lp:~dobey/ubuntu/natty/ubuntuone-control-panel/release-0-9-1
Reviewer Review Type Date Requested Status
Daniel Holbach (community) Approve
Review via email: mp+52795@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Daniel Holbach (dholbach) wrote :

Good work!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'PKG-INFO'
2--- PKG-INFO 2011-02-28 15:38:15 +0000
3+++ PKG-INFO 2011-03-10 03:11:54 +0000
4@@ -1,6 +1,6 @@
5 Metadata-Version: 1.1
6 Name: ubuntuone-control-panel
7-Version: 0.9.0
8+Version: 0.9.1
9 Summary: Ubuntu One Control Panel
10 Home-page: https://launchpad.net/ubuntuone-control-panel
11 Author: Natalia Bidart
12
13=== modified file 'bin/ubuntuone-control-panel-gtk'
14--- bin/ubuntuone-control-panel-gtk 2011-01-25 19:08:59 +0000
15+++ bin/ubuntuone-control-panel-gtk 2011-03-10 03:11:54 +0000
16@@ -40,12 +40,14 @@
17 help="Start the Ubuntu One Control Panel (GTK) in the "
18 "PANEL_NAME tab. Possible values are: "
19 "dashboard, volumes, devices, applications")
20+ result.add_option("-a", "--alert", dest="alert", action="store_true",
21+ help="Start the Ubuntu One Control Panel (GTK) alerting "
22+ "the user to its presence.")
23 return result
24
25
26 if __name__ == "__main__":
27 parser = parser_options()
28 (options, args) = parser.parse_args(sys.argv)
29-
30- gui = ControlPanelWindow(switch_to=options.switch_to)
31+ gui = ControlPanelWindow(switch_to=options.switch_to, alert=options.alert)
32 gui.main()
33
34=== removed file 'data/banner.png'
35Binary files data/banner.png 2011-02-28 15:38:15 +0000 and data/banner.png 1970-01-01 00:00:00 +0000 differ
36=== modified file 'data/contacts.png'
37Binary files data/contacts.png 2011-02-12 03:07:22 +0000 and data/contacts.png 2011-03-10 03:11:54 +0000 differ
38=== modified file 'data/dashboard.ui'
39--- data/dashboard.ui 2011-02-28 15:38:15 +0000
40+++ data/dashboard.ui 2011-03-10 03:11:54 +0000
41@@ -5,8 +5,6 @@
42 <object class="GtkVBox" id="itself">
43 <property name="visible">True</property>
44 <property name="can_focus">False</property>
45- <property name="border_width">10</property>
46- <property name="spacing">10</property>
47 <child>
48 <object class="GtkHBox" id="account">
49 <property name="visible">True</property>
50@@ -59,12 +57,13 @@
51 </child>
52 <child>
53 <object class="GtkLinkButton" id="linkbutton3">
54- <property name="label" translatable="yes">Edit</property>
55+ <property name="label" translatable="yes">Edit account details</property>
56 <property name="visible">True</property>
57 <property name="can_focus">True</property>
58 <property name="receives_default">True</property>
59+ <property name="tooltip_text" translatable="yes">Edit your account details</property>
60 <property name="use_action_appearance">False</property>
61- <property name="relief">none</property>
62+ <property name="relief">half</property>
63 <property name="uri">http://login.ubuntu.com</property>
64 </object>
65 <packing>
66@@ -131,8 +130,9 @@
67 <property name="visible">True</property>
68 <property name="can_focus">True</property>
69 <property name="receives_default">True</property>
70+ <property name="tooltip_text" translatable="yes">Upgrade the storage space on your account</property>
71 <property name="use_action_appearance">False</property>
72- <property name="relief">none</property>
73+ <property name="relief">half</property>
74 <property name="uri">https://one.ubuntu.com/plans/</property>
75 </object>
76 <packing>
77@@ -169,138 +169,5 @@
78 <property name="position">0</property>
79 </packing>
80 </child>
81- <child>
82- <object class="GtkAlignment" id="alignment5">
83- <property name="visible">True</property>
84- <property name="can_focus">False</property>
85- <child>
86- <object class="GtkHBox" id="hbox1">
87- <property name="visible">True</property>
88- <property name="can_focus">False</property>
89- <property name="spacing">20</property>
90- <child>
91- <object class="GtkHButtonBox" id="hbuttonbox3">
92- <property name="visible">True</property>
93- <property name="can_focus">False</property>
94- <property name="spacing">5</property>
95- <child>
96- <object class="GtkLinkButton" id="linkbutton2">
97- <property name="label" translatable="yes">Official Support</property>
98- <property name="visible">True</property>
99- <property name="can_focus">True</property>
100- <property name="receives_default">True</property>
101- <property name="tooltip_text" translatable="yes">Get official Ubuntu One support online</property>
102- <property name="use_action_appearance">False</property>
103- <property name="uri">https://one.ubuntu.com/support/</property>
104- </object>
105- <packing>
106- <property name="expand">False</property>
107- <property name="fill">False</property>
108- <property name="position">0</property>
109- </packing>
110- </child>
111- <child>
112- <object class="GtkLinkButton" id="linkbutton4">
113- <property name="label" translatable="yes">Community Support</property>
114- <property name="visible">True</property>
115- <property name="can_focus">True</property>
116- <property name="receives_default">True</property>
117- <property name="tooltip_text" translatable="yes">View and ask Ubuntu One related questions at AskUbuntu.com</property>
118- <property name="use_action_appearance">False</property>
119- <property name="uri">http://askubuntu.com/questions/tagged/ubuntu-one</property>
120- </object>
121- <packing>
122- <property name="expand">False</property>
123- <property name="fill">False</property>
124- <property name="position">1</property>
125- </packing>
126- </child>
127- </object>
128- <packing>
129- <property name="expand">False</property>
130- <property name="fill">True</property>
131- <property name="position">0</property>
132- </packing>
133- </child>
134- <child>
135- <object class="GtkHBox" id="hbox2">
136- <property name="visible">True</property>
137- <property name="can_focus">False</property>
138- <child>
139- <object class="GtkLabel" id="label4">
140- <property name="visible">True</property>
141- <property name="can_focus">False</property>
142- <property name="label" translatable="yes">Talk to us on:</property>
143- </object>
144- <packing>
145- <property name="expand">True</property>
146- <property name="fill">True</property>
147- <property name="position">0</property>
148- </packing>
149- </child>
150- <child>
151- <object class="GtkLinkButton" id="linkbutton5">
152- <property name="visible">True</property>
153- <property name="can_focus">True</property>
154- <property name="receives_default">True</property>
155- <property name="use_action_appearance">False</property>
156- <property name="relief">none</property>
157- <property name="uri">http://twitter.com/ubuntuone</property>
158- <child>
159- <object class="GtkImage" id="twitter">
160- <property name="visible">True</property>
161- <property name="can_focus">False</property>
162- <property name="tooltip_text" translatable="yes">http://twitter.com/ubuntuone</property>
163- <property name="pixbuf">twitter.png</property>
164- </object>
165- </child>
166- </object>
167- <packing>
168- <property name="expand">False</property>
169- <property name="fill">False</property>
170- <property name="position">1</property>
171- </packing>
172- </child>
173- <child>
174- <object class="GtkLinkButton" id="linkbutton6">
175- <property name="visible">True</property>
176- <property name="can_focus">True</property>
177- <property name="receives_default">True</property>
178- <property name="use_action_appearance">False</property>
179- <property name="relief">none</property>
180- <property name="uri">http://www.facebook.com/ubuntuone</property>
181- <child>
182- <object class="GtkImage" id="facebook">
183- <property name="visible">True</property>
184- <property name="can_focus">False</property>
185- <property name="tooltip_text" translatable="yes">http://www.facebook.com/ubuntuone</property>
186- <property name="pixbuf">facebook.png</property>
187- </object>
188- </child>
189- </object>
190- <packing>
191- <property name="expand">True</property>
192- <property name="fill">True</property>
193- <property name="position">2</property>
194- </packing>
195- </child>
196- </object>
197- <packing>
198- <property name="expand">False</property>
199- <property name="fill">True</property>
200- <property name="pack_type">end</property>
201- <property name="position">1</property>
202- </packing>
203- </child>
204- </object>
205- </child>
206- </object>
207- <packing>
208- <property name="expand">False</property>
209- <property name="fill">True</property>
210- <property name="pack_type">end</property>
211- <property name="position">1</property>
212- </packing>
213- </child>
214 </object>
215 </interface>
216
217=== modified file 'data/device.ui'
218--- data/device.ui 2011-02-23 13:57:42 +0000
219+++ data/device.ui 2011-03-10 03:11:54 +0000
220@@ -12,174 +12,203 @@
221 </object>
222 <object class="GtkVBox" id="itself">
223 <property name="visible">True</property>
224+ <property name="can_focus">False</property>
225 <property name="border_width">10</property>
226 <property name="spacing">5</property>
227 <child>
228 <object class="GtkHBox" id="hbox1">
229 <property name="visible">True</property>
230+ <property name="can_focus">False</property>
231 <property name="spacing">10</property>
232 <child>
233 <object class="GtkVBox" id="vbox1">
234 <property name="visible">True</property>
235+ <property name="can_focus">False</property>
236 <property name="spacing">5</property>
237 <child>
238 <object class="GtkHBox" id="hbox2">
239 <property name="visible">True</property>
240+ <property name="can_focus">False</property>
241 <child>
242 <object class="GtkImage" id="device_type">
243 <property name="visible">True</property>
244+ <property name="can_focus">False</property>
245 <property name="icon_name">computer</property>
246 </object>
247 <packing>
248 <property name="expand">False</property>
249+ <property name="fill">True</property>
250 <property name="position">0</property>
251 </packing>
252 </child>
253 <child>
254 <object class="GtkLabel" id="device_name">
255 <property name="visible">True</property>
256+ <property name="can_focus">False</property>
257 <property name="xalign">0</property>
258 <property name="xpad">5</property>
259 <property name="label">My Laptop</property>
260 <property name="wrap">True</property>
261 </object>
262 <packing>
263+ <property name="expand">True</property>
264+ <property name="fill">True</property>
265 <property name="position">1</property>
266 </packing>
267 </child>
268 </object>
269 <packing>
270+ <property name="expand">True</property>
271+ <property name="fill">True</property>
272 <property name="position">0</property>
273 </packing>
274 </child>
275 <child>
276 <object class="GtkAlignment" id="alignment1">
277 <property name="visible">True</property>
278+ <property name="can_focus">False</property>
279 <property name="left_padding">25</property>
280 <child>
281- <object class="GtkTable" id="throttling">
282+ <object class="GtkVBox" id="config_settings">
283 <property name="visible">True</property>
284- <property name="n_rows">4</property>
285- <property name="n_columns">3</property>
286- <property name="column_spacing">3</property>
287- <property name="row_spacing">3</property>
288+ <property name="can_focus">False</property>
289 <child>
290 <object class="GtkCheckButton" id="show_all_notifications">
291- <property name="label" translatable="yes">Show notifications</property>
292- <property name="visible">True</property>
293- <property name="can_focus">True</property>
294- <property name="receives_default">False</property>
295- <property name="draw_indicator">True</property>
296- <signal name="toggled" handler="on_show_all_notifications_toggled"/>
297- </object>
298- </child>
299- <child>
300- <object class="GtkCheckButton" id="limit_bandwidth">
301- <property name="label" translatable="yes">Limit bandwidth usage</property>
302- <property name="visible">True</property>
303- <property name="can_focus">True</property>
304- <property name="receives_default">False</property>
305- <property name="draw_indicator">True</property>
306- <signal name="toggled" handler="on_limit_bandwidth_toggled"/>
307- </object>
308- <packing>
309- <property name="top_attach">1</property>
310- <property name="bottom_attach">2</property>
311- </packing>
312- </child>
313- <child>
314- <object class="GtkLabel" id="max_upload_speed_label">
315- <property name="visible">True</property>
316- <property name="xalign">0</property>
317- <property name="label" translatable="yes">Max upload speed:</property>
318- <property name="track_visited_links">False</property>
319- </object>
320- <packing>
321- <property name="top_attach">2</property>
322- <property name="bottom_attach">3</property>
323- </packing>
324- </child>
325- <child>
326- <object class="GtkLabel" id="max_download_speed_label">
327- <property name="visible">True</property>
328- <property name="xalign">0</property>
329- <property name="label" translatable="yes">Max download speed:</property>
330- </object>
331- <packing>
332- <property name="top_attach">3</property>
333- <property name="bottom_attach">4</property>
334- </packing>
335- </child>
336- <child>
337- <object class="GtkSpinButton" id="max_upload_speed">
338- <property name="visible">True</property>
339- <property name="can_focus">True</property>
340- <property name="invisible_char">•</property>
341- <property name="activates_default">True</property>
342- <property name="invisible_char_set">True</property>
343- <property name="adjustment">adjustment1</property>
344- <signal name="value_changed" handler="on_max_upload_speed_value_changed"/>
345- </object>
346- <packing>
347- <property name="left_attach">1</property>
348- <property name="right_attach">2</property>
349- <property name="top_attach">2</property>
350- <property name="bottom_attach">3</property>
351- <property name="x_options">GTK_FILL</property>
352- <property name="y_options">GTK_FILL</property>
353- </packing>
354- </child>
355- <child>
356- <object class="GtkSpinButton" id="max_download_speed">
357- <property name="visible">True</property>
358- <property name="can_focus">True</property>
359- <property name="invisible_char">•</property>
360- <property name="activates_default">True</property>
361- <property name="invisible_char_set">True</property>
362- <property name="adjustment">adjustment2</property>
363- <signal name="value_changed" handler="on_max_download_speed_value_changed"/>
364- </object>
365- <packing>
366- <property name="left_attach">1</property>
367- <property name="right_attach">2</property>
368- <property name="top_attach">3</property>
369- <property name="bottom_attach">4</property>
370- </packing>
371- </child>
372- <child>
373- <placeholder/>
374- </child>
375- <child>
376- <placeholder/>
377- </child>
378- <child>
379- <placeholder/>
380- </child>
381- <child>
382- <placeholder/>
383- </child>
384- <child>
385- <object class="GtkLabel" id="label1">
386- <property name="visible">True</property>
387- <property name="label" translatable="yes">KiB/s</property>
388- </object>
389- <packing>
390- <property name="left_attach">2</property>
391- <property name="right_attach">3</property>
392- <property name="top_attach">2</property>
393- <property name="bottom_attach">3</property>
394- </packing>
395- </child>
396- <child>
397- <object class="GtkLabel" id="label2">
398- <property name="visible">True</property>
399- <property name="label" translatable="yes">KiB/s</property>
400- </object>
401- <packing>
402- <property name="left_attach">2</property>
403- <property name="right_attach">3</property>
404- <property name="top_attach">3</property>
405- <property name="bottom_attach">4</property>
406+ <property name="label" translatable="yes">Show activity notifications</property>
407+ <property name="visible">True</property>
408+ <property name="can_focus">True</property>
409+ <property name="receives_default">False</property>
410+ <property name="use_action_appearance">False</property>
411+ <property name="draw_indicator">True</property>
412+ <signal name="toggled" handler="on_show_all_notifications_toggled" swapped="no"/>
413+ </object>
414+ <packing>
415+ <property name="expand">True</property>
416+ <property name="fill">True</property>
417+ <property name="position">0</property>
418+ </packing>
419+ </child>
420+ <child>
421+ <object class="GtkVBox" id="throttling">
422+ <property name="visible">True</property>
423+ <property name="can_focus">False</property>
424+ <child>
425+ <object class="GtkCheckButton" id="limit_bandwidth">
426+ <property name="label" translatable="yes">Limit file sync bandwidth usage</property>
427+ <property name="visible">True</property>
428+ <property name="can_focus">True</property>
429+ <property name="receives_default">False</property>
430+ <property name="use_action_appearance">False</property>
431+ <property name="draw_indicator">True</property>
432+ <signal name="toggled" handler="on_limit_bandwidth_toggled" swapped="no"/>
433+ </object>
434+ <packing>
435+ <property name="expand">True</property>
436+ <property name="fill">True</property>
437+ <property name="position">0</property>
438+ </packing>
439+ </child>
440+ <child>
441+ <object class="GtkTable" id="throttling_limits">
442+ <property name="visible">True</property>
443+ <property name="can_focus">False</property>
444+ <property name="n_rows">2</property>
445+ <property name="n_columns">3</property>
446+ <property name="column_spacing">3</property>
447+ <property name="row_spacing">3</property>
448+ <child>
449+ <object class="GtkLabel" id="max_upload_speed_label">
450+ <property name="visible">True</property>
451+ <property name="can_focus">False</property>
452+ <property name="xalign">0</property>
453+ <property name="xpad">22</property>
454+ <property name="label" translatable="yes">Max upload speed:</property>
455+ <property name="track_visited_links">False</property>
456+ </object>
457+ </child>
458+ <child>
459+ <object class="GtkLabel" id="max_download_speed_label">
460+ <property name="visible">True</property>
461+ <property name="can_focus">False</property>
462+ <property name="xalign">0</property>
463+ <property name="xpad">22</property>
464+ <property name="label" translatable="yes">Max download speed:</property>
465+ </object>
466+ <packing>
467+ <property name="top_attach">1</property>
468+ <property name="bottom_attach">2</property>
469+ </packing>
470+ </child>
471+ <child>
472+ <object class="GtkSpinButton" id="max_upload_speed">
473+ <property name="visible">True</property>
474+ <property name="can_focus">True</property>
475+ <property name="invisible_char">•</property>
476+ <property name="activates_default">True</property>
477+ <property name="invisible_char_set">True</property>
478+ <property name="adjustment">adjustment1</property>
479+ <signal name="value-changed" handler="on_max_upload_speed_value_changed" swapped="no"/>
480+ </object>
481+ <packing>
482+ <property name="left_attach">1</property>
483+ <property name="right_attach">2</property>
484+ <property name="x_options">GTK_FILL</property>
485+ <property name="y_options">GTK_FILL</property>
486+ </packing>
487+ </child>
488+ <child>
489+ <object class="GtkSpinButton" id="max_download_speed">
490+ <property name="visible">True</property>
491+ <property name="can_focus">True</property>
492+ <property name="invisible_char">•</property>
493+ <property name="activates_default">True</property>
494+ <property name="invisible_char_set">True</property>
495+ <property name="adjustment">adjustment2</property>
496+ <signal name="value-changed" handler="on_max_download_speed_value_changed" swapped="no"/>
497+ </object>
498+ <packing>
499+ <property name="left_attach">1</property>
500+ <property name="right_attach">2</property>
501+ <property name="top_attach">1</property>
502+ <property name="bottom_attach">2</property>
503+ </packing>
504+ </child>
505+ <child>
506+ <object class="GtkLabel" id="label1">
507+ <property name="visible">True</property>
508+ <property name="can_focus">False</property>
509+ <property name="label" translatable="yes">KiB/s</property>
510+ </object>
511+ <packing>
512+ <property name="left_attach">2</property>
513+ <property name="right_attach">3</property>
514+ </packing>
515+ </child>
516+ <child>
517+ <object class="GtkLabel" id="label2">
518+ <property name="visible">True</property>
519+ <property name="can_focus">False</property>
520+ <property name="label" translatable="yes">KiB/s</property>
521+ </object>
522+ <packing>
523+ <property name="left_attach">2</property>
524+ <property name="right_attach">3</property>
525+ <property name="top_attach">1</property>
526+ <property name="bottom_attach">2</property>
527+ </packing>
528+ </child>
529+ </object>
530+ <packing>
531+ <property name="expand">True</property>
532+ <property name="fill">True</property>
533+ <property name="position">1</property>
534+ </packing>
535+ </child>
536+ </object>
537+ <packing>
538+ <property name="expand">True</property>
539+ <property name="fill">True</property>
540+ <property name="position">1</property>
541 </packing>
542 </child>
543 </object>
544@@ -187,18 +216,21 @@
545 </object>
546 <packing>
547 <property name="expand">False</property>
548+ <property name="fill">True</property>
549 <property name="position">1</property>
550 </packing>
551 </child>
552 </object>
553 <packing>
554 <property name="expand">False</property>
555+ <property name="fill">True</property>
556 <property name="position">0</property>
557 </packing>
558 </child>
559 <child>
560 <object class="GtkVButtonBox" id="vbuttonbox1">
561 <property name="visible">True</property>
562+ <property name="can_focus">False</property>
563 <property name="layout_style">start</property>
564 <child>
565 <object class="GtkButton" id="remove">
566@@ -206,9 +238,10 @@
567 <property name="visible">True</property>
568 <property name="can_focus">True</property>
569 <property name="receives_default">True</property>
570+ <property name="use_action_appearance">False</property>
571 <property name="use_stock">True</property>
572- <signal name="clicked" handler="on_remove_clicked"/>
573- <signal name="activate" handler="on_remove_clicked"/>
574+ <signal name="activate" handler="on_remove_clicked" swapped="no"/>
575+ <signal name="clicked" handler="on_remove_clicked" swapped="no"/>
576 </object>
577 <packing>
578 <property name="expand">False</property>
579@@ -219,6 +252,7 @@
580 </object>
581 <packing>
582 <property name="expand">False</property>
583+ <property name="fill">True</property>
584 <property name="pack_type">end</property>
585 <property name="position">1</property>
586 </packing>
587@@ -226,14 +260,18 @@
588 </object>
589 <packing>
590 <property name="expand">False</property>
591+ <property name="fill">True</property>
592 <property name="position">0</property>
593 </packing>
594 </child>
595 <child>
596 <object class="GtkLabel" id="warning_label">
597 <property name="visible">True</property>
598+ <property name="can_focus">False</property>
599 </object>
600 <packing>
601+ <property name="expand">True</property>
602+ <property name="fill">True</property>
603 <property name="position">1</property>
604 </packing>
605 </child>
606
607=== modified file 'data/files.png'
608Binary files data/files.png 2011-02-12 03:07:22 +0000 and data/files.png 2011-03-10 03:11:54 +0000 differ
609=== modified file 'data/management.ui'
610--- data/management.ui 2011-02-23 13:57:42 +0000
611+++ data/management.ui 2011-03-10 03:11:54 +0000
612@@ -50,143 +50,120 @@
613 </packing>
614 </child>
615 <child>
616- <object class="GtkVBox" id="vbox1">
617+ <object class="GtkHBox" id="hbox2">
618 <property name="visible">True</property>
619 <property name="can_focus">False</property>
620 <child>
621- <object class="GtkHBox" id="hbox2">
622- <property name="visible">True</property>
623- <property name="can_focus">False</property>
624+ <object class="GtkHSeparator" id="hseparator1">
625+ <property name="visible">True</property>
626+ <property name="can_focus">False</property>
627+ </object>
628+ <packing>
629+ <property name="expand">True</property>
630+ <property name="fill">True</property>
631+ <property name="position">0</property>
632+ </packing>
633+ </child>
634+ <child>
635+ <object class="GtkHButtonBox" id="hbuttonbox1">
636+ <property name="visible">True</property>
637+ <property name="can_focus">False</property>
638+ <property name="layout_style">center</property>
639 <child>
640- <object class="GtkHSeparator" id="hseparator1">
641+ <object class="GtkRadioButton" id="dashboard_button">
642+ <property name="label" translatable="yes">Account</property>
643 <property name="visible">True</property>
644- <property name="can_focus">False</property>
645+ <property name="can_focus">True</property>
646+ <property name="receives_default">False</property>
647+ <property name="use_action_appearance">False</property>
648+ <property name="active">True</property>
649+ <property name="draw_indicator">False</property>
650 </object>
651 <packing>
652- <property name="expand">True</property>
653- <property name="fill">True</property>
654+ <property name="expand">False</property>
655+ <property name="fill">False</property>
656 <property name="position">0</property>
657 </packing>
658 </child>
659 <child>
660- <object class="GtkHButtonBox" id="hbuttonbox1">
661+ <object class="GtkRadioButton" id="volumes_button">
662+ <property name="label" translatable="yes">Cloud Folders</property>
663 <property name="visible">True</property>
664- <property name="can_focus">False</property>
665- <property name="layout_style">center</property>
666- <child>
667- <object class="GtkRadioButton" id="dashboard_button">
668- <property name="label" translatable="yes">Account</property>
669- <property name="visible">True</property>
670- <property name="can_focus">True</property>
671- <property name="receives_default">False</property>
672- <property name="use_action_appearance">False</property>
673- <property name="active">True</property>
674- <property name="draw_indicator">False</property>
675- </object>
676- <packing>
677- <property name="expand">False</property>
678- <property name="fill">False</property>
679- <property name="position">0</property>
680- </packing>
681- </child>
682- <child>
683- <object class="GtkRadioButton" id="volumes_button">
684- <property name="label" translatable="yes">Cloud Folders</property>
685- <property name="visible">True</property>
686- <property name="can_focus">True</property>
687- <property name="receives_default">False</property>
688- <property name="use_action_appearance">False</property>
689- <property name="draw_indicator">False</property>
690- <property name="group">dashboard_button</property>
691- </object>
692- <packing>
693- <property name="expand">False</property>
694- <property name="fill">False</property>
695- <property name="position">1</property>
696- </packing>
697- </child>
698- <child>
699- <object class="GtkRadioButton" id="shares_button">
700- <property name="label" translatable="yes">Shares</property>
701- <property name="can_focus">True</property>
702- <property name="receives_default">False</property>
703- <property name="use_action_appearance">False</property>
704- <property name="draw_indicator">False</property>
705- <property name="group">dashboard_button</property>
706- </object>
707- <packing>
708- <property name="expand">False</property>
709- <property name="fill">False</property>
710- <property name="position">2</property>
711- </packing>
712- </child>
713- <child>
714- <object class="GtkRadioButton" id="devices_button">
715- <property name="label" translatable="yes">Devices</property>
716- <property name="visible">True</property>
717- <property name="can_focus">True</property>
718- <property name="receives_default">False</property>
719- <property name="use_action_appearance">False</property>
720- <property name="draw_indicator">False</property>
721- <property name="group">dashboard_button</property>
722- </object>
723- <packing>
724- <property name="expand">False</property>
725- <property name="fill">False</property>
726- <property name="position">3</property>
727- </packing>
728- </child>
729- <child>
730- <object class="GtkRadioButton" id="services_button">
731- <property name="label" translatable="yes">Services</property>
732- <property name="visible">True</property>
733- <property name="can_focus">True</property>
734- <property name="receives_default">False</property>
735- <property name="use_action_appearance">False</property>
736- <property name="draw_indicator">False</property>
737- <property name="group">dashboard_button</property>
738- </object>
739- <packing>
740- <property name="expand">False</property>
741- <property name="fill">False</property>
742- <property name="position">4</property>
743- </packing>
744- </child>
745+ <property name="can_focus">True</property>
746+ <property name="receives_default">False</property>
747+ <property name="use_action_appearance">False</property>
748+ <property name="draw_indicator">False</property>
749+ <property name="group">dashboard_button</property>
750 </object>
751 <packing>
752 <property name="expand">False</property>
753- <property name="fill">True</property>
754+ <property name="fill">False</property>
755 <property name="position">1</property>
756 </packing>
757 </child>
758 <child>
759- <object class="GtkHSeparator" id="hseparator2">
760- <property name="visible">True</property>
761- <property name="can_focus">False</property>
762+ <object class="GtkRadioButton" id="shares_button">
763+ <property name="label" translatable="yes">Shares</property>
764+ <property name="can_focus">True</property>
765+ <property name="receives_default">False</property>
766+ <property name="use_action_appearance">False</property>
767+ <property name="draw_indicator">False</property>
768+ <property name="group">dashboard_button</property>
769 </object>
770 <packing>
771- <property name="expand">True</property>
772- <property name="fill">True</property>
773+ <property name="expand">False</property>
774+ <property name="fill">False</property>
775 <property name="position">2</property>
776 </packing>
777 </child>
778+ <child>
779+ <object class="GtkRadioButton" id="devices_button">
780+ <property name="label" translatable="yes">Devices</property>
781+ <property name="visible">True</property>
782+ <property name="can_focus">True</property>
783+ <property name="receives_default">False</property>
784+ <property name="use_action_appearance">False</property>
785+ <property name="draw_indicator">False</property>
786+ <property name="group">dashboard_button</property>
787+ </object>
788+ <packing>
789+ <property name="expand">False</property>
790+ <property name="fill">False</property>
791+ <property name="position">3</property>
792+ </packing>
793+ </child>
794+ <child>
795+ <object class="GtkRadioButton" id="services_button">
796+ <property name="label" translatable="yes">Services</property>
797+ <property name="visible">True</property>
798+ <property name="can_focus">True</property>
799+ <property name="receives_default">False</property>
800+ <property name="use_action_appearance">False</property>
801+ <property name="draw_indicator">False</property>
802+ <property name="group">dashboard_button</property>
803+ </object>
804+ <packing>
805+ <property name="expand">False</property>
806+ <property name="fill">False</property>
807+ <property name="position">4</property>
808+ </packing>
809+ </child>
810 </object>
811 <packing>
812 <property name="expand">False</property>
813 <property name="fill">True</property>
814- <property name="position">0</property>
815+ <property name="position">1</property>
816 </packing>
817 </child>
818 <child>
819- <object class="GtkImage" id="image1">
820+ <object class="GtkHSeparator" id="hseparator2">
821 <property name="visible">True</property>
822 <property name="can_focus">False</property>
823- <property name="pixbuf">banner.png</property>
824 </object>
825 <packing>
826- <property name="expand">False</property>
827+ <property name="expand">True</property>
828 <property name="fill">True</property>
829- <property name="position">1</property>
830+ <property name="position">2</property>
831 </packing>
832 </child>
833 </object>
834@@ -219,5 +196,141 @@
835 <property name="position">1</property>
836 </packing>
837 </child>
838+ <child>
839+ <object class="GtkHBox" id="hbox1">
840+ <property name="visible">True</property>
841+ <property name="can_focus">False</property>
842+ <property name="border_width">12</property>
843+ <child>
844+ <object class="GtkAlignment" id="alignment3">
845+ <property name="visible">True</property>
846+ <property name="can_focus">False</property>
847+ <property name="yalign">1</property>
848+ <property name="yscale">0</property>
849+ <child>
850+ <object class="GtkHButtonBox" id="hbuttonbox3">
851+ <property name="visible">True</property>
852+ <property name="can_focus">False</property>
853+ <property name="spacing">5</property>
854+ <property name="layout_style">start</property>
855+ <child>
856+ <object class="GtkLinkButton" id="linkbutton2">
857+ <property name="label" translatable="yes">Official Support</property>
858+ <property name="visible">True</property>
859+ <property name="can_focus">True</property>
860+ <property name="receives_default">True</property>
861+ <property name="use_action_appearance">False</property>
862+ <property name="relief">half</property>
863+ <property name="uri">https://one.ubuntu.com/support/</property>
864+ </object>
865+ <packing>
866+ <property name="expand">False</property>
867+ <property name="fill">False</property>
868+ <property name="position">0</property>
869+ </packing>
870+ </child>
871+ <child>
872+ <object class="GtkLinkButton" id="linkbutton4">
873+ <property name="label" translatable="yes">Community Support</property>
874+ <property name="visible">True</property>
875+ <property name="can_focus">True</property>
876+ <property name="receives_default">True</property>
877+ <property name="use_action_appearance">False</property>
878+ <property name="relief">half</property>
879+ <property name="uri">http://askubuntu.com/questions/tagged/ubuntu-one</property>
880+ </object>
881+ <packing>
882+ <property name="expand">False</property>
883+ <property name="fill">False</property>
884+ <property name="position">1</property>
885+ </packing>
886+ </child>
887+ </object>
888+ </child>
889+ </object>
890+ <packing>
891+ <property name="expand">False</property>
892+ <property name="fill">True</property>
893+ <property name="position">0</property>
894+ </packing>
895+ </child>
896+ <child>
897+ <object class="GtkHBox" id="hbox3">
898+ <property name="visible">True</property>
899+ <property name="can_focus">False</property>
900+ <child>
901+ <object class="GtkLabel" id="label4">
902+ <property name="visible">True</property>
903+ <property name="can_focus">False</property>
904+ <property name="label" translatable="yes">Talk to us on:</property>
905+ </object>
906+ <packing>
907+ <property name="expand">True</property>
908+ <property name="fill">True</property>
909+ <property name="position">0</property>
910+ </packing>
911+ </child>
912+ <child>
913+ <object class="GtkLinkButton" id="linkbutton5">
914+ <property name="visible">True</property>
915+ <property name="can_focus">True</property>
916+ <property name="receives_default">True</property>
917+ <property name="use_action_appearance">False</property>
918+ <property name="relief">none</property>
919+ <property name="uri">http://twitter.com/ubuntuone</property>
920+ <child>
921+ <object class="GtkImage" id="twitter">
922+ <property name="visible">True</property>
923+ <property name="can_focus">False</property>
924+ <property name="tooltip_text" translatable="yes">http://twitter.com/ubuntuone</property>
925+ <property name="pixbuf">twitter.png</property>
926+ </object>
927+ </child>
928+ </object>
929+ <packing>
930+ <property name="expand">False</property>
931+ <property name="fill">False</property>
932+ <property name="position">1</property>
933+ </packing>
934+ </child>
935+ <child>
936+ <object class="GtkLinkButton" id="linkbutton6">
937+ <property name="visible">True</property>
938+ <property name="can_focus">True</property>
939+ <property name="receives_default">True</property>
940+ <property name="use_action_appearance">False</property>
941+ <property name="relief">none</property>
942+ <property name="uri">http://www.facebook.com/ubuntuone</property>
943+ <child>
944+ <object class="GtkImage" id="facebook">
945+ <property name="visible">True</property>
946+ <property name="can_focus">False</property>
947+ <property name="tooltip_text" translatable="yes">http://www.facebook.com/ubuntuone</property>
948+ <property name="pixbuf">facebook.png</property>
949+ </object>
950+ </child>
951+ </object>
952+ <packing>
953+ <property name="expand">True</property>
954+ <property name="fill">True</property>
955+ <property name="position">2</property>
956+ </packing>
957+ </child>
958+ </object>
959+ <packing>
960+ <property name="expand">False</property>
961+ <property name="fill">True</property>
962+ <property name="pack_type">end</property>
963+ <property name="position">1</property>
964+ </packing>
965+ </child>
966+ </object>
967+ <packing>
968+ <property name="expand">False</property>
969+ <property name="fill">True</property>
970+ <property name="pack_type">end</property>
971+ <property name="position">1</property>
972+ </packing>
973+ </child>
974 </object>
975 </interface>
976
977=== added file 'data/music-store.png'
978Binary files data/music-store.png 1970-01-01 00:00:00 +0000 and data/music-store.png 2011-03-10 03:11:54 +0000 differ
979=== added file 'data/music-stream.png'
980Binary files data/music-stream.png 1970-01-01 00:00:00 +0000 and data/music-stream.png 2011-03-10 03:11:54 +0000 differ
981=== removed file 'data/music.png'
982Binary files data/music.png 2011-02-12 03:07:22 +0000 and data/music.png 1970-01-01 00:00:00 +0000 differ
983=== modified file 'data/notes.png'
984Binary files data/notes.png 2011-02-12 03:07:22 +0000 and data/notes.png 2011-03-10 03:11:54 +0000 differ
985=== modified file 'data/overview.png'
986Binary files data/overview.png 2010-12-06 12:27:11 +0000 and data/overview.png 2011-03-10 03:11:54 +0000 differ
987=== modified file 'data/overview.ui'
988--- data/overview.ui 2011-02-12 03:07:22 +0000
989+++ data/overview.ui 2011-03-10 03:11:54 +0000
990@@ -6,7 +6,7 @@
991 <property name="visible">True</property>
992 <property name="can_focus">False</property>
993 <child>
994- <object class="GtkEventBox" id="header">
995+ <object class="GtkEventBox" id="banner">
996 <property name="visible">True</property>
997 <property name="can_focus">False</property>
998 <child>
999@@ -35,247 +35,316 @@
1000 <property name="can_focus">False</property>
1001 <property name="shadow_type">none</property>
1002 <child>
1003- <object class="GtkHBox" id="hbox1">
1004+ <object class="GtkVBox" id="vbox1">
1005 <property name="visible">True</property>
1006 <property name="can_focus">False</property>
1007- <property name="border_width">20</property>
1008- <property name="spacing">5</property>
1009- <child>
1010- <object class="GtkTable" id="table1">
1011- <property name="visible">True</property>
1012- <property name="can_focus">False</property>
1013- <property name="n_rows">4</property>
1014- <property name="n_columns">2</property>
1015- <property name="column_spacing">10</property>
1016- <property name="row_spacing">10</property>
1017- <child>
1018- <object class="GtkImage" id="image1">
1019- <property name="visible">True</property>
1020- <property name="can_focus">False</property>
1021- <property name="pixbuf">files.png</property>
1022- </object>
1023- <packing>
1024- <property name="x_options">GTK_FILL</property>
1025- <property name="y_options">GTK_FILL</property>
1026- </packing>
1027- </child>
1028- <child>
1029- <object class="GtkImage" id="image2">
1030- <property name="visible">True</property>
1031- <property name="can_focus">False</property>
1032- <property name="pixbuf">contacts.png</property>
1033- </object>
1034- <packing>
1035- <property name="top_attach">1</property>
1036- <property name="bottom_attach">2</property>
1037- <property name="x_options">GTK_FILL</property>
1038- <property name="y_options">GTK_FILL</property>
1039- </packing>
1040- </child>
1041- <child>
1042- <object class="GtkImage" id="image3">
1043- <property name="visible">True</property>
1044- <property name="can_focus">False</property>
1045- <property name="pixbuf">music.png</property>
1046- </object>
1047- <packing>
1048- <property name="top_attach">2</property>
1049- <property name="bottom_attach">3</property>
1050- <property name="x_options">GTK_FILL</property>
1051- <property name="y_options">GTK_FILL</property>
1052- </packing>
1053- </child>
1054- <child>
1055- <object class="GtkImage" id="image4">
1056- <property name="visible">True</property>
1057- <property name="can_focus">False</property>
1058- <property name="pixbuf">notes.png</property>
1059- </object>
1060- <packing>
1061- <property name="top_attach">3</property>
1062- <property name="bottom_attach">4</property>
1063- <property name="x_options">GTK_FILL</property>
1064- <property name="y_options">GTK_FILL</property>
1065- </packing>
1066- </child>
1067- <child>
1068- <object class="GtkLabel" id="label3">
1069- <property name="visible">True</property>
1070- <property name="can_focus">False</property>
1071- <property name="xalign">0</property>
1072- <property name="label" translatable="yes">Files Anywhere
1073-&lt;span foreground="#909090"&gt;Backup and access your files from Windows, Ubuntu, or Mobile&lt;/span&gt;</property>
1074- <property name="use_markup">True</property>
1075- <property name="wrap">True</property>
1076- </object>
1077- <packing>
1078- <property name="left_attach">1</property>
1079- <property name="right_attach">2</property>
1080- </packing>
1081- </child>
1082- <child>
1083- <object class="GtkLabel" id="label4">
1084- <property name="visible">True</property>
1085- <property name="can_focus">False</property>
1086- <property name="xalign">0</property>
1087- <property name="label" translatable="yes">Keep connected
1088-&lt;span foreground="#909090"&gt;Unify your contacts accress Desktop, Mobile and Web&lt;/span&gt;</property>
1089- <property name="use_markup">True</property>
1090- <property name="wrap">True</property>
1091- </object>
1092- <packing>
1093- <property name="left_attach">1</property>
1094- <property name="right_attach">2</property>
1095- <property name="top_attach">1</property>
1096- <property name="bottom_attach">2</property>
1097- </packing>
1098- </child>
1099- <child>
1100- <object class="GtkLabel" id="label5">
1101- <property name="visible">True</property>
1102- <property name="can_focus">False</property>
1103- <property name="xalign">0</property>
1104- <property name="label" translatable="yes">Rock Out
1105-&lt;span foreground="#909090"&gt;Your library at your fingertips with Android, iPhone, and AirPlay
1106-Plus the Ubuntu One Music store to grow your collection&lt;/span&gt;</property>
1107- <property name="use_markup">True</property>
1108- <property name="wrap">True</property>
1109- </object>
1110- <packing>
1111- <property name="left_attach">1</property>
1112- <property name="right_attach">2</property>
1113- <property name="top_attach">2</property>
1114- <property name="bottom_attach">3</property>
1115- </packing>
1116- </child>
1117- <child>
1118- <object class="GtkLabel" id="label6">
1119- <property name="visible">True</property>
1120- <property name="can_focus">False</property>
1121- <property name="xalign">0</property>
1122- <property name="label" translatable="yes">Stay Productive
1123+ <child>
1124+ <object class="GtkLabel" id="label7">
1125+ <property name="visible">True</property>
1126+ <property name="can_focus">False</property>
1127+ <property name="label" translatable="yes">&lt;span font_size="xx-large" foreground="#4d4d4d"&gt;The Power of Your Personal Cloud&lt;/span&gt;</property>
1128+ <property name="use_markup">True</property>
1129+ </object>
1130+ <packing>
1131+ <property name="expand">False</property>
1132+ <property name="fill">True</property>
1133+ <property name="padding">12</property>
1134+ <property name="position">0</property>
1135+ </packing>
1136+ </child>
1137+ <child>
1138+ <object class="GtkHBox" id="hbox1">
1139+ <property name="visible">True</property>
1140+ <property name="can_focus">False</property>
1141+ <property name="border_width">20</property>
1142+ <property name="spacing">5</property>
1143+ <child>
1144+ <object class="GtkTable" id="table1">
1145+ <property name="visible">True</property>
1146+ <property name="can_focus">False</property>
1147+ <property name="n_rows">4</property>
1148+ <property name="n_columns">2</property>
1149+ <property name="column_spacing">10</property>
1150+ <child>
1151+ <object class="GtkImage" id="image1">
1152+ <property name="visible">True</property>
1153+ <property name="can_focus">False</property>
1154+ <property name="pixbuf">files.png</property>
1155+ </object>
1156+ <packing>
1157+ <property name="x_options">GTK_FILL</property>
1158+ <property name="y_options">GTK_FILL</property>
1159+ </packing>
1160+ </child>
1161+ <child>
1162+ <object class="GtkImage" id="image2">
1163+ <property name="visible">True</property>
1164+ <property name="can_focus">False</property>
1165+ <property name="pixbuf">music-stream.png</property>
1166+ </object>
1167+ <packing>
1168+ <property name="top_attach">1</property>
1169+ <property name="bottom_attach">2</property>
1170+ <property name="x_options">GTK_FILL</property>
1171+ <property name="y_options">GTK_FILL</property>
1172+ </packing>
1173+ </child>
1174+ <child>
1175+ <object class="GtkImage" id="image3">
1176+ <property name="visible">True</property>
1177+ <property name="can_focus">False</property>
1178+ <property name="pixbuf">contacts.png</property>
1179+ </object>
1180+ <packing>
1181+ <property name="top_attach">2</property>
1182+ <property name="bottom_attach">3</property>
1183+ <property name="x_options">GTK_FILL</property>
1184+ <property name="y_options">GTK_FILL</property>
1185+ </packing>
1186+ </child>
1187+ <child>
1188+ <object class="GtkImage" id="image4">
1189+ <property name="visible">True</property>
1190+ <property name="can_focus">False</property>
1191+ <property name="pixbuf">notes.png</property>
1192+ </object>
1193+ <packing>
1194+ <property name="top_attach">3</property>
1195+ <property name="bottom_attach">4</property>
1196+ <property name="x_options">GTK_FILL</property>
1197+ <property name="y_options">GTK_FILL</property>
1198+ </packing>
1199+ </child>
1200+ <child>
1201+ <object class="GtkLabel" id="label3">
1202+ <property name="visible">True</property>
1203+ <property name="can_focus">False</property>
1204+ <property name="xalign">0</property>
1205+ <property name="label" translatable="yes">Files Anywhere
1206+&lt;span foreground="#909090"&gt;Backup and access your files from Ubuntu, Windows or Mobile&lt;/span&gt;</property>
1207+ <property name="use_markup">True</property>
1208+ <property name="wrap">True</property>
1209+ </object>
1210+ <packing>
1211+ <property name="left_attach">1</property>
1212+ <property name="right_attach">2</property>
1213+ </packing>
1214+ </child>
1215+ <child>
1216+ <object class="GtkLabel" id="label4">
1217+ <property name="visible">True</property>
1218+ <property name="can_focus">False</property>
1219+ <property name="xalign">0</property>
1220+ <property name="label" translatable="yes">Keep Connected
1221+&lt;span foreground="#909090"&gt;Unify your contacts across Desktop, Mobile and Web&lt;/span&gt;</property>
1222+ <property name="use_markup">True</property>
1223+ <property name="wrap">True</property>
1224+ </object>
1225+ <packing>
1226+ <property name="left_attach">1</property>
1227+ <property name="right_attach">2</property>
1228+ <property name="top_attach">2</property>
1229+ <property name="bottom_attach">3</property>
1230+ </packing>
1231+ </child>
1232+ <child>
1233+ <object class="GtkLabel" id="label5">
1234+ <property name="visible">True</property>
1235+ <property name="can_focus">False</property>
1236+ <property name="xalign">0</property>
1237+ <property name="label" translatable="yes">Rock Out
1238+&lt;span foreground="#909090"&gt;Your entire collection follows you around with music streaming to Android and iPhone&lt;/span&gt;</property>
1239+ <property name="use_markup">True</property>
1240+ <property name="wrap">True</property>
1241+ </object>
1242+ <packing>
1243+ <property name="left_attach">1</property>
1244+ <property name="right_attach">2</property>
1245+ <property name="top_attach">1</property>
1246+ <property name="bottom_attach">2</property>
1247+ </packing>
1248+ </child>
1249+ <child>
1250+ <object class="GtkLabel" id="label6">
1251+ <property name="visible">True</property>
1252+ <property name="can_focus">False</property>
1253+ <property name="xalign">0</property>
1254+ <property name="label" translatable="yes">Stay Productive
1255 &lt;span foreground="#909090"&gt;Keep your Firefox bookmarks and Tomboy notes synced&lt;/span&gt;</property>
1256- <property name="use_markup">True</property>
1257- <property name="wrap">True</property>
1258+ <property name="use_markup">True</property>
1259+ <property name="wrap">True</property>
1260+ </object>
1261+ <packing>
1262+ <property name="left_attach">1</property>
1263+ <property name="right_attach">2</property>
1264+ <property name="top_attach">3</property>
1265+ <property name="bottom_attach">4</property>
1266+ </packing>
1267+ </child>
1268 </object>
1269 <packing>
1270- <property name="left_attach">1</property>
1271- <property name="right_attach">2</property>
1272- <property name="top_attach">3</property>
1273- <property name="bottom_attach">4</property>
1274+ <property name="expand">True</property>
1275+ <property name="fill">True</property>
1276+ <property name="position">0</property>
1277 </packing>
1278 </child>
1279- </object>
1280- <packing>
1281- <property name="expand">True</property>
1282- <property name="fill">True</property>
1283- <property name="position">0</property>
1284- </packing>
1285- </child>
1286- <child>
1287- <object class="GtkVBox" id="vbox1">
1288- <property name="visible">True</property>
1289- <property name="can_focus">False</property>
1290 <child>
1291- <object class="GtkLabel" id="warning_label">
1292+ <object class="GtkVBox" id="vbox2">
1293 <property name="visible">True</property>
1294 <property name="can_focus">False</property>
1295- <property name="label">A warning label that can be long. Possible really long, let's see how it behaves.</property>
1296- <property name="wrap">True</property>
1297+ <child>
1298+ <object class="GtkLabel" id="warning_label">
1299+ <property name="visible">True</property>
1300+ <property name="can_focus">False</property>
1301+ <property name="label">A warning label that can be long. Possible really long, let's see how it behaves.</property>
1302+ <property name="wrap">True</property>
1303+ </object>
1304+ <packing>
1305+ <property name="expand">False</property>
1306+ <property name="fill">True</property>
1307+ <property name="position">0</property>
1308+ </packing>
1309+ </child>
1310+ <child>
1311+ <object class="GtkAlignment" id="alignment2">
1312+ <property name="visible">True</property>
1313+ <property name="can_focus">False</property>
1314+ <property name="xscale">0</property>
1315+ <property name="yscale">0</property>
1316+ <child>
1317+ <object class="GtkVBox" id="vbox3">
1318+ <property name="visible">True</property>
1319+ <property name="can_focus">False</property>
1320+ <property name="spacing">10</property>
1321+ <child>
1322+ <object class="GtkButton" id="learn_more_button">
1323+ <property name="visible">True</property>
1324+ <property name="can_focus">True</property>
1325+ <property name="receives_default">True</property>
1326+ <property name="use_action_appearance">False</property>
1327+ <signal name="clicked" handler="on_learn_more_button_clicked" swapped="no"/>
1328+ <child>
1329+ <object class="GtkVBox" id="vbox5">
1330+ <property name="visible">True</property>
1331+ <property name="can_focus">False</property>
1332+ <child>
1333+ <object class="GtkImage" id="image5">
1334+ <property name="visible">True</property>
1335+ <property name="can_focus">False</property>
1336+ <property name="pixel_size">45</property>
1337+ <property name="icon_name">ubuntuone</property>
1338+ </object>
1339+ <packing>
1340+ <property name="expand">True</property>
1341+ <property name="fill">True</property>
1342+ <property name="position">0</property>
1343+ </packing>
1344+ </child>
1345+ <child>
1346+ <object class="GtkLabel" id="label8">
1347+ <property name="visible">True</property>
1348+ <property name="can_focus">False</property>
1349+ <property name="label" translatable="yes">&lt;span foreground="#909090"&gt;Learn More&lt;/span&gt;</property>
1350+ <property name="use_markup">True</property>
1351+ </object>
1352+ <packing>
1353+ <property name="expand">True</property>
1354+ <property name="fill">True</property>
1355+ <property name="position">1</property>
1356+ </packing>
1357+ </child>
1358+ </object>
1359+ </child>
1360+ </object>
1361+ <packing>
1362+ <property name="expand">True</property>
1363+ <property name="fill">True</property>
1364+ <property name="position">0</property>
1365+ </packing>
1366+ </child>
1367+ <child>
1368+ <object class="GtkButton" id="join_now_button">
1369+ <property name="visible">True</property>
1370+ <property name="can_focus">True</property>
1371+ <property name="can_default">True</property>
1372+ <property name="receives_default">True</property>
1373+ <property name="use_action_appearance">False</property>
1374+ <signal name="clicked" handler="on_join_now_button_clicked" swapped="no"/>
1375+ <child>
1376+ <object class="GtkVBox" id="vbox4">
1377+ <property name="visible">True</property>
1378+ <property name="can_focus">False</property>
1379+ <property name="spacing">5</property>
1380+ <child>
1381+ <object class="GtkLabel" id="label1">
1382+ <property name="visible">True</property>
1383+ <property name="can_focus">False</property>
1384+ <property name="label" translatable="yes">&lt;span font_size="xx-large" foreground="#4d4d4d"&gt;Join now&lt;/span&gt;</property>
1385+ <property name="use_markup">True</property>
1386+ </object>
1387+ <packing>
1388+ <property name="expand">True</property>
1389+ <property name="fill">True</property>
1390+ <property name="position">0</property>
1391+ </packing>
1392+ </child>
1393+ <child>
1394+ <object class="GtkLabel" id="label2">
1395+ <property name="visible">True</property>
1396+ <property name="can_focus">False</property>
1397+ <property name="label" translatable="yes">&lt;span foreground="#909090"&gt;2GB of free storage&lt;/span&gt;</property>
1398+ <property name="use_markup">True</property>
1399+ </object>
1400+ <packing>
1401+ <property name="expand">True</property>
1402+ <property name="fill">True</property>
1403+ <property name="position">1</property>
1404+ </packing>
1405+ </child>
1406+ </object>
1407+ </child>
1408+ </object>
1409+ <packing>
1410+ <property name="expand">False</property>
1411+ <property name="fill">True</property>
1412+ <property name="position">1</property>
1413+ </packing>
1414+ </child>
1415+ <child>
1416+ <object class="GtkLinkButton" id="connect_button">
1417+ <property name="label" translatable="yes">I already have an account!</property>
1418+ <property name="visible">True</property>
1419+ <property name="can_focus">True</property>
1420+ <property name="receives_default">True</property>
1421+ <property name="use_action_appearance">False</property>
1422+ <property name="relief">none</property>
1423+ <signal name="clicked" handler="on_connect_button_clicked" swapped="no"/>
1424+ </object>
1425+ <packing>
1426+ <property name="expand">False</property>
1427+ <property name="fill">False</property>
1428+ <property name="position">2</property>
1429+ </packing>
1430+ </child>
1431+ </object>
1432+ </child>
1433+ </object>
1434+ <packing>
1435+ <property name="expand">True</property>
1436+ <property name="fill">True</property>
1437+ <property name="position">1</property>
1438+ </packing>
1439+ </child>
1440 </object>
1441 <packing>
1442 <property name="expand">False</property>
1443 <property name="fill">True</property>
1444- <property name="position">0</property>
1445- </packing>
1446- </child>
1447- <child>
1448- <object class="GtkAlignment" id="alignment2">
1449- <property name="visible">True</property>
1450- <property name="can_focus">False</property>
1451- <property name="xscale">0</property>
1452- <property name="yscale">0</property>
1453- <child>
1454- <object class="GtkVBox" id="vbox2">
1455- <property name="visible">True</property>
1456- <property name="can_focus">False</property>
1457- <property name="spacing">10</property>
1458- <child>
1459- <object class="GtkButton" id="join_now_button">
1460- <property name="visible">True</property>
1461- <property name="can_focus">True</property>
1462- <property name="can_default">True</property>
1463- <property name="receives_default">True</property>
1464- <property name="use_action_appearance">False</property>
1465- <signal name="clicked" handler="on_join_now_button_clicked" swapped="no"/>
1466- <child>
1467- <object class="GtkVBox" id="vbox3">
1468- <property name="visible">True</property>
1469- <property name="can_focus">False</property>
1470- <property name="spacing">5</property>
1471- <child>
1472- <object class="GtkLabel" id="label1">
1473- <property name="visible">True</property>
1474- <property name="can_focus">False</property>
1475- <property name="label" translatable="yes">&lt;span font_size="xx-large"&gt;Join now&lt;/span&gt;</property>
1476- <property name="use_markup">True</property>
1477- </object>
1478- <packing>
1479- <property name="expand">True</property>
1480- <property name="fill">True</property>
1481- <property name="position">0</property>
1482- </packing>
1483- </child>
1484- <child>
1485- <object class="GtkLabel" id="label2">
1486- <property name="visible">True</property>
1487- <property name="can_focus">False</property>
1488- <property name="label" translatable="yes">&lt;span foreground="#909090"&gt;2GB of free storage&lt;/span&gt;</property>
1489- <property name="use_markup">True</property>
1490- </object>
1491- <packing>
1492- <property name="expand">True</property>
1493- <property name="fill">True</property>
1494- <property name="position">1</property>
1495- </packing>
1496- </child>
1497- </object>
1498- </child>
1499- </object>
1500- <packing>
1501- <property name="expand">False</property>
1502- <property name="fill">True</property>
1503- <property name="position">0</property>
1504- </packing>
1505- </child>
1506- <child>
1507- <object class="GtkLinkButton" id="connect_button">
1508- <property name="label" translatable="yes">I already have an account!</property>
1509- <property name="visible">True</property>
1510- <property name="can_focus">True</property>
1511- <property name="receives_default">True</property>
1512- <property name="use_action_appearance">False</property>
1513- <property name="relief">none</property>
1514- <signal name="clicked" handler="on_connect_button_clicked" swapped="no"/>
1515- </object>
1516- <packing>
1517- <property name="expand">False</property>
1518- <property name="fill">False</property>
1519- <property name="position">1</property>
1520- </packing>
1521- </child>
1522- </object>
1523- </child>
1524- </object>
1525- <packing>
1526- <property name="expand">True</property>
1527- <property name="fill">True</property>
1528 <property name="position">1</property>
1529 </packing>
1530 </child>
1531 </object>
1532 <packing>
1533- <property name="expand">False</property>
1534+ <property name="expand">True</property>
1535 <property name="fill">True</property>
1536 <property name="position">1</property>
1537 </packing>
1538
1539=== added file 'data/services-bookmarks.png'
1540Binary files data/services-bookmarks.png 1970-01-01 00:00:00 +0000 and data/services-bookmarks.png 2011-03-10 03:11:54 +0000 differ
1541=== added file 'data/services-contacts.png'
1542Binary files data/services-contacts.png 1970-01-01 00:00:00 +0000 and data/services-contacts.png 2011-03-10 03:11:54 +0000 differ
1543=== added file 'data/services-files-example.png'
1544Binary files data/services-files-example.png 1970-01-01 00:00:00 +0000 and data/services-files-example.png 2011-03-10 03:11:54 +0000 differ
1545=== added file 'data/services-files.png'
1546Binary files data/services-files.png 1970-01-01 00:00:00 +0000 and data/services-files.png 2011-03-10 03:11:54 +0000 differ
1547=== modified file 'data/services.ui'
1548--- data/services.ui 2011-01-25 19:08:59 +0000
1549+++ data/services.ui 2011-03-10 03:11:54 +0000
1550@@ -4,8 +4,7 @@
1551 <!-- interface-naming-policy project-wide -->
1552 <object class="GtkVBox" id="itself">
1553 <property name="visible">True</property>
1554- <property name="border_width">10</property>
1555- <property name="spacing">10</property>
1556+ <property name="can_focus">False</property>
1557 <child>
1558 <object class="GtkScrolledWindow" id="scrolledwindow1">
1559 <property name="visible">True</property>
1560@@ -15,44 +14,338 @@
1561 <child>
1562 <object class="GtkViewport" id="viewport1">
1563 <property name="visible">True</property>
1564+ <property name="can_focus">False</property>
1565 <property name="resize_mode">queue</property>
1566 <property name="shadow_type">none</property>
1567 <child>
1568 <object class="GtkVBox" id="vbox1">
1569 <property name="visible">True</property>
1570+ <property name="can_focus">False</property>
1571+ <property name="spacing">5</property>
1572 <child>
1573- <object class="GtkAlignment" id="alignment1">
1574+ <object class="GtkAspectFrame" id="files">
1575 <property name="visible">True</property>
1576+ <property name="can_focus">False</property>
1577+ <property name="label_xalign">0</property>
1578+ <property name="shadow_type">out</property>
1579 <child>
1580- <object class="GtkVBox" id="replications">
1581+ <object class="GtkHBox" id="hbox2">
1582 <property name="visible">True</property>
1583- <property name="spacing">5</property>
1584- <child>
1585- <placeholder/>
1586+ <property name="can_focus">False</property>
1587+ <property name="border_width">5</property>
1588+ <child>
1589+ <object class="GtkTable" id="table1">
1590+ <property name="visible">True</property>
1591+ <property name="can_focus">False</property>
1592+ <property name="n_rows">3</property>
1593+ <property name="n_columns">3</property>
1594+ <property name="row_spacing">5</property>
1595+ <child>
1596+ <object class="GtkCheckButton" id="file_sync_check">
1597+ <property name="visible">True</property>
1598+ <property name="can_focus">True</property>
1599+ <property name="receives_default">False</property>
1600+ <property name="use_action_appearance">False</property>
1601+ <property name="draw_indicator">True</property>
1602+ </object>
1603+ </child>
1604+ <child>
1605+ <object class="GtkImage" id="image2">
1606+ <property name="visible">True</property>
1607+ <property name="can_focus">False</property>
1608+ <property name="xpad">5</property>
1609+ <property name="pixbuf">services-files.png</property>
1610+ </object>
1611+ <packing>
1612+ <property name="left_attach">1</property>
1613+ <property name="right_attach">2</property>
1614+ </packing>
1615+ </child>
1616+ <child>
1617+ <object class="GtkLabel" id="label1">
1618+ <property name="visible">True</property>
1619+ <property name="can_focus">False</property>
1620+ <property name="xalign">0</property>
1621+ <property name="label" translatable="yes">Enable File Sync</property>
1622+ </object>
1623+ <packing>
1624+ <property name="left_attach">2</property>
1625+ <property name="right_attach">3</property>
1626+ </packing>
1627+ </child>
1628+ <child>
1629+ <object class="GtkLabel" id="label2">
1630+ <property name="visible">True</property>
1631+ <property name="can_focus">False</property>
1632+ <property name="xalign">0</property>
1633+ <property name="yalign">0</property>
1634+ <property name="label" translatable="yes">&lt;span font_size="small"&gt;Enable and then choose which folders you want to access from the Web or any device you connected to Ubuntu One
1635+
1636+Simply drag and drop any file or folder to your Ubuntu One folder on this computer&lt;/span&gt;</property>
1637+ <property name="use_markup">True</property>
1638+ <property name="wrap">True</property>
1639+ <property name="width_chars">30</property>
1640+ </object>
1641+ <packing>
1642+ <property name="left_attach">2</property>
1643+ <property name="right_attach">3</property>
1644+ <property name="top_attach">1</property>
1645+ <property name="bottom_attach">2</property>
1646+ </packing>
1647+ </child>
1648+ <child>
1649+ <object class="GtkHButtonBox" id="hbuttonbox1">
1650+ <property name="visible">True</property>
1651+ <property name="can_focus">False</property>
1652+ <child>
1653+ <object class="GtkButton" id="file_sync_button">
1654+ <property name="label" translatable="yes">_Show me my Ubuntu One folder</property>
1655+ <property name="visible">True</property>
1656+ <property name="can_focus">True</property>
1657+ <property name="receives_default">True</property>
1658+ <property name="use_action_appearance">False</property>
1659+ <property name="use_underline">True</property>
1660+ <signal name="clicked" handler="on_file_sync_button_clicked" swapped="no"/>
1661+ </object>
1662+ <packing>
1663+ <property name="expand">False</property>
1664+ <property name="fill">False</property>
1665+ <property name="position">0</property>
1666+ </packing>
1667+ </child>
1668+ </object>
1669+ <packing>
1670+ <property name="left_attach">2</property>
1671+ <property name="right_attach">3</property>
1672+ <property name="top_attach">2</property>
1673+ <property name="bottom_attach">3</property>
1674+ </packing>
1675+ </child>
1676+ <child>
1677+ <placeholder/>
1678+ </child>
1679+ <child>
1680+ <placeholder/>
1681+ </child>
1682+ <child>
1683+ <placeholder/>
1684+ </child>
1685+ <child>
1686+ <placeholder/>
1687+ </child>
1688+ </object>
1689+ <packing>
1690+ <property name="expand">False</property>
1691+ <property name="fill">True</property>
1692+ <property name="position">0</property>
1693+ </packing>
1694+ </child>
1695+ <child>
1696+ <object class="GtkImage" id="image1">
1697+ <property name="visible">True</property>
1698+ <property name="can_focus">False</property>
1699+ <property name="xpad">5</property>
1700+ <property name="ypad">5</property>
1701+ <property name="pixbuf">services-files-example.png</property>
1702+ </object>
1703+ <packing>
1704+ <property name="expand">False</property>
1705+ <property name="fill">True</property>
1706+ <property name="pack_type">end</property>
1707+ <property name="position">1</property>
1708+ </packing>
1709 </child>
1710 </object>
1711 </child>
1712 </object>
1713 <packing>
1714- <property name="expand">False</property>
1715+ <property name="expand">True</property>
1716+ <property name="fill">True</property>
1717 <property name="position">0</property>
1718 </packing>
1719 </child>
1720 <child>
1721- <object class="GtkAlignment" id="alignment2">
1722+ <object class="GtkAspectFrame" id="replications">
1723 <property name="visible">True</property>
1724+ <property name="can_focus">False</property>
1725+ <property name="label_xalign">0</property>
1726+ <property name="shadow_type">out</property>
1727 <child>
1728- <object class="GtkVBox" id="files">
1729+ <object class="GtkHBox" id="hbox3">
1730 <property name="visible">True</property>
1731- <property name="spacing">5</property>
1732- <child>
1733- <placeholder/>
1734+ <property name="can_focus">False</property>
1735+ <property name="border_width">5</property>
1736+ <child>
1737+ <object class="GtkVBox" id="contacts">
1738+ <property name="visible">True</property>
1739+ <property name="can_focus">False</property>
1740+ <child>
1741+ <object class="GtkTable" id="contacts_sync">
1742+ <property name="visible">True</property>
1743+ <property name="can_focus">False</property>
1744+ <property name="n_rows">2</property>
1745+ <property name="n_columns">3</property>
1746+ <property name="row_spacing">5</property>
1747+ <child>
1748+ <object class="GtkCheckButton" id="contacts_check">
1749+ <property name="visible">True</property>
1750+ <property name="can_focus">True</property>
1751+ <property name="receives_default">False</property>
1752+ <property name="use_action_appearance">False</property>
1753+ <property name="draw_indicator">True</property>
1754+ </object>
1755+ </child>
1756+ <child>
1757+ <object class="GtkImage" id="image3">
1758+ <property name="visible">True</property>
1759+ <property name="can_focus">False</property>
1760+ <property name="xpad">5</property>
1761+ <property name="pixbuf">services-contacts.png</property>
1762+ </object>
1763+ <packing>
1764+ <property name="left_attach">1</property>
1765+ <property name="right_attach">2</property>
1766+ </packing>
1767+ </child>
1768+ <child>
1769+ <object class="GtkLabel" id="label4">
1770+ <property name="visible">True</property>
1771+ <property name="can_focus">False</property>
1772+ <property name="xalign">0</property>
1773+ <property name="label" translatable="yes">Enable Contacts Sync</property>
1774+ </object>
1775+ <packing>
1776+ <property name="left_attach">2</property>
1777+ <property name="right_attach">3</property>
1778+ </packing>
1779+ </child>
1780+ <child>
1781+ <object class="GtkLabel" id="label5">
1782+ <property name="visible">True</property>
1783+ <property name="can_focus">True</property>
1784+ <property name="xalign">0</property>
1785+ <property name="yalign">0</property>
1786+ <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>
1787+ <property name="use_markup">True</property>
1788+ <property name="wrap">True</property>
1789+ <property name="width_chars">30</property>
1790+ </object>
1791+ <packing>
1792+ <property name="left_attach">2</property>
1793+ <property name="right_attach">3</property>
1794+ <property name="top_attach">1</property>
1795+ <property name="bottom_attach">2</property>
1796+ </packing>
1797+ </child>
1798+ <child>
1799+ <placeholder/>
1800+ </child>
1801+ <child>
1802+ <placeholder/>
1803+ </child>
1804+ </object>
1805+ <packing>
1806+ <property name="expand">False</property>
1807+ <property name="fill">True</property>
1808+ <property name="position">0</property>
1809+ </packing>
1810+ </child>
1811+ </object>
1812+ <packing>
1813+ <property name="expand">False</property>
1814+ <property name="fill">True</property>
1815+ <property name="position">0</property>
1816+ </packing>
1817+ </child>
1818+ <child>
1819+ <object class="GtkVBox" id="bookmarks">
1820+ <property name="visible">True</property>
1821+ <property name="can_focus">False</property>
1822+ <child>
1823+ <object class="GtkTable" id="bookmarks_sync">
1824+ <property name="visible">True</property>
1825+ <property name="can_focus">False</property>
1826+ <property name="n_rows">2</property>
1827+ <property name="n_columns">3</property>
1828+ <property name="row_spacing">5</property>
1829+ <child>
1830+ <object class="GtkCheckButton" id="bookmarks_check">
1831+ <property name="visible">True</property>
1832+ <property name="can_focus">True</property>
1833+ <property name="receives_default">False</property>
1834+ <property name="use_action_appearance">False</property>
1835+ <property name="draw_indicator">True</property>
1836+ </object>
1837+ </child>
1838+ <child>
1839+ <object class="GtkImage" id="image4">
1840+ <property name="visible">True</property>
1841+ <property name="can_focus">False</property>
1842+ <property name="xpad">5</property>
1843+ <property name="pixbuf">services-bookmarks.png</property>
1844+ </object>
1845+ <packing>
1846+ <property name="left_attach">1</property>
1847+ <property name="right_attach">2</property>
1848+ </packing>
1849+ </child>
1850+ <child>
1851+ <object class="GtkLabel" id="label6">
1852+ <property name="visible">True</property>
1853+ <property name="can_focus">False</property>
1854+ <property name="xalign">0</property>
1855+ <property name="label" translatable="yes">Enable Bookmarks Sync</property>
1856+ </object>
1857+ <packing>
1858+ <property name="left_attach">2</property>
1859+ <property name="right_attach">3</property>
1860+ </packing>
1861+ </child>
1862+ <child>
1863+ <object class="GtkLabel" id="label7">
1864+ <property name="visible">True</property>
1865+ <property name="can_focus">False</property>
1866+ <property name="xalign">0</property>
1867+ <property name="yalign">0</property>
1868+ <property name="label" translatable="yes">&lt;span font_size="small"&gt;Bookmarks sync works with Firefox. Once enabled, you will need to install a plugin&lt;/span&gt;</property>
1869+ <property name="use_markup">True</property>
1870+ <property name="wrap">True</property>
1871+ <property name="width_chars">30</property>
1872+ </object>
1873+ <packing>
1874+ <property name="left_attach">2</property>
1875+ <property name="right_attach">3</property>
1876+ <property name="top_attach">1</property>
1877+ <property name="bottom_attach">2</property>
1878+ </packing>
1879+ </child>
1880+ <child>
1881+ <placeholder/>
1882+ </child>
1883+ <child>
1884+ <placeholder/>
1885+ </child>
1886+ </object>
1887+ <packing>
1888+ <property name="expand">False</property>
1889+ <property name="fill">True</property>
1890+ <property name="position">0</property>
1891+ </packing>
1892+ </child>
1893+ </object>
1894+ <packing>
1895+ <property name="expand">False</property>
1896+ <property name="fill">True</property>
1897+ <property name="pack_type">end</property>
1898+ <property name="position">1</property>
1899+ </packing>
1900 </child>
1901 </object>
1902 </child>
1903 </object>
1904 <packing>
1905- <property name="expand">False</property>
1906+ <property name="expand">True</property>
1907+ <property name="fill">True</property>
1908 <property name="position">1</property>
1909 </packing>
1910 </child>
1911@@ -62,6 +355,8 @@
1912 </child>
1913 </object>
1914 <packing>
1915+ <property name="expand">True</property>
1916+ <property name="fill">True</property>
1917 <property name="position">0</property>
1918 </packing>
1919 </child>
1920
1921=== modified file 'data/volumes.ui'
1922--- data/volumes.ui 2011-02-12 03:07:22 +0000
1923+++ data/volumes.ui 2011-03-10 03:11:54 +0000
1924@@ -2,26 +2,6 @@
1925 <interface>
1926 <requires lib="gtk+" version="2.22"/>
1927 <!-- interface-naming-policy project-wide -->
1928- <object class="GtkTreeStore" id="volumes_store">
1929- <columns>
1930- <!-- column-name description -->
1931- <column type="gchararray"/>
1932- <!-- column-name subscribed -->
1933- <column type="gboolean"/>
1934- <!-- column-name icon-name -->
1935- <column type="gchararray"/>
1936- <!-- column-name subscribed-visible -->
1937- <column type="gboolean"/>
1938- <!-- column-name subscribed-sensitive -->
1939- <column type="gboolean"/>
1940- <!-- column-name icon-size -->
1941- <column type="gint"/>
1942- <!-- column-name identifier -->
1943- <column type="gchararray"/>
1944- <!-- column-name path -->
1945- <column type="gchararray"/>
1946- </columns>
1947- </object>
1948 <object class="GtkAlignment" id="itself">
1949 <property name="visible">True</property>
1950 <property name="can_focus">False</property>
1951@@ -94,4 +74,24 @@
1952 </object>
1953 </child>
1954 </object>
1955+ <object class="GtkTreeStore" id="volumes_store">
1956+ <columns>
1957+ <!-- column-name description -->
1958+ <column type="gchararray"/>
1959+ <!-- column-name subscribed -->
1960+ <column type="gboolean"/>
1961+ <!-- column-name icon-name -->
1962+ <column type="gchararray"/>
1963+ <!-- column-name subscribed-visible -->
1964+ <column type="gboolean"/>
1965+ <!-- column-name subscribed-sensitive -->
1966+ <column type="gboolean"/>
1967+ <!-- column-name icon-size -->
1968+ <column type="gint"/>
1969+ <!-- column-name identifier -->
1970+ <column type="gchararray"/>
1971+ <!-- column-name path -->
1972+ <column type="gchararray"/>
1973+ </columns>
1974+ </object>
1975 </interface>
1976
1977=== modified file 'debian/changelog'
1978--- debian/changelog 2011-02-28 15:38:15 +0000
1979+++ debian/changelog 2011-03-10 03:11:54 +0000
1980@@ -1,3 +1,27 @@
1981+ubuntuone-control-panel (0.9.1-0ubuntu1) natty; urgency=low
1982+
1983+ * New upstream release.
1984+ - Display notice when merging volumes (LP: #674462)
1985+ - Show visible message when out of space (LP: #701729)
1986+ - Move 'get support' button (LP: #706661)
1987+ - Purchased Music folder not clearly shown (LP: #720650)
1988+ - More UI tweaks (LP: #728663)
1989+ - Redesign Services tab (LP: #729361)
1990+ - Better icon for folder owner (LP: #706034)
1991+ - Center status messages on toolbar (LP: #715715)
1992+ - Alter bandwidth throttling layout (LP: #715812)
1993+ - Tooltip for 'Edit' is unhelpful (LP: #715822)
1994+ - Tooltip for 'Buy more storage' not helpful (LP: #715875)
1995+ - Improve off-line experience (LP: #720990)
1996+ - Make link buttons in Account tab regular buttons (LP: #725143)
1997+ - Typo: 'accress' (LP: #725802)
1998+ - Show local device first in devices list (LP: #727996)
1999+ - Misc. improvements to GTK+ UI (LP: #727998)
2000+ - Service names should use title format (LP: #728027)
2001+ * Require new version of ubuntuone-client
2002+
2003+ -- Rodney Dawes <rodney.dawes@ubuntu.com> Wed, 09 Mar 2011 21:59:45 -0500
2004+
2005 ubuntuone-control-panel (0.9.0-0ubuntu1) natty; urgency=low
2006
2007 * New upstream release:
2008
2009=== modified file 'debian/control'
2010--- debian/control 2011-02-28 15:38:15 +0000
2011+++ debian/control 2011-03-10 03:11:54 +0000
2012@@ -17,7 +17,7 @@
2013 ${python:Depends},
2014 python,
2015 python-ubuntuone-control-panel (= ${binary:Version}),
2016- ubuntuone-client (>= 1.5.4),
2017+ ubuntuone-client (>= 1.5.6),
2018 Recommends: ubuntuone-control-panel-gui
2019 Description: Ubuntu One Control Panel
2020 Desktop application to manage a Ubuntu One account.
2021@@ -37,7 +37,7 @@
2022 python-simplejson,
2023 python-twisted-core,
2024 python-twisted-web,
2025- python-ubuntuone-client (>= 1.5.4),
2026+ python-ubuntuone-client (>= 1.5.6),
2027 ubuntu-sso-client (>= 1.1.11),
2028 Description: Ubuntu One Control Panel Python Libraries
2029 Ubuntu One Control Panel provides a Python library to manage an Ubuntu One
2030@@ -54,9 +54,9 @@
2031 python-defer,
2032 python-gobject,
2033 python-gtk2,
2034- python-ubuntuone-client (>= 1.5.4),
2035+ python-ubuntuone-client (>= 1.5.6),
2036 ubuntu-sso-client (>= 1.1.11),
2037- ubuntuone-client (>= 1.5.4),
2038+ ubuntuone-client (>= 1.5.6),
2039 ubuntuone-control-panel (= ${binary:Version}),
2040 Provides: ubuntuone-control-panel-gui
2041 Description: Ubuntu One Control Panel
2042
2043=== modified file 'pylintrc'
2044--- pylintrc 2011-01-07 20:07:39 +0000
2045+++ pylintrc 2011-03-10 03:11:54 +0000
2046@@ -49,7 +49,7 @@
2047 # Disable the message(s) with the given id(s) or categories
2048 # W0142: Used * or ** magic
2049 # W0613: Unused argument 'yyy'
2050-disable=R,I,W0142,W0613
2051+disable=R,I,W0142,W0613,W0511
2052
2053
2054 [REPORTS]
2055
2056=== modified file 'setup.py'
2057--- setup.py 2011-02-28 15:38:15 +0000
2058+++ setup.py 2011-03-10 03:11:54 +0000
2059@@ -76,7 +76,7 @@
2060
2061 DistUtilsExtra.auto.setup(
2062 name='ubuntuone-control-panel',
2063- version='0.9.0',
2064+ version='0.9.1',
2065 license='GPL v3',
2066 author='Natalia Bidart',
2067 author_email='natalia.bidart@canonical.com',
2068
2069=== modified file 'ubuntuone/__init__.py'
2070--- ubuntuone/__init__.py 2010-12-06 12:27:11 +0000
2071+++ ubuntuone/__init__.py 2011-03-10 03:11:54 +0000
2072@@ -17,5 +17,4 @@
2073 # with this program. If not, see <http://www.gnu.org/licenses/>.
2074
2075 """Ubuntuone package."""
2076-
2077 __import__('pkg_resources').declare_namespace(__name__)
2078
2079=== modified file 'ubuntuone/controlpanel/backend.py'
2080--- ubuntuone/controlpanel/backend.py 2011-02-23 13:57:42 +0000
2081+++ ubuntuone/controlpanel/backend.py 2011-03-10 03:11:54 +0000
2082@@ -195,7 +195,6 @@
2083 devices = yield self.wc.call_api(DEVICES_API)
2084 for d in devices:
2085 di = {}
2086- result.append(di)
2087 di["type"] = d["kind"]
2088 di["name"] = d["description"]
2089 di["configurable"] = ''
2090@@ -225,6 +224,11 @@
2091 # di["available_services"] = ""
2092 # di["enabled_services"] = ""
2093
2094+ if is_local: # prepend the local device!
2095+ result.insert(0, di)
2096+ else:
2097+ result.append(di)
2098+
2099 returnValue(result)
2100
2101 def type_n_id(self, device_id):
2102
2103=== modified file 'ubuntuone/controlpanel/gtk/gui.py'
2104--- ubuntuone/controlpanel/gtk/gui.py 2011-02-28 15:38:15 +0000
2105+++ ubuntuone/controlpanel/gtk/gui.py 2011-03-10 03:11:54 +0000
2106@@ -210,13 +210,10 @@
2107
2108 CREDENTIALS_ERROR = _('There was a problem while retrieving the '
2109 'credentials.')
2110- AUTHORIZATION_DENIED = _('The authentication was cancelled.')
2111 NETWORK_OFFLINE = _('An internet connection is required to join or sign '
2112 'in to %(app_name)s.')
2113- NETWORK_UNKNOWN = _('The internet connection state couldn\'t be '
2114- 'discovered. Maybe NetworkManager is not running?')
2115-
2116 CONNECT = _('Connect to Ubuntu One')
2117+ LEARN_MORE_LINK = 'https://one.ubuntu.com/'
2118
2119 def __init__(self, main_window):
2120 GreyableBin.__init__(self)
2121@@ -299,6 +296,10 @@
2122 self.set_property('greyed', True)
2123 self.warning_label.set_text('')
2124
2125+ def on_learn_more_button_clicked(self, *a, **kw):
2126+ """User wants to learn more."""
2127+ uri_hook(self.learn_more_button, self.LEARN_MORE_LINK)
2128+
2129 @filter_by_app_name
2130 @log_call(logger.info, with_args=False)
2131 def on_credentials_found(self, app_name, credentials):
2132@@ -325,23 +326,18 @@
2133 def on_authorization_denied(self, app_name):
2134 """SSO backend notifies that user refused auth for 'app_name'."""
2135 self.set_property('greyed', False)
2136- self._set_warning(self.AUTHORIZATION_DENIED)
2137
2138 @log_call(logger.info)
2139 def on_network_state_changed(self, state):
2140 """Network state is reported."""
2141- msg = None
2142+ msg = ''
2143 if state is networkstate.OFFLINE:
2144 msg = self.NETWORK_OFFLINE % {'app_name': U1_APP_NAME}
2145- elif state is networkstate.UNKNOWN:
2146- msg = self.NETWORK_UNKNOWN
2147-
2148- if msg is not None:
2149 self.set_sensitive(False)
2150 self._set_warning(msg)
2151 else:
2152 self.set_sensitive(True)
2153- self.warning_label.set_text('')
2154+ self.warning_label.set_text(msg)
2155 self.sso_backend.find_credentials(U1_APP_NAME, {},
2156 reply_handler=NO_OP, error_handler=error_handler)
2157
2158@@ -350,6 +346,8 @@
2159 """The dashboard panel. The user can manage the subscription."""
2160
2161 TITLE = _('Welcome to Ubuntu One!')
2162+ VALUE_ERROR = _('The information could not be retrieved. '
2163+ 'Maybe your internet connection is down?')
2164
2165 def __init__(self, main_window=None):
2166 UbuntuOneBin.__init__(self)
2167@@ -380,26 +378,32 @@
2168 @log_call(logger.error)
2169 def on_account_info_error(self, error_dict=None):
2170 """Backend notifies of an error when fetching account info."""
2171- self.on_error()
2172+ self.on_error(message=self.VALUE_ERROR)
2173 self.is_processing = False
2174
2175
2176 class VolumesPanel(UbuntuOneBin, ControlPanelMixin):
2177 """The volumes panel."""
2178
2179- TITLE = _('Select the folders from your personal cloud that you want '
2180- 'synchronized in this device.')
2181+ TITLE = _('Select which folders from your cloud you want to sync with '
2182+ 'this computer')
2183 MY_FOLDERS = _('My folders')
2184 ALWAYS_SUBSCRIBED = _('Always in sync!')
2185 FREE_SPACE = _('%(free_space)s available storage')
2186 NO_VOLUMES = _('No folders to show.')
2187 NAME_NOT_SET = _('[unknown user name]')
2188+ CONFIRM_MERGE = _('The contents of your cloud folder will be merged with '
2189+ 'your local folder "%(folder_path)s" when subscribing.\n'
2190+ 'Do you want to subscribe to this cloud folder?')
2191+ MUSIC_DISPLAY_NAME = _('Purchased Music')
2192+ MUSIC_REAL_PATH = '~/.ubuntuone/Purchased from Ubuntu One'
2193
2194 MAX_COLS = 8
2195
2196- CONTACT_ICON_NAME = 'system-users'
2197+ CONTACT_ICON_NAME = 'avatar-default'
2198 FOLDER_ICON_NAME = 'folder'
2199 SHARE_ICON_NAME = 'folder-remote'
2200+ MUSIC_ICON_NAME = 'audio-x-generic'
2201 ROW_HEADER = '<span font_size="large"><b>%s</b></span> ' \
2202 '<span foreground="grey">%s</span>'
2203 ROOT = '%s - <span foreground="%s" font_size="small">%s</span>'
2204@@ -410,6 +414,12 @@
2205 self.add(self.itself)
2206 self.show_all()
2207
2208+ kw = dict(parent=main_window,
2209+ flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
2210+ type=gtk.MESSAGE_WARNING,
2211+ buttons=gtk.BUTTONS_YES_NO)
2212+ self.confirm_dialog = gtk.MessageDialog(**kw)
2213+
2214 # name, subscribed, icon name, show toggle, sensitive, icon size,
2215 # id, path
2216 self._empty_row = ('', False, '', False, False, gtk.ICON_SIZE_MENU,
2217@@ -427,7 +437,14 @@
2218 def _process_path(self, path):
2219 """Trim 'path' so the '~' is removed."""
2220 home = os.path.expanduser('~')
2221- return path.replace(os.path.join(home, ''), '')
2222+ music_path = os.path.expanduser(self.MUSIC_REAL_PATH)
2223+
2224+ if path == music_path:
2225+ result = self.MUSIC_DISPLAY_NAME
2226+ else:
2227+ result = path.replace(os.path.join(home, ''), '')
2228+
2229+ return result
2230
2231 def on_volumes_info_ready(self, info):
2232 """Backend notifies of volumes info."""
2233@@ -445,13 +462,10 @@
2234
2235 if name:
2236 name = name + "'s"
2237- icon_name = self.SHARE_ICON_NAME
2238-
2239 # we already added user folders, let's add an empty row
2240 treeiter = self.volumes_store.append(None, self._empty_row)
2241 else:
2242 name = self.MY_FOLDERS
2243- icon_name = self.FOLDER_ICON_NAME
2244
2245 free_bytes_args = {'free_space': self.humanize(int(free_bytes))}
2246 row = (self.ROW_HEADER % (name, self.FREE_SPACE % free_bytes_args),
2247@@ -463,6 +477,7 @@
2248 for volume in volumes:
2249 sensitive = True
2250 name = self._process_path(volume[u'path'])
2251+ icon_name = self.FOLDER_ICON_NAME
2252
2253 is_root = volume[u'type'] == backend.ControlBackend.ROOT_TYPE
2254 is_share = volume[u'type'] == backend.ControlBackend.SHARE_TYPE
2255@@ -472,6 +487,9 @@
2256 name = self.ROOT % (name, ORANGE, self.ALWAYS_SUBSCRIBED)
2257 elif is_share:
2258 name = volume[u'name']
2259+ icon_name = self.SHARE_ICON_NAME
2260+ elif name == self.MUSIC_DISPLAY_NAME:
2261+ icon_name = self.MUSIC_ICON_NAME
2262
2263 row = (name, bool(volume[u'subscribed']), icon_name, True,
2264 sensitive, gtk.ICON_SIZE_MENU, volume['volume_id'],
2265@@ -506,15 +524,24 @@
2266 """The user toggled 'widget'."""
2267 treeiter = self.volumes_store.get_iter(path)
2268 volume_id = self.volumes_store.get_value(treeiter, 6)
2269- subscribed = not self.volumes_store.get_value(treeiter, 1)
2270-
2271- self.volumes_store.set_value(treeiter, 1, subscribed)
2272-
2273- self.backend.change_volume_settings(volume_id,
2274- {'subscribed': bool_str(subscribed)},
2275- reply_handler=NO_OP, error_handler=error_handler)
2276-
2277- self.is_processing = True
2278+ volume_path = self.volumes_store.get_value(treeiter, 7)
2279+ subscribed = self.volumes_store.get_value(treeiter, 1)
2280+
2281+ response = gtk.RESPONSE_YES
2282+ if not subscribed and os.path.exists(volume_path):
2283+ self.confirm_dialog.set_markup(self.CONFIRM_MERGE %
2284+ {'folder_path': volume_path})
2285+ response = self.confirm_dialog.run()
2286+ self.confirm_dialog.hide()
2287+
2288+ if response == gtk.RESPONSE_YES:
2289+ subscribed = not subscribed
2290+ self.volumes_store.set_value(treeiter, 1, subscribed)
2291+ self.backend.change_volume_settings(volume_id,
2292+ {'subscribed': bool_str(subscribed)},
2293+ reply_handler=NO_OP, error_handler=error_handler)
2294+
2295+ self.is_processing = True
2296
2297 def on_volumes_view_row_activated(self, widget, path, *args, **kwargs):
2298 """The user double clicked on a row."""
2299@@ -607,10 +634,14 @@
2300 return inner
2301
2302 on_show_all_notifications_toggled = _change_device_settings
2303- on_limit_bandwidth_toggled = _change_device_settings
2304 on_max_upload_speed_value_changed = _change_device_settings
2305 on_max_download_speed_value_changed = _change_device_settings
2306
2307+ def on_limit_bandwidth_toggled(self, *args, **kwargs):
2308+ """The limit bandwidth checkbox was toggled."""
2309+ self.throttling_limits.set_sensitive(self.limit_bandwidth.get_active())
2310+ self._change_device_settings()
2311+
2312 def on_remove_clicked(self, widget):
2313 """Remove button was clicked or activated."""
2314 response = gtk.RESPONSE_YES
2315@@ -659,14 +690,16 @@
2316
2317 if 'configurable' in kwargs:
2318 self.configurable = bool(kwargs['configurable'])
2319- self.throttling.set_visible(self.configurable)
2320+ self.config_settings.set_visible(self.configurable)
2321
2322 if 'show_all_notifications' in kwargs:
2323 value = bool(kwargs['show_all_notifications'])
2324 self.show_all_notifications.set_active(value)
2325
2326 if 'limit_bandwidth' in kwargs:
2327- self.limit_bandwidth.set_active(bool(kwargs['limit_bandwidth']))
2328+ enabled = bool(kwargs['limit_bandwidth'])
2329+ self.limit_bandwidth.set_active(enabled)
2330+ self.throttling_limits.set_sensitive(enabled)
2331
2332 for speed in ('max_upload_speed', 'max_download_speed'):
2333 if speed in kwargs:
2334@@ -821,14 +854,12 @@
2335 }
2336
2337 INSTALL_PACKAGE = _('You need to install the package <i>%(package_name)s'
2338- '</i> in order to enable replication.')
2339- INSTALLING = _('The package <i>%(package_name)s</i> is being installed, '
2340- 'please wait...')
2341- FAILED_INSTALL = _('The installation of <i>%(package_name)s</i> failed.')
2342- SUCCESS_INSTALL = _('The installation of <i>%(package_name)s</i> '
2343- 'was successful.')
2344+ '</i> in order to enable more sync services.')
2345+ INSTALLING = _('installation of <i>%(package_name)s</i> in progress')
2346+ FAILED_INSTALL = _('<i>%(package_name)s</i> could not be installed')
2347+ SUCCESS_INSTALL = _('<i>%(package_name)s</i> was successfully installed')
2348
2349- def __init__(self, package_name):
2350+ def __init__(self, package_name, message=None):
2351 gtk.VBox.__init__(self)
2352 ControlPanelMixin.__init__(self, filename='install.ui')
2353 self.add(self.itself)
2354@@ -839,10 +870,23 @@
2355 self.transaction = None
2356
2357 self.progress_bar = None
2358- self.install_label.set_markup(self.INSTALL_PACKAGE % self.args)
2359+
2360+ self.message = message
2361+ if self.message is None:
2362+ self.message = self.INSTALL_PACKAGE % self.args
2363+ self.reset()
2364
2365 self.show()
2366
2367+ def reset(self):
2368+ """Reset this interface."""
2369+ children = self.itself.get_children()
2370+ if self.progress_bar in children:
2371+ self.itself.remove(self.progress_bar)
2372+ if self.install_button_box not in children:
2373+ self.itself.pack_start(self.install_button_box)
2374+ self.install_label.set_markup(self.message)
2375+
2376 @package_manager.inline_callbacks
2377 def on_install_button_clicked(self, button):
2378 """The install button was clicked."""
2379@@ -869,10 +913,14 @@
2380 self.transaction.connect('finished', self.on_install_finished)
2381 self.install_label.set_markup(self.INSTALLING % self.args)
2382 yield self.transaction.run()
2383+ except package_manager.aptdaemon.errors.NotAuthorizedError:
2384+ self.reset()
2385 except: # pylint: disable=W0702
2386 logger.exception('on_install_button_clicked')
2387 self._set_warning(self.FAILED_INSTALL % self.args,
2388 self.install_label)
2389+ if self.progress_bar is not None:
2390+ self.progress_bar.hide()
2391
2392 @log_call(logger.info)
2393 def on_install_finished(self, transaction, exit_code):
2394@@ -880,6 +928,8 @@
2395 if self.progress_bar is not None:
2396 self.progress_bar.set_sensitive(False)
2397
2398+ logger.info('on_install_finished: installation of %r was %r',
2399+ self.package_name, exit_code)
2400 if exit_code != package_manager.aptdaemon.enums.EXIT_SUCCESS:
2401 if hasattr(transaction, 'error'):
2402 logger.error('transaction failed: %r', transaction.error)
2403@@ -896,10 +946,15 @@
2404 CHANGE_ERROR = _('The settings could not be changed,\n'
2405 'previous values were restored.')
2406
2407- def __init__(self, service_id, name, *args, **kwargs):
2408+ def __init__(self, service_id, name,
2409+ container=None, check_button=None, action_button=None,
2410+ *args, **kwargs):
2411 gtk.VBox.__init__(self)
2412 ControlPanelMixin.__init__(self)
2413 self.id = service_id
2414+ self.container = container
2415+ self.check_button = check_button
2416+ self.action_button = action_button
2417
2418 self.warning_label = gtk.Label()
2419 self.pack_start(self.warning_label, expand=False)
2420@@ -915,11 +970,14 @@
2421
2422 FILES_SERVICE_NAME = _('File Sync')
2423
2424- def __init__(self):
2425+ def __init__(self, container, check_button, action_button):
2426 Service.__init__(self, service_id='file-sync',
2427- name=self.FILES_SERVICE_NAME)
2428+ name=self.FILES_SERVICE_NAME,
2429+ container=container,
2430+ check_button=check_button,
2431+ action_button=action_button)
2432
2433- self.set_sensitive(False)
2434+ self.container.set_sensitive(False)
2435
2436 self.backend.connect_to_signal('FileSyncStatusChanged',
2437 self.on_file_sync_status_changed)
2438@@ -933,12 +991,15 @@
2439 def on_file_sync_status_changed(self, status):
2440 """File Sync status changed."""
2441 enabled = status != backend.FILE_SYNC_DISABLED
2442- self.button.set_active(enabled)
2443+ logger.info('FileSyncService: enabled? %r', enabled)
2444+ self.check_button.set_active(enabled)
2445+ # if service is disabled, disable the action_button
2446+ self.action_button.set_sensitive(enabled)
2447
2448- if not self.is_sensitive():
2449+ if not self.container.is_sensitive():
2450 # first time we're getting this event
2451- self.button.connect('toggled', self.on_button_toggled)
2452- self.set_sensitive(True)
2453+ self.check_button.connect('toggled', self.on_button_toggled)
2454+ self.container.set_sensitive(True)
2455
2456 def on_files_enabled(self):
2457 """Files service was enabled."""
2458@@ -951,8 +1012,8 @@
2459 @log_call(logger.debug)
2460 def on_button_toggled(self, button):
2461 """Button was toggled, exclude/replicate the service properly."""
2462- logger.info('File Sync enabled? %r', self.button.get_active())
2463- if self.button.get_active():
2464+ logger.info('File Sync enabled? %r', self.check_button.get_active())
2465+ if self.check_button.get_active():
2466 self.backend.enable_files(reply_handler=NO_OP,
2467 error_handler=error_handler)
2468 else:
2469@@ -963,38 +1024,49 @@
2470 class DesktopcouchService(Service):
2471 """A desktopcouch service."""
2472
2473- def __init__(self, service_id, name, enabled, dependency=None):
2474- Service.__init__(self, service_id, name)
2475+ INSTALL_PACKAGE = _('Install <i>%(plugin_name)s</i> plugin '
2476+ 'for %(service_name)s sync')
2477+
2478+ def __init__(self, service_id, name, enabled,
2479+ container, check_button,
2480+ dependency=None, dependency_name=None):
2481+ Service.__init__(self, service_id, name,
2482+ container, check_button, action_button=None)
2483
2484 self.backend.connect_to_signal('ReplicationSettingsChanged',
2485 self.on_replication_settings_changed)
2486 self.backend.connect_to_signal('ReplicationSettingsChangeError',
2487 self.on_replication_settings_change_error)
2488
2489- self.button.set_active(enabled)
2490+ self.check_button.set_active(enabled)
2491
2492 self.dependency = None
2493 if dependency is not None:
2494- self.dependency = InstallPackage(dependency)
2495+ if dependency_name is None:
2496+ dependency_name = dependency
2497+ args = {'plugin_name': dependency_name, 'service_name': service_id}
2498+ message = self.INSTALL_PACKAGE % args
2499+ self.dependency = InstallPackage(dependency, message)
2500 self.dependency.connect('finished', self.on_depedency_finished)
2501- self.pack_start(self.dependency, expand=False)
2502- self.button.set_sensitive(False)
2503-
2504- self.button.connect('toggled', self.on_button_toggled)
2505+
2506+ self.container.pack_end(self.dependency, expand=False)
2507+ self.check_button.set_sensitive(False)
2508+
2509+ self.check_button.connect('toggled', self.on_button_toggled)
2510
2511 def on_depedency_finished(self, widget):
2512 """The dependency was installed."""
2513- self.button.set_sensitive(True)
2514- self.remove(self.dependency)
2515+ self.check_button.set_sensitive(True)
2516+ self.container.remove(self.dependency)
2517 self.dependency = None
2518
2519 @log_call(logger.debug)
2520 def on_button_toggled(self, button):
2521 """Button was toggled, exclude/replicate the service properly."""
2522 logger.info('Starting replication for %r? %r',
2523- self.id, self.button.get_active())
2524+ self.id, self.check_button.get_active())
2525
2526- args = {'enabled': bool_str(self.button.get_active())}
2527+ args = {'enabled': bool_str(self.check_button.get_active())}
2528 self.backend.change_replication_settings(self.id, args,
2529 reply_handler=NO_OP, error_handler=error_handler)
2530
2531@@ -1011,26 +1083,28 @@
2532 """The change of settings for this replication failed."""
2533 if replication_id != self.id:
2534 return
2535- self.button.set_active(not self.button.get_active())
2536+ self.check_button.set_active(not self.check_button.get_active())
2537 self._set_warning(self.CHANGE_ERROR, self.warning_label)
2538
2539
2540 class ServicesPanel(UbuntuOneBin, ControlPanelMixin):
2541 """The services panel."""
2542
2543- TITLE = _('Ubuntu One services including data sync are enabled for the '
2544- 'data types and services listed below.')
2545- CHOOSE_SERVICES = _('Choose services to synchronize with this computer:')
2546+ TITLE = _('Enable the sync services for this computer.')
2547 DESKTOPCOUCH_PKG = 'desktopcouch-ubuntuone'
2548- BOOKMARKS = _('Bookmarks (Firefox)')
2549- CONTACTS = _('Contacts (Evolution)')
2550+ BOOKMARKS = 'Firefox'
2551+ CONTACTS = 'Evolution'
2552 NO_PAIRING_RECORD = _('There is no Ubuntu One pairing record.')
2553+ CONTACTS_LINK = 'https://one.ubuntu.com/'
2554
2555 def __init__(self, main_window=None):
2556 UbuntuOneBin.__init__(self)
2557 ControlPanelMixin.__init__(self, filename='services.ui')
2558 self.add(self.itself)
2559
2560+ self.plugin_names = {'contacts': self.CONTACTS,
2561+ 'bookmarks': self.BOOKMARKS}
2562+
2563 self.package_manager = package_manager.PackageManager()
2564 self.install_box = None
2565
2566@@ -1039,7 +1113,9 @@
2567 self.backend.connect_to_signal('ReplicationsInfoError',
2568 self.on_replications_info_error)
2569
2570- self.files.pack_start(FileSyncService(), expand=False)
2571+ self.file_sync_service = FileSyncService(container=self.files,
2572+ check_button=self.file_sync_check,
2573+ action_button=self.file_sync_button)
2574
2575 self.show()
2576
2577@@ -1048,6 +1124,30 @@
2578 """Is desktopcouch installed?"""
2579 return self.package_manager.is_installed(self.DESKTOPCOUCH_PKG)
2580
2581+ def on_file_sync_button_clicked(self, *args, **kwargs):
2582+ """The "Show me my U1 folder" button was clicked.
2583+
2584+ XXX: this should be part of the FileSyncService widget.
2585+ XXX: the Ubuntu One folder should be the user's root.
2586+
2587+ """
2588+ uri_hook(None, FILE_URI_PREFIX + os.path.expanduser('~/Ubuntu One'))
2589+
2590+ def on_contacts_button_clicked(self, *args, **kwargs):
2591+ """The "Take me to the Ubuntu One website" button was clicked.
2592+
2593+ XXX: this should be part of the DesktopcouchService widget.
2594+
2595+ """
2596+ uri_hook(None, self.CONTACTS)
2597+
2598+ def on_bookmarks_button_clicked(self, *args, **kwargs):
2599+ """The bookmarks button was clicked.
2600+
2601+ XXX: this should be part of the DesktopcouchService widget.
2602+
2603+ """
2604+
2605 @log_call(logger.debug)
2606 def load(self):
2607 """Load info."""
2608@@ -1062,7 +1162,7 @@
2609
2610 self.install_box = InstallPackage(self.DESKTOPCOUCH_PKG)
2611 self.install_box.connect('finished', self.load_replications)
2612- self.itself.pack_start(self.install_box, expand=False)
2613+ self.itself.pack_end(self.install_box, expand=False)
2614 self.itself.reorder_child(self.install_box, 0)
2615 else:
2616 self.load_replications()
2617@@ -1080,7 +1180,7 @@
2618 @log_call(logger.debug)
2619 def on_replications_info_ready(self, info):
2620 """The replication info is ready."""
2621- self.on_success(self.CHOOSE_SERVICES)
2622+ self.on_success()
2623
2624 self.replications.show()
2625
2626@@ -1088,18 +1188,20 @@
2627 self.itself.remove(self.install_box)
2628 self.install_box = None
2629
2630- for child in self.replications.get_children():
2631- self.replications.remove(child)
2632-
2633 for item in info:
2634 pkg = item['dependency']
2635 if not pkg or self.package_manager.is_installed(pkg):
2636 pkg = None
2637- child = DesktopcouchService(service_id=item['replication_id'],
2638- name=item['name'],
2639- enabled=bool(item['enabled']),
2640- dependency=pkg)
2641- self.replications.pack_start(child, expand=False)
2642+
2643+ sid = item['replication_id']
2644+ container = getattr(self, sid, None)
2645+ check_button = getattr(self, '%s_check' % sid, None)
2646+ name = self.plugin_names.get(sid, None)
2647+ child = DesktopcouchService(service_id=sid, name=item['name'],
2648+ enabled=bool(item['enabled']), container=container,
2649+ check_button=check_button,
2650+ dependency=pkg, dependency_name=name)
2651+ setattr(self, '%s_service' % sid, child)
2652
2653 @log_call(logger.error)
2654 def on_replications_info_error(self, error_dict=None):
2655@@ -1143,7 +1245,7 @@
2656 ControlPanelMixin.__init__(self)
2657
2658 self.label = LabelLoading(LOADING)
2659- self.pack_start(self.label, expand=False)
2660+ self.pack_start(self.label, expand=True)
2661
2662 self.button = gtk.LinkButton(uri='')
2663 self.button.connect('clicked', self._on_button_clicked)
2664@@ -1237,7 +1339,11 @@
2665 @log_call(logger.error)
2666 def on_file_sync_status_error(self, error_dict=None):
2667 """Backend notifies of an error when fetching file sync status."""
2668- self._update_status(WARNING_MARKUP % self.FILE_SYNC_ERROR,
2669+ msg = self.FILE_SYNC_ERROR
2670+ reason = error_dict.get('error_msg', '') if error_dict else ''
2671+ if reason:
2672+ msg += ' (' + reason + ')'
2673+ self._update_status(WARNING_MARKUP % msg,
2674 self.RESTART, self.on_restart_clicked,
2675 tooltip=self.RESTART_TOOLTIP)
2676
2677@@ -1291,7 +1397,8 @@
2678 gobject.TYPE_NONE, ()),
2679 }
2680
2681- QUOTA_LABEL = _('Using %(used)s of %(total)s (%(percentage).1f%%)')
2682+ QUOTA_LABEL = _('Using %(used)s of %(total)s (%(percentage).0f%%)')
2683+ QUOTA_THRESHOLD = 0.95
2684 DASHBOARD_BUTTON_NAME = 'Account'
2685 SERVICES_BUTTON_NAME = 'Devices' # Intentional until the theme is fixed
2686
2687@@ -1321,7 +1428,7 @@
2688 self.quota_box.reorder_child(self.quota_label, 0)
2689
2690 self.status_label = FileSyncStatus()
2691- self.status_box.pack_end(self.status_label, expand=False)
2692+ self.status_box.pack_end(self.status_label, expand=True)
2693
2694 self.dashboard = DashboardPanel(main_window=main_window)
2695 self.volumes = VolumesPanel(main_window=main_window)
2696@@ -1352,20 +1459,32 @@
2697 lambda widget: self.emit('local-device-removed'))
2698
2699 self.services_button.set_name(self.SERVICES_BUTTON_NAME)
2700- self.services_button.connect('clicked', lambda b: self.services.load())
2701 self.services_button.set_tooltip_text(self.SERVICES_BUTTON_TOOLTIP)
2702+ self.services.load()
2703
2704 def _update_quota(self, msg, data=None):
2705 """Update the quota info."""
2706- self.quota_label.set_markup(msg)
2707- self.quota_label.stop()
2708-
2709 fraction = 0.0
2710 if data is not None:
2711 fraction = data.get('percentage', 0.0) / 100
2712 if fraction > 0 and fraction < 0.05:
2713 fraction = 0.05
2714- self.quota_progressbar.set_fraction(fraction)
2715+ else:
2716+ fraction = round(fraction, 2)
2717+
2718+ logger.debug('ManagementPanel: updating quota to %r.', fraction)
2719+ if fraction >= self.QUOTA_THRESHOLD:
2720+ self.quota_label.set_markup(WARNING_MARKUP % msg)
2721+ else:
2722+ self.quota_label.set_markup(msg)
2723+ self.quota_label.stop()
2724+
2725+ if fraction == 0.0:
2726+ self.quota_progressbar.set_sensitive(False)
2727+ else:
2728+ self.quota_progressbar.set_sensitive(True)
2729+
2730+ self.quota_progressbar.set_fraction(min(fraction, 1))
2731
2732 def load(self):
2733 """Load the account info and file sync status list."""
2734@@ -1384,7 +1503,7 @@
2735 @log_call(logger.error)
2736 def on_account_info_error(self, error_dict=None):
2737 """Backend notifies of an error when fetching account info."""
2738- self._update_quota(WARNING_MARKUP % VALUE_ERROR)
2739+ self._update_quota(msg='')
2740
2741
2742 class ControlPanel(gtk.Notebook):
2743@@ -1439,7 +1558,7 @@
2744
2745 TITLE = _('%(app_name)s Control Panel')
2746
2747- def __init__(self, switch_to=None):
2748+ def __init__(self, switch_to=None, alert=False):
2749 super(ControlPanelWindow, self).__init__()
2750
2751 self.set_title(self.TITLE % {'app_name': U1_APP_NAME})
2752@@ -1448,7 +1567,13 @@
2753 self.set_size_request(-1, 525) # bug #683164
2754
2755 self.connect('delete-event', lambda w, e: gtk.main_quit())
2756- self.show()
2757+ if alert:
2758+ print "YES"
2759+ # NOTE this should prevent focus stealing but it does not :(
2760+ self.present_with_time(1)
2761+ self.set_urgency_hint(True)
2762+ else:
2763+ self.present()
2764
2765 self.control_panel = ControlPanel(main_window=self)
2766 self.add(self.control_panel)
2767
2768=== modified file 'ubuntuone/controlpanel/gtk/tests/__init__.py'
2769--- ubuntuone/controlpanel/gtk/tests/__init__.py 2011-02-23 13:57:42 +0000
2770+++ ubuntuone/controlpanel/gtk/tests/__init__.py 2011-03-10 03:11:54 +0000
2771@@ -18,11 +18,21 @@
2772
2773 """The test suite for the GTK UI for the control panel for Ubuntu One."""
2774
2775+import logging
2776+
2777 from collections import defaultdict
2778
2779+from ubuntuone.devtools.handlers import MementoHandler
2780+
2781+from ubuntuone.controlpanel.backend import ControlBackend
2782 from ubuntuone.controlpanel.gtk import gui
2783 from ubuntuone.controlpanel.gtk.tests.test_package_manager import (
2784 FakedTransaction)
2785+from ubuntuone.controlpanel.tests import TestCase
2786+
2787+
2788+# Attribute 'yyy' defined outside __init__, access to a protected member
2789+# pylint: disable=W0201, W0212
2790
2791
2792 FAKE_ACCOUNT_INFO = {'type': 'Payed', 'name': 'Test me',
2793@@ -32,26 +42,36 @@
2794
2795 ROOT = {
2796 u'volume_id': '', u'path': '/home/tester/My Ubuntu',
2797- u'subscribed': 'True', u'type': u'ROOT',
2798+ u'subscribed': 'True', u'type': ControlBackend.ROOT_TYPE,
2799+}
2800+
2801+MUSIC_FOLDER = {
2802+ u'volume_id': u'58236', u'subscribed': u'True',
2803+ u'type': ControlBackend.FOLDER_TYPE,
2804+ u'path': u'/home/tester/.ubuntuone/Purchased from Ubuntu One',
2805+ u'suggested_path': u'~/.ubuntuone/Purchased from Ubuntu One',
2806 }
2807
2808 FAKE_FOLDERS_INFO = [
2809 {u'volume_id': u'0', u'path': u'/home/tester/foo',
2810- u'suggested_path': u'~/foo', u'subscribed': u'', u'type': u'UDF'},
2811+ u'suggested_path': u'~/foo', u'subscribed': u'',
2812+ u'type': ControlBackend.FOLDER_TYPE},
2813 {u'volume_id': u'1', u'path': u'/home/tester/bar',
2814- u'suggested_path': u'~/bar', u'subscribed': u'True', u'type': u'UDF'},
2815+ u'suggested_path': u'~/bar', u'subscribed': u'True',
2816+ u'type': ControlBackend.FOLDER_TYPE},
2817 {u'volume_id': u'2', u'path': u'/home/tester/baz',
2818- u'suggested_path': u'~/baz', u'subscribed': u'True', u'type': u'UDF'},
2819+ u'suggested_path': u'~/baz', u'subscribed': u'True',
2820+ u'type': ControlBackend.FOLDER_TYPE},
2821 ]
2822
2823 FAKE_SHARES_INFO = [
2824 {u'volume_id': u'1234', u'name': u'do',
2825 u'path': u'/home/tester/.local/share/ubuntuone/shares/do from Other User',
2826- u'subscribed': u'', u'type': u'SHARE'},
2827+ u'subscribed': u'', u'type': ControlBackend.SHARE_TYPE},
2828
2829 {u'volume_id': u'5678', u'name': u're',
2830 u'path': u'/home/tester/.local/share/ubuntuone/shares/re from Other User',
2831- u'subscribed': u'True', u'type': u'SHARE'},
2832+ u'subscribed': u'True', u'type': ControlBackend.SHARE_TYPE},
2833 ]
2834
2835 FAKE_VOLUMES_INFO = [
2836@@ -207,7 +227,8 @@
2837 self._args = args
2838 self._kwargs = kwargs
2839 self.was_run = False
2840- self.is_visible = True
2841+ self.is_visible = False
2842+ self.markup = kwargs.get('message_format', None)
2843 self.show = lambda: setattr(self, 'is_visible', True)
2844 self.hide = lambda: setattr(self, 'is_visible', False)
2845 self.response_code = None
2846@@ -216,3 +237,67 @@
2847 """Set flag and return 'self.response_code'."""
2848 self.was_run = True
2849 return self.response_code
2850+
2851+ def set_markup(self, msg):
2852+ """Set the markup."""
2853+ self.markup = msg
2854+
2855+
2856+class BaseTestCase(TestCase):
2857+ """Basics for testing."""
2858+
2859+ # self.klass is not callable
2860+ # pylint: disable=E1102
2861+ klass = None
2862+ kwargs = {}
2863+
2864+ def setUp(self):
2865+ super(BaseTestCase, self).setUp()
2866+ self.patch(gui.os.path, 'expanduser',
2867+ lambda path: path.replace('~', USER_HOME))
2868+ self.patch(gui.gtk, 'main', lambda: None)
2869+ self.patch(gui.gtk, 'MessageDialog', FakedConfirmDialog)
2870+ self.patch(gui.dbus, 'SessionBus', FakedSessionBus)
2871+ self.patch(gui.dbus, 'Interface', FakedInterface)
2872+ self.patch(gui.networkstate, 'NetworkManagerState', FakedNMState)
2873+ self.patch(gui.package_manager, 'PackageManager', FakedPackageManager)
2874+
2875+ if self.klass is not None:
2876+ self.ui = self.klass(**self.kwargs)
2877+
2878+ self.memento = MementoHandler()
2879+ self.memento.setLevel(logging.DEBUG)
2880+ gui.logger.addHandler(self.memento)
2881+
2882+ def tearDown(self):
2883+ try:
2884+ self.ui.hide()
2885+ del self.ui
2886+ self.ui = None
2887+ except AttributeError:
2888+ pass
2889+ super(BaseTestCase, self).tearDown()
2890+
2891+ def assert_image_equal(self, image, filename):
2892+ """Check that expected and actual represent the same image."""
2893+ pb = gui.gtk.gdk.pixbuf_new_from_file(gui.get_data_file(filename))
2894+ self.assertEqual(image.get_pixbuf().get_pixels(), pb.get_pixels())
2895+
2896+ def assert_backend_called(self, method_name, args, backend=None):
2897+ """Check that the control panel backend 'method_name' was called."""
2898+ if backend is None:
2899+ backend = self.ui.backend
2900+ self.assertIn(method_name, backend._called)
2901+ kwargs = {'reply_handler': gui.NO_OP,
2902+ 'error_handler': gui.error_handler}
2903+ self.assertEqual(backend._called[method_name], (args, kwargs))
2904+
2905+ def assert_warning_correct(self, warning, text):
2906+ """Check that 'warning' is visible, showing 'text'."""
2907+ self.assertTrue(warning.get_visible(), 'Must be visible.')
2908+ self.assertEqual(warning.get_label(), gui.WARNING_MARKUP % text)
2909+
2910+ def assert_function_decorated(self, decorator, func):
2911+ """Check that 'func' is decorated with 'decorator'."""
2912+ expected = decorator(lambda: None)
2913+ self.assertEqual(expected.func_code, func.im_func.func_code)
2914
2915=== modified file 'ubuntuone/controlpanel/gtk/tests/test_gui.py'
2916--- ubuntuone/controlpanel/gtk/tests/test_gui.py 2011-02-28 15:38:15 +0000
2917+++ ubuntuone/controlpanel/gtk/tests/test_gui.py 2011-03-10 03:11:54 +0000
2918@@ -20,645 +20,25 @@
2919
2920 from __future__ import division
2921
2922-import logging
2923-
2924-from ubuntuone.devtools.handlers import MementoHandler
2925-
2926 from ubuntuone.controlpanel.gtk import gui
2927 from ubuntuone.controlpanel.gtk.tests import (FAKE_ACCOUNT_INFO,
2928- FAKE_DEVICE_INFO, FAKE_DEVICES_INFO,
2929- FAKE_VOLUMES_INFO, FAKE_REPLICATIONS_INFO, ROOT, USER_HOME,
2930- FakedNMState, FakedSSOBackend, FakedSessionBus, FakedInterface,
2931- FakedPackageManager, FakedConfirmDialog,
2932-)
2933-from ubuntuone.controlpanel.tests import TOKEN, TestCase
2934+ FAKE_DEVICE_INFO, FAKE_DEVICES_INFO, FAKE_FOLDERS_INFO,
2935+ FAKE_VOLUMES_INFO, FAKE_REPLICATIONS_INFO,
2936+ MUSIC_FOLDER, ROOT, USER_HOME,
2937+ FakedConfirmDialog,
2938+)
2939+from ubuntuone.controlpanel.gtk.tests.test_gui_basic import (
2940+ ControlPanelMixinTestCase,
2941+)
2942 from ubuntuone.controlpanel.gtk.tests.test_package_manager import (
2943 SUCCESS, FAILURE)
2944
2945
2946 # Attribute 'yyy' defined outside __init__, access to a protected member
2947 # pylint: disable=W0201, W0212
2948-# Too many lines in module
2949-# pylint: disable=C0302
2950-
2951-
2952-class BaseTestCase(TestCase):
2953- """Basics for testing."""
2954-
2955- # self.klass is not callable
2956- # pylint: disable=E1102
2957- klass = None
2958- kwargs = {}
2959-
2960- def setUp(self):
2961- super(BaseTestCase, self).setUp()
2962- self.patch(gui.os.path, 'expanduser',
2963- lambda path: path.replace('~', USER_HOME))
2964- self.patch(gui.gtk, 'main', lambda: None)
2965- self.patch(gui.gtk, 'MessageDialog', FakedConfirmDialog)
2966- self.patch(gui.dbus, 'SessionBus', FakedSessionBus)
2967- self.patch(gui.dbus, 'Interface', FakedInterface)
2968- self.patch(gui.networkstate, 'NetworkManagerState', FakedNMState)
2969- self.patch(gui.package_manager, 'PackageManager', FakedPackageManager)
2970-
2971- if self.klass is not None:
2972- self.ui = self.klass(**self.kwargs)
2973-
2974- self.memento = MementoHandler()
2975- self.memento.setLevel(logging.DEBUG)
2976- gui.logger.addHandler(self.memento)
2977-
2978- def tearDown(self):
2979- try:
2980- self.ui.hide()
2981- del self.ui
2982- self.ui = None
2983- except AttributeError:
2984- pass
2985- super(BaseTestCase, self).tearDown()
2986-
2987- def assert_image_equal(self, image, filename):
2988- """Check that expected and actual represent the same image."""
2989- pb = gui.gtk.gdk.pixbuf_new_from_file(gui.get_data_file(filename))
2990- self.assertEqual(image.get_pixbuf().get_pixels(), pb.get_pixels())
2991-
2992- def assert_backend_called(self, method_name, args, backend=None):
2993- """Check that the control panel backend 'method_name' was called."""
2994- if backend is None:
2995- backend = self.ui.backend
2996- self.assertIn(method_name, backend._called)
2997- kwargs = {'reply_handler': gui.NO_OP,
2998- 'error_handler': gui.error_handler}
2999- self.assertEqual(backend._called[method_name], (args, kwargs))
3000-
3001- def assert_warning_correct(self, warning, text):
3002- """Check that 'warning' is visible, showing 'text'."""
3003- self.assertTrue(warning.get_visible(), 'Must be visible.')
3004- self.assertEqual(warning.get_label(), gui.WARNING_MARKUP % text)
3005-
3006- def assert_function_decorated(self, decorator, func):
3007- """Check that 'func' is decorated with 'decorator'."""
3008- expected = decorator(lambda: None)
3009- self.assertEqual(expected.func_code, func.im_func.func_code)
3010-
3011-
3012-class ControlPanelMixinTestCase(BaseTestCase):
3013- """The test suite for the control panel widget."""
3014-
3015- klass = gui.ControlPanelMixin
3016- ui_filename = None
3017-
3018- def test_is_a_control_panel_mixin(self):
3019- """Inherits from ControlPanelMixin."""
3020- self.assertIsInstance(self.ui, gui.ControlPanelMixin)
3021-
3022- def test_ui_can_be_created(self):
3023- """UI main class exists and can be created."""
3024- self.assertTrue(self.ui is not None)
3025-
3026-
3027-class ControlPanelWindowTestCase(BaseTestCase):
3028- """The test suite for the control panel window."""
3029-
3030- klass = gui.ControlPanelWindow
3031-
3032- def test_is_a_window(self):
3033- """Inherits from gtk.Window."""
3034- self.assertIsInstance(self.ui, gui.gtk.Window)
3035-
3036- def test_startup_visibility(self):
3037- """The widget is visible at startup."""
3038- self.assertTrue(self.ui.get_visible(), 'must be visible at startup.')
3039-
3040- def test_main_start_gtk_main_loop(self):
3041- """The GTK main loop is started when calling main()."""
3042- self.patch(gui.gtk, 'main', self._set_called)
3043- self.ui.main()
3044- self.assertEqual(self._called, ((), {}), 'gtk.main was called.')
3045-
3046- def test_closing_stops_the_main_lopp(self):
3047- """The GTK main loop is stopped when closing the window."""
3048- self.patch(gui.gtk, 'main_quit', self._set_called)
3049- self.ui.emit('delete-event', None)
3050- self.assertEqual(self._called, ((), {}), 'gtk.main_quit was called.')
3051-
3052- def test_title_is_correct(self):
3053- """The window title is correct."""
3054- expected = self.ui.TITLE % {'app_name': gui.U1_APP_NAME}
3055- self.assertEqual(self.ui.get_title(), expected)
3056-
3057- def test_control_panel_is_the_only_child(self):
3058- """The control panel is the window's content."""
3059- children = self.ui.get_children()
3060- self.assertEqual(1, len(children))
3061-
3062- control_panel = self.ui.get_children()[0]
3063- self.assertTrue(control_panel is self.ui.control_panel)
3064- self.assertIsInstance(self.ui.control_panel, gui.ControlPanel)
3065- self.assertTrue(self.ui.control_panel.get_visible())
3066-
3067- def test_main_window_is_passed_to_child(self):
3068- """The child gets the main_window."""
3069- self.assertEqual(self.ui.control_panel.main_window, self.ui)
3070-
3071- def test_icon_name_is_correct(self):
3072- """The icon name is correct."""
3073- self.assertEqual(self.ui.get_icon_name(), 'ubuntuone')
3074-
3075- def test_max_size(self):
3076- """Max size is not bigger than 736x525 (LP: #645526, LP: #683164)."""
3077- self.assertTrue(self.ui.get_size_request() <= (736, 525))
3078-
3079-
3080-class ControlPanelWindowParamsTestCase(ControlPanelWindowTestCase):
3081- """The test suite for the control panel window when passing params."""
3082-
3083- kwargs = {'switch_to': 'devices'}
3084-
3085- def test_switch_to(self):
3086- """Can pass a 'switch_to' parameter to start on a particular tab."""
3087- actual = self.ui.control_panel.management.notebook.get_current_page()
3088- self.assertEqual(actual, self.ui.control_panel.management.DEVICES_PAGE)
3089-
3090-
3091-class ControlPanelWindowParamsNoneTestCase(ControlPanelWindowTestCase):
3092- """The suite for the control panel window when passing None params."""
3093-
3094- kwargs = {'switch_to': None}
3095-
3096- def test_switch_to(self):
3097- """Can pass a 'switch_to' being None. Should default to dashboard."""
3098- actual = self.ui.control_panel.management.notebook.get_current_page()
3099- expected = self.ui.control_panel.management.DASHBOARD_PAGE
3100- self.assertEqual(actual, expected)
3101-
3102-
3103-class ControlPanelWindowInvalidParamsTestCase(ControlPanelWindowTestCase):
3104- """The suite for the control panel window when passing invalid params."""
3105-
3106- kwargs = {'switch_to': 'yadda-yadda'}
3107-
3108- def test_switch_to(self):
3109- """Can pass an invalid 'switch_to'. Should default to dashboard."""
3110- actual = self.ui.control_panel.management.notebook.get_current_page()
3111- expected = self.ui.control_panel.management.DASHBOARD_PAGE
3112- self.assertEqual(actual, expected)
3113-
3114-
3115-class ControlPanelTestCase(BaseTestCase):
3116- """The test suite for the control panel itself."""
3117-
3118- klass = gui.ControlPanel
3119- kwargs = {'main_window': object()}
3120-
3121- def assert_current_tab_correct(self, expected_tab):
3122- """Check that the wiget 'expected_tab' is the current page."""
3123- actual = self.ui.get_nth_page(self.ui.get_current_page())
3124- self.assertTrue(expected_tab is actual)
3125-
3126- def test_is_a_notebook(self):
3127- """Inherits from gtk.VBox."""
3128- self.assertIsInstance(self.ui, gui.gtk.Notebook)
3129-
3130- def test_startup_visibility(self):
3131- """The widget is visible at startup."""
3132- self.assertTrue(self.ui.get_visible(),
3133- 'must be visible at startup.')
3134-
3135- def test_startup_props(self):
3136- """The tabs and border are not shown."""
3137- self.assertFalse(self.ui.get_show_border(), 'must not show border.')
3138- self.assertFalse(self.ui.get_show_tabs(), 'must not show tabs.')
3139-
3140- def test_overview_is_shown_at_startup(self):
3141- """The overview is shown at startup."""
3142- self.assertIsInstance(self.ui.overview, gui.OverviewPanel)
3143- self.assert_current_tab_correct(self.ui.overview)
3144-
3145- def test_main_window_is_passed_to_child(self):
3146- """The child gets the main_window."""
3147- self.assertEqual(self.ui.overview.main_window,
3148- self.kwargs['main_window'])
3149-
3150- def test_on_show_management_panel(self):
3151- """A ManagementPanel is shown when the callback is executed."""
3152- self.ui.on_show_management_panel()
3153- self.assert_current_tab_correct(self.ui.management)
3154-
3155- def test_on_show_management_panel_is_idempotent(self):
3156- """Only one ManagementPanel is shown."""
3157- self.ui.on_show_management_panel()
3158- self.ui.on_show_management_panel()
3159-
3160- self.assert_current_tab_correct(self.ui.management)
3161-
3162- def test_credentials_found_shows_dashboard_management_panel(self):
3163- """On 'credentials-found' signal, the management panel is shown.
3164-
3165- If first signal parameter is False, visible tab should be dashboard.
3166-
3167- """
3168- self.patch(self.ui.management, 'load', self._set_called)
3169- self.ui.overview.emit('credentials-found', False, object())
3170-
3171- self.assert_current_tab_correct(self.ui.management)
3172- self.assertEqual(self.ui.management.notebook.get_current_page(),
3173- self.ui.management.DASHBOARD_PAGE)
3174- self.assertEqual(self._called, ((), {}))
3175-
3176- def test_credentials_found_shows_volumes_management_panel(self):
3177- """On 'credentials-found' signal, the management panel is shown.
3178-
3179- If first signal parameter is True, visible tab should be volumes.
3180-
3181- """
3182- a_token = object()
3183- self.ui.overview.emit('credentials-found', True, a_token)
3184-
3185- self.assert_current_tab_correct(self.ui.management)
3186- self.assertEqual(self.ui.management.notebook.get_current_page(),
3187- self.ui.management.VOLUMES_PAGE)
3188-
3189- def test_local_device_removed_shows_overview_panel(self):
3190- """On 'local-device-removed' signal, the overview panel is shown."""
3191- self.ui.overview.emit('credentials-found', True, object())
3192- self.ui.management.emit('local-device-removed')
3193-
3194- self.assert_current_tab_correct(self.ui.overview)
3195-
3196-
3197-class UbuntuOneBinTestCase(BaseTestCase):
3198- """The test suite for a Ubuntu One panel."""
3199-
3200- klass = gui.UbuntuOneBin
3201- kwargs = {'title': 'Something old, something new and something blue.'}
3202-
3203- def test_is_a_vbox(self):
3204- """Inherits from proper gtk widget."""
3205- self.assertIsInstance(self.ui, gui.gtk.VBox)
3206-
3207- def test_startup_visibility(self):
3208- """The widget is visible at startup."""
3209- self.assertTrue(self.ui.get_visible(),
3210- 'must be visible at startup.')
3211- for child in self.ui.get_children():
3212- self.assertTrue(child.get_visible())
3213-
3214- def test_title_is_a_panel_title(self):
3215- """Title is the correct widget."""
3216- self.assertIsInstance(self.ui.title, gui.PanelTitle)
3217- self.assertIn(self.ui.title, self.ui.get_children())
3218-
3219- def test_title_markup_is_correct(self):
3220- """The title markup is correctly set when passed as argument."""
3221- self.assertEqual(self.ui.title.label.get_text(), self.kwargs['title'])
3222-
3223- def test_title_is_correct(self):
3224- """The title markup is correctly set when defined at class level."""
3225- ui = self.klass() # no title given
3226- self.assertEqual(ui.title.label.get_text(), '')
3227-
3228- def test_message_is_a_label_loading(self):
3229- """Message is the correct widget."""
3230- self.assertIsInstance(self.ui.message, gui.LabelLoading)
3231- self.assertIn(self.ui.message, self.ui.get_children())
3232-
3233- def test_on_success(self):
3234- """Callback to stop the Loading and clear messages."""
3235- self.ui.on_success()
3236- self.assertEqual(self.ui.message.get_label(), '')
3237- self.assertFalse(self.ui.message.active)
3238-
3239- def test_on_success_with_message(self):
3240- """Callback to stop the Loading and show a info message."""
3241- msg = 'WOW! <i>this rocks</i>'
3242- self.ui.on_success(message=msg)
3243- self.assertEqual(self.ui.message.get_label(), msg)
3244- self.assertFalse(self.ui.message.active)
3245-
3246- def test_on_error(self):
3247- """Callback to stop the Loading and clear messages."""
3248- self.ui.on_error()
3249- self.assert_warning_correct(self.ui.message, gui.VALUE_ERROR)
3250- self.assertFalse(self.ui.message.active)
3251-
3252- def test_on_error_with_message(self):
3253- """Callback to stop the Loading and show a info message."""
3254- msg = 'WOW! <i>this does not rock</i> :-/'
3255- self.ui.on_error(message=msg)
3256- self.assert_warning_correct(self.ui.message, msg)
3257- self.assertFalse(self.ui.message.active)
3258-
3259- def test_is_processing(self):
3260- """The flag 'is_processing' is False on start."""
3261- self.assertFalse(self.ui.is_processing)
3262- self.assertTrue(self.ui.is_sensitive())
3263-
3264- def test_set_is_processing(self):
3265- """When setting 'is_processing', the spinner is shown."""
3266- self.ui.is_processing = False
3267- self.ui.is_processing = True
3268-
3269- self.assertTrue(self.ui.message.get_visible())
3270- self.assertTrue(self.ui.message.active)
3271- self.assertFalse(self.ui.is_sensitive())
3272-
3273- def test_unset_is_processing(self):
3274- """When unsetting 'is_processing', the spinner is not shown."""
3275- self.ui.is_processing = True
3276- self.ui.is_processing = False
3277-
3278- self.assertTrue(self.ui.message.get_visible())
3279- self.assertFalse(self.ui.message.active)
3280- self.assertTrue(self.ui.is_sensitive())
3281-
3282-
3283-class OverwiewPanelTestCase(ControlPanelMixinTestCase):
3284- """The test suite for the overview panel."""
3285-
3286- klass = gui.OverviewPanel
3287- kwargs = {'main_window': gui.gtk.Window()}
3288- ui_filename = 'overview.ui'
3289-
3290- def test_is_a_greyable_bin(self):
3291- """Inherits from GreyableBin."""
3292- self.assertIsInstance(self.ui, gui.GreyableBin)
3293-
3294- def test_inner_widget_is_packed(self):
3295- """The 'itself' vbox is packed into the widget."""
3296- self.assertIn(self.ui.itself, self.ui.get_children())
3297-
3298- def test_join_now_is_default(self):
3299- """The 'join_now' button is the default widget."""
3300- self.assertTrue(self.ui.join_now_button.get_property('can-default'))
3301-
3302- def test_sso_backend(self):
3303- """Has a correct SSO backend."""
3304- self.assertIsInstance(self.ui.sso_backend, FakedSSOBackend)
3305-
3306- def test_sso_backend_signals(self):
3307- """The proper signals are connected to the backend."""
3308- self.assertEqual(self.ui.sso_backend._signals['CredentialsFound'],
3309- [self.ui.on_credentials_found])
3310- self.assertEqual(self.ui.sso_backend._signals['CredentialsNotFound'],
3311- [self.ui.on_credentials_not_found])
3312- self.assertEqual(self.ui.sso_backend._signals['CredentialsError'],
3313- [self.ui.on_credentials_error])
3314- self.assertEqual(self.ui.sso_backend._signals['AuthorizationDenied'],
3315- [self.ui.on_authorization_denied])
3316-
3317-
3318-class OverwiewNetworkStatePanelTestCase(OverwiewPanelTestCase):
3319- """The test suite for the overview panel regarding network state."""
3320-
3321- def test_network_state_is_created(self):
3322- """The network state is created."""
3323- self.assertIsInstance(self.ui.network_manager_state,
3324- gui.networkstate.NetworkManagerState)
3325- self.assertEqual(self.ui.network_manager_state._kwargs['result_cb'],
3326- self.ui.on_network_state_changed)
3327-
3328- def test_network_state_is_queried_at_startup(self):
3329- """The network state is asked to the NetworkManagerState."""
3330- self.assertTrue('find_online_state' in
3331- self.ui.network_manager_state._called)
3332-
3333- def test_state_online(self):
3334- """Network connection is online."""
3335- self.ui.on_network_state_changed(gui.networkstate.ONLINE)
3336- # all green, no warning
3337- self.assertEqual(self.ui.warning_label.get_text(), '')
3338- self.assertTrue(self.ui.get_sensitive())
3339-
3340- def test_state_offline(self):
3341- """Network connection is offline."""
3342- self.ui.on_network_state_changed(gui.networkstate.OFFLINE)
3343- msg = self.ui.NETWORK_OFFLINE % {'app_name': gui.U1_APP_NAME}
3344-
3345- self.assert_warning_correct(self.ui.warning_label, msg)
3346- self.assertFalse(self.ui.get_sensitive())
3347-
3348- def test_state_unknown(self):
3349- """Network connection is unknown."""
3350- self.ui.on_network_state_changed(gui.networkstate.UNKNOWN)
3351-
3352- self.assert_warning_correct(self.ui.warning_label,
3353- self.ui.NETWORK_UNKNOWN)
3354- self.assertFalse(self.ui.get_sensitive())
3355-
3356-
3357-class OverwiewPanelOnlineTestCase(OverwiewPanelTestCase):
3358- """The test suite for the overview panel."""
3359-
3360- def setUp(self):
3361- super(OverwiewPanelOnlineTestCase, self).setUp()
3362- self.ui.on_network_state_changed(gui.networkstate.ONLINE)
3363-
3364- def test_find_credentials_is_called(self):
3365- """Credentials are asked to SSO backend."""
3366- self.assertFalse(self.ui._credentials_are_new)
3367- self.assert_backend_called('find_credentials', (gui.U1_APP_NAME, {}),
3368- backend=self.ui.sso_backend)
3369-
3370- def test_on_credentials_found(self):
3371- """Callback 'on_credentials_found' is correct."""
3372- self.ui.connect('credentials-found', self._set_called)
3373-
3374- self.ui.on_credentials_found(gui.U1_APP_NAME, TOKEN)
3375-
3376- self.assertFalse(self.ui.get_property('greyed'), 'Must not be greyed.')
3377- # assume credentials were in local keyring
3378- self.assertEqual(self._called, ((self.ui, False, TOKEN), {}))
3379-
3380- def test_on_credentials_found_when_creds_are_not_new(self):
3381- """Callback 'on_credentials_found' distinguish if creds are new."""
3382- self.ui.connect('credentials-found', self._set_called)
3383-
3384- # credentials weren't in the system
3385- self.ui.on_credentials_not_found(gui.U1_APP_NAME)
3386- # now they are!
3387- self.ui.on_credentials_found(gui.U1_APP_NAME, TOKEN)
3388-
3389- self.assertFalse(self.ui.get_property('greyed'), 'Must not be greyed.')
3390- # assume credentials were not in local keyring
3391- self.assertEqual(self._called, ((self.ui, True, TOKEN), {}))
3392-
3393- def test_on_credentials_not_found(self):
3394- """Callback 'on_credentials_not_found' is correct."""
3395- self.ui.on_credentials_not_found(gui.U1_APP_NAME)
3396- self.assertTrue(self.ui.get_visible())
3397- self.assertTrue(self.ui._credentials_are_new)
3398-
3399- def test_on_credentials_error(self):
3400- """Callback 'on_credentials_error' is correct."""
3401- self.ui.on_credentials_error(gui.U1_APP_NAME, {})
3402- self.assertTrue(self.ui.get_visible())
3403- self.assert_warning_correct(self.ui.warning_label,
3404- self.ui.CREDENTIALS_ERROR)
3405-
3406- def test_on_authorization_denied(self):
3407- """Callback 'on_authorization_denied' is correct."""
3408- self.ui.on_authorization_denied(gui.U1_APP_NAME)
3409- self.assertTrue(self.ui.get_visible())
3410- self.assert_warning_correct(self.ui.warning_label,
3411- self.ui.AUTHORIZATION_DENIED)
3412-
3413-
3414-class OverwiewPanelAppNameMismatchTestCase(OverwiewPanelTestCase):
3415- """The test suite for the overview panel when the app_name won't match."""
3416-
3417- NOT_U1_APP = 'Not ' + gui.U1_APP_NAME
3418-
3419- def test_filter_by_app_name(self):
3420- """The filter_by_app_name decorator is correct."""
3421- f = gui.filter_by_app_name(self._set_called)
3422- f(self.ui, self.NOT_U1_APP)
3423- self.assertFalse(self._called)
3424- self.assertTrue(self.memento.check_info('ignoring', self.NOT_U1_APP))
3425-
3426- args = ('test', object())
3427- kwargs = {'really': 'AWESOME'}
3428- f(self.ui, gui.U1_APP_NAME, *args, **kwargs)
3429- self.assertEqual(self._called,
3430- ((self.ui, gui.U1_APP_NAME,) + args, kwargs))
3431-
3432- def test_on_credentials_found(self):
3433- """Callback 'on_credentials_found' is not executed."""
3434- self.assert_function_decorated(gui.filter_by_app_name,
3435- self.ui.on_credentials_found)
3436-
3437- def test_on_credentials_not_found(self):
3438- """Callback 'on_credentials_not_found' is not executed."""
3439- self.assert_function_decorated(gui.filter_by_app_name,
3440- self.ui.on_credentials_not_found)
3441-
3442- def test_on_credentials_error(self):
3443- """Callback 'on_credentials_error' is not executed."""
3444- self.assert_function_decorated(gui.filter_by_app_name,
3445- self.ui.on_credentials_error)
3446-
3447- def test_on_authorization_denied(self):
3448- """Callback 'on_authorization_denied' is not executed."""
3449- self.assert_function_decorated(gui.filter_by_app_name,
3450- self.ui.on_authorization_denied)
3451-
3452-
3453-class OverwiewPanelNoCredsTestCase(OverwiewPanelTestCase):
3454- """The test suite for the overview panel when no credentials are found."""
3455-
3456- def setUp(self):
3457- super(OverwiewPanelNoCredsTestCase, self).setUp()
3458- self.ui.on_credentials_not_found(gui.U1_APP_NAME)
3459-
3460- def test_startup_visibility(self):
3461- """The widget is visible at startup."""
3462- self.assertTrue(self.ui.get_visible(),
3463- 'must be visible at startup if credentials not found.')
3464-
3465- def test_warning_label_is_hidden(self):
3466- """The warning label is not shown by default."""
3467- self.assertEqual(self.ui.warning_label.get_text(), '')
3468-
3469- def test_image_is_correct(self):
3470- """There is an image attribute and is correct."""
3471- self.assert_image_equal(self.ui.image, 'overview.png')
3472-
3473- def test_join_now_is_default_widget(self):
3474- """The join now button is the default widget."""
3475- self.assertTrue(self.ui.join_now_button.get_property('can_default'))
3476-
3477- def test_join_now_button_clicked(self):
3478- """Test the 'join now' button callback."""
3479- self.kwargs['main_window'].show() # ensure parent window is realized
3480- self.addCleanup(self.kwargs['main_window'].hide)
3481-
3482- self.ui.join_now_button.clicked()
3483-
3484- window_id = self.kwargs['main_window'].window.xid
3485- args = (gui.U1_APP_NAME,
3486- {gui.TC_URL_KEY: gui.U1_TC_URL,
3487- gui.HELP_TEXT_KEY: gui.U1_DESCRIPTION,
3488- gui.WINDOW_ID_KEY: str(window_id),
3489- gui.PING_URL_KEY: gui.U1_PING_URL})
3490- self.assert_backend_called('register', args,
3491- backend=self.ui.sso_backend)
3492-
3493- def test_connect_button_clicked(self):
3494- """Test the 'join now' button callback."""
3495- self.kwargs['main_window'].show() # ensure parent window is realized
3496- self.addCleanup(self.kwargs['main_window'].hide)
3497-
3498- self.ui.connect_button.clicked()
3499-
3500- window_id = self.kwargs['main_window'].window.xid
3501- args = (gui.U1_APP_NAME,
3502- {gui.TC_URL_KEY: gui.U1_TC_URL,
3503- gui.HELP_TEXT_KEY: gui.U1_DESCRIPTION,
3504- gui.WINDOW_ID_KEY: str(window_id),
3505- gui.PING_URL_KEY: gui.U1_PING_URL})
3506- self.assert_backend_called('login', args,
3507- backend=self.ui.sso_backend)
3508-
3509- def test_join_now_button_clicked_set_greyed(self):
3510- """Clicking on 'join_now' self is greyed."""
3511- self.ui.join_now_button.clicked()
3512- self.assertTrue(self.ui.get_property('greyed'), 'Must be greyed.')
3513-
3514- def test_join_now_button_clicked_removes_warning(self):
3515- """Clicking on 'join_now' the warnings are removed."""
3516- self.ui.on_authorization_denied(gui.U1_APP_NAME) # show warning
3517- self.ui.join_now_button.clicked()
3518-
3519- self.assertEqual(self.ui.warning_label.get_text(), '')
3520-
3521- def test_connect_button_clicked_set_greyed(self):
3522- """Clicking on 'connect' self is greyed."""
3523- self.ui.connect_button.clicked()
3524- self.assertTrue(self.ui.get_property('greyed'), 'Must be greyed.')
3525-
3526- def test_connect_button_clicked_removes_warning(self):
3527- """Clicking on 'connect' the warnings are removed."""
3528- self.ui.on_authorization_denied(gui.U1_APP_NAME) # show warning
3529- self.ui.connect_button.clicked()
3530-
3531- self.assertEqual(self.ui.warning_label.get_text(), '')
3532-
3533- def test_on_credentials_not_found_unset_greyed(self):
3534- """Callback 'on_credentials_not_found' unsets the 'greyed' prop."""
3535- self.ui.connect_button.clicked()
3536- self.ui.on_credentials_not_found(gui.U1_APP_NAME)
3537-
3538- self.assertFalse(self.ui.get_property('greyed'), 'Must not be greyed.')
3539-
3540- def test_on_credentials_error_unset_greyed(self):
3541- """Callback 'on_credentials_error' unsets the 'greyed' prop."""
3542- self.ui.connect_button.clicked()
3543- self.ui.on_credentials_error(gui.U1_APP_NAME, {})
3544-
3545- self.assertFalse(self.ui.get_property('greyed'), 'Must not be greyed.')
3546-
3547- def test_on_authorization_denied_unset_greyed(self):
3548- """Callback 'on_authorization_denied' unsets the 'greyed' prop."""
3549- self.ui.connect_button.clicked()
3550- self.ui.on_authorization_denied(gui.U1_APP_NAME)
3551-
3552- self.assertFalse(self.ui.get_property('greyed'), 'Must not be greyed.')
3553-
3554- def test_buttons_disabled_when_greyed(self):
3555- """Buttons should be disabled when widget is greyed."""
3556- self.ui.set_sensitive(True)
3557- self.ui.set_property('greyed', True)
3558-
3559- self.assertFalse(self.ui.join_now_button.is_sensitive())
3560- self.assertFalse(self.ui.connect_button.is_sensitive())
3561-
3562- def test_buttons_enabled_when_not_greyed(self):
3563- """Buttons should be enabled when widget is not greyed."""
3564- self.ui.set_sensitive(False)
3565- self.ui.set_property('greyed', False)
3566-
3567- self.assertTrue(self.ui.join_now_button.is_sensitive())
3568- self.assertTrue(self.ui.connect_button.is_sensitive())
3569+
3570+# Unused variable 'skip'
3571+#pylint: disable=W0612
3572
3573
3574 class DashboardTestCase(ControlPanelMixinTestCase):
3575@@ -727,7 +107,7 @@
3576 self.ui.on_account_info_error()
3577
3578 self.assertFalse(self.ui.account.get_visible())
3579- self.assert_warning_correct(self.ui.message, gui.VALUE_ERROR)
3580+ self.assert_warning_correct(self.ui.message, self.ui.VALUE_ERROR)
3581
3582
3583 class VolumesTestCase(ControlPanelMixinTestCase):
3584@@ -896,10 +276,61 @@
3585 self.test_on_volumes_info_error()
3586 self.test_on_volumes_info_ready_with_no_volumes()
3587
3588+ def test_clicking_on_row_opens_folder(self):
3589+ """The folder activated is opened."""
3590+ self.patch(gui, 'uri_hook', self._set_called)
3591+ self.ui.on_volumes_info_ready(FAKE_VOLUMES_INFO)
3592+
3593+ self.ui.volumes_view.row_activated('0:0',
3594+ self.ui.volumes_view.get_column(0))
3595+
3596+ self.assertEqual(self._called,
3597+ ((None, gui.FILE_URI_PREFIX + ROOT['path']), {}))
3598+
3599+ def test_on_volumes_info_ready_with_music_folder(self):
3600+ """The volumes info is processed when ready."""
3601+ info = [(u'', u'147852369', [ROOT] + [MUSIC_FOLDER])]
3602+
3603+ self.ui.on_volumes_info_ready(info)
3604+
3605+ treeiter = self.ui.volumes_store.get_iter_root()
3606+ row = self.ui.volumes_store.get(treeiter, *xrange(self.ui.MAX_COLS))
3607+
3608+ # walk 'Mine' folders children
3609+ treeiter = self.ui.volumes_store.iter_children(treeiter)
3610+
3611+ # grab next row since first one is root
3612+ treeiter = self.ui.volumes_store.iter_next(treeiter)
3613+ row = self.ui.volumes_store.get(treeiter, *xrange(self.ui.MAX_COLS))
3614+
3615+ volume = MUSIC_FOLDER
3616+ expected_path = volume['path'].replace(USER_HOME, '~')
3617+ expected_path = expected_path.replace(self.ui.MUSIC_REAL_PATH,
3618+ self.ui.MUSIC_DISPLAY_NAME)
3619+ self.assertEqual(row[0], expected_path)
3620+ self.assertEqual(row[1], bool(volume['subscribed']))
3621+ self.assertEqual(row[2], self.ui.MUSIC_ICON_NAME)
3622+ self.assertTrue(row[3], 'toggle should be shown on child!')
3623+ self.assertTrue(row[4], 'toggle should be sensitive')
3624+ self.assertEqual(row[5], gui.gtk.ICON_SIZE_MENU)
3625+ self.assertEqual(row[6], volume['volume_id'])
3626+ self.assertEqual(row[7], volume['path'])
3627+
3628+
3629+class VolumesSubscriptionTestCase(VolumesTestCase):
3630+ """The test suite for the volumes panel."""
3631+
3632+ kwargs = {'main_window': object()}
3633+ tree_path = '0:3' # this is the /home/tester/foo folder, not subscribed
3634+
3635+ def setUp(self):
3636+ super(VolumesSubscriptionTestCase, self).setUp()
3637+ self.patch(gui.os.path, 'exists', lambda path: True)
3638+ self.ui.confirm_dialog.response_code = gui.gtk.RESPONSE_YES
3639+ self.ui.on_volumes_info_ready(FAKE_VOLUMES_INFO)
3640+
3641 def test_on_subscribed_toggled(self):
3642 """Clicking on 'subscribed' updates the folder subscription."""
3643- self.ui.on_volumes_info_ready(FAKE_VOLUMES_INFO)
3644-
3645 real_rows = len(FAKE_VOLUMES_INFO)
3646 data = zip(range(real_rows)[::2], FAKE_VOLUMES_INFO) # skip emtpy rows
3647 for parent, (_, _, volumes) in data:
3648@@ -930,8 +361,7 @@
3649
3650 def test_on_volume_setting_changed(self):
3651 """The setting for a volume was successfully changed."""
3652- self.ui.on_volumes_info_ready(FAKE_VOLUMES_INFO)
3653- self.ui.on_subscribed_toggled(None, "0:0")
3654+ self.ui.on_subscribed_toggled(None, self.tree_path)
3655
3656 self.ui.on_volume_settings_changed(volume_id=None) # id not used
3657
3658@@ -940,8 +370,7 @@
3659
3660 def test_on_volume_setting_change_error(self):
3661 """The setting for a volume was not successfully changed."""
3662- self.ui.on_volumes_info_ready(FAKE_VOLUMES_INFO)
3663- self.ui.on_subscribed_toggled(None, "0:0")
3664+ self.ui.on_subscribed_toggled(None, self.tree_path)
3665
3666 self.patch(self.ui, 'load', self._set_called)
3667 self.ui.on_volume_settings_change_error(volume_id=None,
3668@@ -949,16 +378,66 @@
3669 # reload folders list to sanitize the info in volumes_store
3670 self.assertTrue(self._called, ((), {}))
3671
3672- def test_clicking_on_row_opens_folder(self):
3673- """The folder activated is opened."""
3674- self.patch(gui, 'uri_hook', self._set_called)
3675- self.ui.on_volumes_info_ready(FAKE_VOLUMES_INFO)
3676-
3677- self.ui.volumes_view.row_activated('0:0',
3678- self.ui.volumes_view.get_column(0))
3679-
3680- self.assertEqual(self._called,
3681- ((None, gui.FILE_URI_PREFIX + ROOT['path']), {}))
3682+ def test_confirm_dialog(self):
3683+ """The confirmation dialog is correct."""
3684+ dialog = self.ui.confirm_dialog
3685+
3686+ self.assertEqual(dialog._args, ())
3687+ flags = gui.gtk.DIALOG_MODAL | gui.gtk.DIALOG_DESTROY_WITH_PARENT
3688+ kwargs = dict(parent=self.kwargs['main_window'],
3689+ flags=flags, type=gui.gtk.MESSAGE_WARNING,
3690+ buttons=gui.gtk.BUTTONS_YES_NO)
3691+ self.assertEqual(dialog._kwargs, kwargs)
3692+
3693+ def test_subscribe_shows_confirmation_dialog(self):
3694+ """Clicking on subscribe displays a confirmation dialog."""
3695+ self.ui.on_subscribed_toggled(None, self.tree_path)
3696+
3697+ path = FAKE_FOLDERS_INFO[0]['path']
3698+ self.assertTrue(self.ui.confirm_dialog.was_run, 'dialog was run')
3699+ self.assertEqual(self.ui.confirm_dialog.markup,
3700+ self.ui.CONFIRM_MERGE % {'folder_path': path})
3701+ self.assertFalse(self.ui.confirm_dialog.is_visible, 'dialog was hid')
3702+
3703+ def test_subscribe_does_not_call_backend_if_dialog_closed(self):
3704+ """Backend is not called if users closes the confirmation dialog."""
3705+ self.ui.confirm_dialog.response_code = gui.gtk.RESPONSE_DELETE_EVENT
3706+ self.ui.on_subscribed_toggled(None, self.tree_path)
3707+
3708+ self.assertNotIn('change_volume_settings', self.ui.backend._called)
3709+ self.assertFalse(self.ui.is_processing)
3710+
3711+ def test_subscribe_does_not_call_backend_if_answer_is_no(self):
3712+ """Backend is not called if users clicks on 'No'."""
3713+ self.ui.confirm_dialog.response_code = gui.gtk.RESPONSE_NO
3714+ self.ui.on_subscribed_toggled(None, self.tree_path)
3715+
3716+ self.assertNotIn('change_volume_settings', self.ui.backend._called)
3717+ self.assertFalse(self.ui.is_processing)
3718+
3719+ def test_no_confirmation_if_no_local_folder(self):
3720+ """The confirmation dialog is not shown if local folder not present."""
3721+ self.patch(gui.os.path, 'exists', lambda path: False)
3722+ self.ui.on_subscribed_toggled(None, self.tree_path)
3723+
3724+ self.assertFalse(self.ui.confirm_dialog.was_run, 'dialog was not run')
3725+ self.assertFalse(self.ui.confirm_dialog.is_visible, 'dialog was hid')
3726+
3727+ def test_no_confirmation_if_unsubscribing(self):
3728+ """The confirmation dialog is not shown if unsubscribing."""
3729+ self.ui.on_subscribed_toggled(None, self.tree_path)
3730+
3731+ treeiter = self.ui.volumes_store.get_iter(self.tree_path)
3732+ assert self.ui.volumes_store.get_value(treeiter, 1)
3733+
3734+ # reset flags
3735+ self.ui.confirm_dialog.was_run = False
3736+ self.ui.confirm_dialog.is_visible = False
3737+
3738+ self.ui.on_subscribed_toggled(None, self.tree_path)
3739+
3740+ self.assertFalse(self.ui.confirm_dialog.was_run, 'dialog was not run')
3741+ self.assertFalse(self.ui.confirm_dialog.is_visible, 'dialog was hid')
3742
3743
3744 class DeviceTestCase(ControlPanelMixinTestCase):
3745@@ -984,6 +463,12 @@
3746 self.assertEqual(device.limit_bandwidth.get_active(),
3747 bool(expected['limit_bandwidth']))
3748
3749+ config_enabled = self.ui.config_settings.get_sensitive()
3750+ self.assertEqual(device.configurable, config_enabled)
3751+
3752+ limit_enabled = self.ui.throttling_limits.get_sensitive()
3753+ self.assertEqual(device.limit_bandwidth.get_active(), limit_enabled)
3754+
3755 value = int(expected['max_upload_speed']) // gui.KILOBYTES
3756 self.assertEqual(device.max_upload_speed.get_value_as_int(), value)
3757 value = int(expected['max_download_speed']) // gui.KILOBYTES
3758@@ -996,6 +481,9 @@
3759 (self.ui.id, expected))
3760 self.assertEqual(self.ui.warning_label.get_text(), '')
3761
3762+ limit_enabled = self.ui.throttling_limits.get_sensitive()
3763+ self.assertEqual(self.ui.limit_bandwidth.get_active(), limit_enabled)
3764+
3765 def modify_settings(self):
3766 """Modify settings so values actually change."""
3767 new_val = not self.ui.show_all_notifications.get_active()
3768@@ -1087,13 +575,13 @@
3769 """A device can be updated from a dict."""
3770 self.ui.update(configurable='')
3771 self.assertFalse(self.ui.configurable)
3772- self.assertFalse(self.ui.throttling.get_visible())
3773+ self.assertFalse(self.ui.config_settings.get_visible())
3774
3775 def test_update_configurable(self):
3776 """A device can be updated from a dict."""
3777 self.ui.update(configurable='True')
3778 self.assertTrue(self.ui.configurable)
3779- self.assertTrue(self.ui.throttling.get_visible())
3780+ self.assertTrue(self.ui.config_settings.get_visible())
3781
3782 def test_update_show_all_notifications(self):
3783 """A device can be updated from a dict."""
3784@@ -1107,9 +595,11 @@
3785 """A device can be updated from a dict."""
3786 self.ui.update(limit_bandwidth='')
3787 self.assertFalse(self.ui.limit_bandwidth.get_active())
3788+ self.assertFalse(self.ui.throttling_limits.get_sensitive())
3789
3790 self.ui.update(limit_bandwidth='True')
3791 self.assertTrue(self.ui.limit_bandwidth.get_active())
3792+ self.assertTrue(self.ui.throttling_limits.get_sensitive())
3793
3794 def test_update_upload_speed(self):
3795 """A device can be updated from a dict."""
3796@@ -1610,7 +1100,8 @@
3797 klass = gui.Service
3798 service_id = 'dc_test'
3799 name = u'Qué lindo test!'
3800- kwargs = {'service_id': service_id, 'name': name}
3801+ kwargs = {'service_id': service_id, 'name': name,
3802+ 'container': None, 'check_button': None}
3803
3804 def test_is_an_box(self):
3805 """Inherits from gtk.VBox."""
3806@@ -1647,7 +1138,7 @@
3807 klass = gui.FileSyncService
3808 service_id = 'file-sync'
3809 name = gui.FileSyncService.FILES_SERVICE_NAME
3810- kwargs = {}
3811+ kwargs = {'container': None, 'check_button': None, 'action_button': None}
3812
3813 def test_backend_account_signals(self):
3814 """The proper signals are connected to the backend."""
3815@@ -1708,6 +1199,8 @@
3816 self.ui.on_files_disabled()
3817 self.assertFalse(self.ui.button.get_active())
3818
3819+FileSyncServiceTestCase.skip = 'LP: #729349'
3820+
3821
3822 class DesktopcouchServiceTestCase(ServiceTestCase):
3823 """The test suite for a desktopcouch service."""
3824@@ -1812,6 +1305,8 @@
3825
3826 self.assertEqual(self.ui.warning_label.get_text(), '')
3827
3828+DesktopcouchServiceTestCase.skip = 'LP: #729349'
3829+
3830
3831 class DesktopcouchServiceDisabledAtStartupTestCase(ServiceTestCase):
3832 """The test suite for a desktopcouch service when enabled=False."""
3833@@ -1854,6 +1349,8 @@
3834 self.assertEqual(sorted(self.ui.get_children()),
3835 sorted([self.ui.button, self.ui.warning_label]))
3836
3837+DesktopcouchServiceWithDependencyTestCase.skip = 'LP: #729349'
3838+
3839
3840 class ServicesTestCase(ControlPanelMixinTestCase):
3841 """The test suite for the services panel."""
3842@@ -1902,6 +1399,8 @@
3843 child, = self.ui.files.get_children()
3844 self.assertIsInstance(child, gui.FileSyncService)
3845
3846+ test_files_is_a_file_sync_service.skip = 'LP: #729349'
3847+
3848
3849 class ServicesWithoutDesktopcouchTestCase(ServicesTestCase):
3850 """The test suite for the services panel when DC is not installed."""
3851@@ -2004,6 +1503,30 @@
3852 self.ui.on_replications_info_ready(info=FAKE_REPLICATIONS_INFO)
3853 self.test_replications()
3854
3855+ def test_service_name_in_mapping(self):
3856+ """If available, use a user-friendly, translatable service name."""
3857+ for sid, name in self.ui.service_names.iteritems():
3858+ info = [{'replication_id': sid, 'name': 'Bar',
3859+ 'enabled': 'True', 'dependency': ''}]
3860+ self.ui.on_replications_info_ready(info=info)
3861+
3862+ child, = self.ui.replications.get_children()
3863+ self.assertEqual(child.button.get_label(), name)
3864+
3865+ def test_service_name_not_in_mapping(self):
3866+ """If available, use a user-friendly, translatable service name."""
3867+ sid = 'not-in-mapping'
3868+ assert sid not in self.ui.service_names
3869+
3870+ info = [{'replication_id': sid, 'name': 'Bar',
3871+ 'enabled': 'True', 'dependency': ''}]
3872+ self.ui.on_replications_info_ready(info=info)
3873+
3874+ child, = self.ui.replications.get_children()
3875+ self.assertEqual(child.button.get_label(), info[0]['name'])
3876+
3877+ServicesWithDesktopcouchTestCase.skip = 'LP: #729349'
3878+
3879
3880 class ServicesWithDesktopcouchErrorTestCase(ServicesTestCase):
3881 """The test suite for the services panel."""
3882@@ -2046,6 +1569,8 @@
3883 self.assertFalse(self.ui.message.active)
3884 self.assert_warning_correct(self.ui.message, gui.VALUE_ERROR)
3885
3886+ServicesWithDesktopcouchErrorTestCase.skip = 'LP: #729349'
3887+
3888
3889 class FileSyncStatusTestCase(ControlPanelMixinTestCase):
3890 """Test case for a file sync status widget."""
3891@@ -2065,7 +1590,8 @@
3892 """
3893 self.assertTrue(self.ui.label.get_visible())
3894 self.assertFalse(self.ui.label.active)
3895- self.assertTrue(self.ui.label.get_label().endswith(status))
3896+ msg = '%r does not end with %r' % (self.ui.label.get_label(), status)
3897+ self.assertTrue(self.ui.label.get_label().endswith(status), msg)
3898
3899 self.assertTrue(self.ui.button.is_sensitive())
3900 self.assertFalse(self.ui.button.get_visited())
3901@@ -2212,10 +1738,21 @@
3902 action=self.ui.DISCONNECT,
3903 tooltip=self.ui.DISCONNECT_TOOLTIP)
3904
3905- def test_on_file_sync_status_error(self):
3906- """The file sync status couldn't be retrieved."""
3907- self.patch(self.ui, 'on_restart_clicked', self._set_called)
3908- self.ui.on_file_sync_status_error({'error_msg': 'error msg'})
3909+ def test_on_file_sync_status_error_with_error_msg(self):
3910+ """The file sync status couldn't be retrieved."""
3911+ self.patch(self.ui, 'on_restart_clicked', self._set_called)
3912+ msg = 'error message'
3913+ self.ui.on_file_sync_status_error({'error_msg': msg})
3914+
3915+ msg = gui.WARNING_MARKUP % (self.ui.FILE_SYNC_ERROR + ' (' + msg + ')')
3916+ self.assert_status_correct(msg,
3917+ action=self.ui.RESTART,
3918+ tooltip=self.ui.RESTART_TOOLTIP)
3919+
3920+ def test_on_file_sync_status_error_without_error_msg(self):
3921+ """The file sync status couldn't be retrieved."""
3922+ self.patch(self.ui, 'on_restart_clicked', self._set_called)
3923+ self.ui.on_file_sync_status_error()
3924
3925 msg = gui.WARNING_MARKUP % self.ui.FILE_SYNC_ERROR
3926 self.assert_status_correct(msg,
3927@@ -2276,17 +1813,21 @@
3928 """Check that the displayed account info matches 'info'."""
3929 used = int(info['quota_used'])
3930 total = int(info['quota_total'])
3931- percentage = (used / total) * 100
3932+ percentage = round((used / total) * 100, 2)
3933 expected = {'used': self.ui.humanize(used),
3934 'total': self.ui.humanize(total),
3935 'percentage': percentage}
3936- self.assertEqual(self.ui.quota_label.get_text(),
3937- self.ui.QUOTA_LABEL % expected)
3938+ msg = self.ui.QUOTA_LABEL % expected
3939+ self.assertEqual(self.ui.quota_label.get_text(), msg)
3940+
3941+ if percentage >= self.ui.QUOTA_THRESHOLD * 100:
3942+ self.assert_warning_correct(self.ui.quota_label, msg)
3943
3944 if progressbar_fraction is None:
3945- progressbar_fraction = percentage / 100
3946+ progressbar_fraction = min(percentage / 100, 1)
3947 self.assertEqual(self.ui.quota_progressbar.get_fraction(),
3948 progressbar_fraction)
3949+ self.assertTrue(self.ui.quota_progressbar.get_sensitive())
3950
3951 def test_is_a_vbox(self):
3952 """Inherits from gtk.VBox."""
3953@@ -2386,13 +1927,13 @@
3954
3955 self.assertEqual(self._called, ((), {}))
3956
3957- def test_entering_services_tab_loads_content(self):
3958- """The services info is loaded when entering the Devices tab."""
3959+ def test_entering_services_tab_does_not_load_content(self):
3960+ """The services info is not loaded when entering the Services tab."""
3961 self.patch(self.ui.services, 'load', self._set_called)
3962 # clean backend calls
3963 self.ui.services_button.clicked()
3964
3965- self.assertEqual(self._called, ((), {}))
3966+ self.assertEqual(self._called, False)
3967
3968 def test_quota_placeholder_is_loading(self):
3969 """Placeholders for quota label is a Loading widget."""
3970@@ -2403,16 +1944,20 @@
3971 """The account info is processed when ready."""
3972 self.ui.on_account_info_ready(FAKE_ACCOUNT_INFO)
3973 self.assert_account_info_correct(FAKE_ACCOUNT_INFO)
3974-
3975- for widget in (self.ui.quota_label,):
3976- self.assertFalse(widget.active)
3977+ self.assertFalse(self.ui.quota_label.active)
3978
3979 def test_on_account_info_error(self):
3980 """The account info couldn't be retrieved."""
3981 self.ui.on_account_info_error()
3982- for widget in (self.ui.quota_label,):
3983- self.assert_warning_correct(widget, gui.VALUE_ERROR)
3984- self.assertFalse(widget.active)
3985+ self.assertEqual(self.ui.quota_label.get_text(), '')
3986+ self.assertEqual(self.ui.quota_progressbar.get_fraction(), 0)
3987+ self.assertFalse(self.ui.quota_progressbar.get_sensitive())
3988+ self.assertFalse(self.ui.quota_label.active)
3989+
3990+ def test_on_account_info_error_after_success(self):
3991+ """The account info couldn't be retrieved."""
3992+ self.test_on_account_info_ready()
3993+ self.test_on_account_info_error()
3994
3995 def test_on_account_info_ready_quota_unused(self):
3996 """The used quota is correct when unused."""
3997@@ -2429,6 +1974,26 @@
3998
3999 self.assert_account_info_correct(info, progressbar_fraction=0.05)
4000
4001+ def test_on_account_info_ready_quota_used_at_threshold(self):
4002+ """Show red notification when quota usage is same as threshold."""
4003+ info = FAKE_ACCOUNT_INFO.copy()
4004+
4005+ info['quota_total'] = '12345678'
4006+ info['quota_used'] = str(int(int(info['quota_total']) *
4007+ self.ui.QUOTA_THRESHOLD))
4008+ self.ui.on_account_info_ready(info)
4009+
4010+ self.assert_account_info_correct(info)
4011+
4012+ def test_on_account_info_ready_quota_over_threshold(self):
4013+ """Show red notification when quota usage is higher than threshold."""
4014+ info = FAKE_ACCOUNT_INFO.copy()
4015+ info['quota_total'] = '12345678'
4016+ info['quota_used'] = info['quota_total'] + '0'
4017+ self.ui.on_account_info_ready(info)
4018+
4019+ self.assert_account_info_correct(info)
4020+
4021 def test_file_sync_status(self):
4022 """The file sync status is shown correctly."""
4023 self.assertIsInstance(self.ui.status_label, gui.FileSyncStatus)
4024
4025=== added file 'ubuntuone/controlpanel/gtk/tests/test_gui_basic.py'
4026--- ubuntuone/controlpanel/gtk/tests/test_gui_basic.py 1970-01-01 00:00:00 +0000
4027+++ ubuntuone/controlpanel/gtk/tests/test_gui_basic.py 2011-03-10 03:11:54 +0000
4028@@ -0,0 +1,595 @@
4029+# -*- coding: utf-8 -*-
4030+
4031+# Authors: Natalia B Bidart <natalia.bidart@canonical.com>
4032+#
4033+# Copyright 2010 Canonical Ltd.
4034+#
4035+# This program is free software: you can redistribute it and/or modify it
4036+# under the terms of the GNU General Public License version 3, as published
4037+# by the Free Software Foundation.
4038+#
4039+# This program is distributed in the hope that it will be useful, but
4040+# WITHOUT ANY WARRANTY; without even the implied warranties of
4041+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
4042+# PURPOSE. See the GNU General Public License for more details.
4043+#
4044+# You should have received a copy of the GNU General Public License along
4045+# with this program. If not, see <http://www.gnu.org/licenses/>.
4046+
4047+"""The test suite for the control panel user interface (basics)."""
4048+
4049+from __future__ import division
4050+
4051+from ubuntuone.controlpanel.gtk import gui
4052+from ubuntuone.controlpanel.gtk.tests import BaseTestCase, FakedSSOBackend
4053+from ubuntuone.controlpanel.tests import TOKEN
4054+
4055+
4056+# Attribute 'yyy' defined outside __init__, access to a protected member
4057+# pylint: disable=W0201, W0212
4058+
4059+
4060+class ControlPanelMixinTestCase(BaseTestCase):
4061+ """The test suite for the control panel widget."""
4062+
4063+ klass = gui.ControlPanelMixin
4064+ ui_filename = None
4065+
4066+ def test_is_a_control_panel_mixin(self):
4067+ """Inherits from ControlPanelMixin."""
4068+ self.assertIsInstance(self.ui, gui.ControlPanelMixin)
4069+
4070+ def test_ui_can_be_created(self):
4071+ """UI main class exists and can be created."""
4072+ self.assertTrue(self.ui is not None)
4073+
4074+
4075+class ControlPanelWindowTestCase(BaseTestCase):
4076+ """The test suite for the control panel window."""
4077+
4078+ klass = gui.ControlPanelWindow
4079+
4080+ def test_is_a_window(self):
4081+ """Inherits from gtk.Window."""
4082+ self.assertIsInstance(self.ui, gui.gtk.Window)
4083+
4084+ def test_startup_visibility(self):
4085+ """The widget is visible at startup."""
4086+ self.assertTrue(self.ui.get_visible(), 'must be visible at startup.')
4087+
4088+ def test_main_start_gtk_main_loop(self):
4089+ """The GTK main loop is started when calling main()."""
4090+ self.patch(gui.gtk, 'main', self._set_called)
4091+ self.ui.main()
4092+ self.assertEqual(self._called, ((), {}), 'gtk.main was called.')
4093+
4094+ def test_closing_stops_the_main_lopp(self):
4095+ """The GTK main loop is stopped when closing the window."""
4096+ self.patch(gui.gtk, 'main_quit', self._set_called)
4097+ self.ui.emit('delete-event', None)
4098+ self.assertEqual(self._called, ((), {}), 'gtk.main_quit was called.')
4099+
4100+ def test_title_is_correct(self):
4101+ """The window title is correct."""
4102+ expected = self.ui.TITLE % {'app_name': gui.U1_APP_NAME}
4103+ self.assertEqual(self.ui.get_title(), expected)
4104+
4105+ def test_control_panel_is_the_only_child(self):
4106+ """The control panel is the window's content."""
4107+ children = self.ui.get_children()
4108+ self.assertEqual(1, len(children))
4109+
4110+ control_panel = self.ui.get_children()[0]
4111+ self.assertTrue(control_panel is self.ui.control_panel)
4112+ self.assertIsInstance(self.ui.control_panel, gui.ControlPanel)
4113+ self.assertTrue(self.ui.control_panel.get_visible())
4114+
4115+ def test_main_window_is_passed_to_child(self):
4116+ """The child gets the main_window."""
4117+ self.assertEqual(self.ui.control_panel.main_window, self.ui)
4118+
4119+ def test_icon_name_is_correct(self):
4120+ """The icon name is correct."""
4121+ self.assertEqual(self.ui.get_icon_name(), 'ubuntuone')
4122+
4123+ def test_max_size(self):
4124+ """Max size is not bigger than 736x525 (LP: #645526, LP: #683164)."""
4125+ self.assertTrue(self.ui.get_size_request() <= (736, 525))
4126+
4127+
4128+class ControlPanelWindowParamsTestCase(ControlPanelWindowTestCase):
4129+ """The test suite for the control panel window when passing params."""
4130+
4131+ kwargs = {'switch_to': 'devices'}
4132+
4133+ def test_switch_to(self):
4134+ """Can pass a 'switch_to' parameter to start on a particular tab."""
4135+ actual = self.ui.control_panel.management.notebook.get_current_page()
4136+ self.assertEqual(actual, self.ui.control_panel.management.DEVICES_PAGE)
4137+
4138+
4139+class ControlPanelWindowParamsNoneTestCase(ControlPanelWindowTestCase):
4140+ """The suite for the control panel window when passing None params."""
4141+
4142+ kwargs = {'switch_to': None}
4143+
4144+ def test_switch_to(self):
4145+ """Can pass a 'switch_to' being None. Should default to dashboard."""
4146+ actual = self.ui.control_panel.management.notebook.get_current_page()
4147+ expected = self.ui.control_panel.management.DASHBOARD_PAGE
4148+ self.assertEqual(actual, expected)
4149+
4150+
4151+class ControlPanelWindowInvalidParamsTestCase(ControlPanelWindowTestCase):
4152+ """The suite for the control panel window when passing invalid params."""
4153+
4154+ kwargs = {'switch_to': 'yadda-yadda'}
4155+
4156+ def test_switch_to(self):
4157+ """Can pass an invalid 'switch_to'. Should default to dashboard."""
4158+ actual = self.ui.control_panel.management.notebook.get_current_page()
4159+ expected = self.ui.control_panel.management.DASHBOARD_PAGE
4160+ self.assertEqual(actual, expected)
4161+
4162+
4163+class ControlPanelTestCase(BaseTestCase):
4164+ """The test suite for the control panel itself."""
4165+
4166+ klass = gui.ControlPanel
4167+ kwargs = {'main_window': object()}
4168+
4169+ def assert_current_tab_correct(self, expected_tab):
4170+ """Check that the wiget 'expected_tab' is the current page."""
4171+ actual = self.ui.get_nth_page(self.ui.get_current_page())
4172+ self.assertTrue(expected_tab is actual)
4173+
4174+ def test_is_a_notebook(self):
4175+ """Inherits from gtk.VBox."""
4176+ self.assertIsInstance(self.ui, gui.gtk.Notebook)
4177+
4178+ def test_startup_visibility(self):
4179+ """The widget is visible at startup."""
4180+ self.assertTrue(self.ui.get_visible(),
4181+ 'must be visible at startup.')
4182+
4183+ def test_startup_props(self):
4184+ """The tabs and border are not shown."""
4185+ self.assertFalse(self.ui.get_show_border(), 'must not show border.')
4186+ self.assertFalse(self.ui.get_show_tabs(), 'must not show tabs.')
4187+
4188+ def test_overview_is_shown_at_startup(self):
4189+ """The overview is shown at startup."""
4190+ self.assertIsInstance(self.ui.overview, gui.OverviewPanel)
4191+ self.assert_current_tab_correct(self.ui.overview)
4192+
4193+ def test_main_window_is_passed_to_child(self):
4194+ """The child gets the main_window."""
4195+ self.assertEqual(self.ui.overview.main_window,
4196+ self.kwargs['main_window'])
4197+
4198+ def test_on_show_management_panel(self):
4199+ """A ManagementPanel is shown when the callback is executed."""
4200+ self.ui.on_show_management_panel()
4201+ self.assert_current_tab_correct(self.ui.management)
4202+
4203+ def test_on_show_management_panel_is_idempotent(self):
4204+ """Only one ManagementPanel is shown."""
4205+ self.ui.on_show_management_panel()
4206+ self.ui.on_show_management_panel()
4207+
4208+ self.assert_current_tab_correct(self.ui.management)
4209+
4210+ def test_credentials_found_shows_dashboard_management_panel(self):
4211+ """On 'credentials-found' signal, the management panel is shown.
4212+
4213+ If first signal parameter is False, visible tab should be dashboard.
4214+
4215+ """
4216+ self.patch(self.ui.management, 'load', self._set_called)
4217+ self.ui.overview.emit('credentials-found', False, object())
4218+
4219+ self.assert_current_tab_correct(self.ui.management)
4220+ self.assertEqual(self.ui.management.notebook.get_current_page(),
4221+ self.ui.management.DASHBOARD_PAGE)
4222+ self.assertEqual(self._called, ((), {}))
4223+
4224+ def test_credentials_found_shows_volumes_management_panel(self):
4225+ """On 'credentials-found' signal, the management panel is shown.
4226+
4227+ If first signal parameter is True, visible tab should be volumes.
4228+
4229+ """
4230+ a_token = object()
4231+ self.ui.overview.emit('credentials-found', True, a_token)
4232+
4233+ self.assert_current_tab_correct(self.ui.management)
4234+ self.assertEqual(self.ui.management.notebook.get_current_page(),
4235+ self.ui.management.VOLUMES_PAGE)
4236+
4237+ def test_local_device_removed_shows_overview_panel(self):
4238+ """On 'local-device-removed' signal, the overview panel is shown."""
4239+ self.ui.overview.emit('credentials-found', True, object())
4240+ self.ui.management.emit('local-device-removed')
4241+
4242+ self.assert_current_tab_correct(self.ui.overview)
4243+
4244+
4245+class UbuntuOneBinTestCase(BaseTestCase):
4246+ """The test suite for a Ubuntu One panel."""
4247+
4248+ klass = gui.UbuntuOneBin
4249+ kwargs = {'title': 'Something old, something new and something blue.'}
4250+
4251+ def test_is_a_vbox(self):
4252+ """Inherits from proper gtk widget."""
4253+ self.assertIsInstance(self.ui, gui.gtk.VBox)
4254+
4255+ def test_startup_visibility(self):
4256+ """The widget is visible at startup."""
4257+ self.assertTrue(self.ui.get_visible(),
4258+ 'must be visible at startup.')
4259+ for child in self.ui.get_children():
4260+ self.assertTrue(child.get_visible())
4261+
4262+ def test_title_is_a_panel_title(self):
4263+ """Title is the correct widget."""
4264+ self.assertIsInstance(self.ui.title, gui.PanelTitle)
4265+ self.assertIn(self.ui.title, self.ui.get_children())
4266+
4267+ def test_title_markup_is_correct(self):
4268+ """The title markup is correctly set when passed as argument."""
4269+ self.assertEqual(self.ui.title.label.get_text(), self.kwargs['title'])
4270+
4271+ def test_title_is_correct(self):
4272+ """The title markup is correctly set when defined at class level."""
4273+ ui = self.klass() # no title given
4274+ self.assertEqual(ui.title.label.get_text(), '')
4275+
4276+ def test_message_is_a_label_loading(self):
4277+ """Message is the correct widget."""
4278+ self.assertIsInstance(self.ui.message, gui.LabelLoading)
4279+ self.assertIn(self.ui.message, self.ui.get_children())
4280+
4281+ def test_on_success(self):
4282+ """Callback to stop the Loading and clear messages."""
4283+ self.ui.on_success()
4284+ self.assertEqual(self.ui.message.get_label(), '')
4285+ self.assertFalse(self.ui.message.active)
4286+
4287+ def test_on_success_with_message(self):
4288+ """Callback to stop the Loading and show a info message."""
4289+ msg = 'WOW! <i>this rocks</i>'
4290+ self.ui.on_success(message=msg)
4291+ self.assertEqual(self.ui.message.get_label(), msg)
4292+ self.assertFalse(self.ui.message.active)
4293+
4294+ def test_on_error(self):
4295+ """Callback to stop the Loading and clear messages."""
4296+ self.ui.on_error()
4297+ self.assert_warning_correct(self.ui.message, gui.VALUE_ERROR)
4298+ self.assertFalse(self.ui.message.active)
4299+
4300+ def test_on_error_with_message(self):
4301+ """Callback to stop the Loading and show a info message."""
4302+ msg = 'WOW! <i>this does not rock</i> :-/'
4303+ self.ui.on_error(message=msg)
4304+ self.assert_warning_correct(self.ui.message, msg)
4305+ self.assertFalse(self.ui.message.active)
4306+
4307+ def test_is_processing(self):
4308+ """The flag 'is_processing' is False on start."""
4309+ self.assertFalse(self.ui.is_processing)
4310+ self.assertTrue(self.ui.is_sensitive())
4311+
4312+ def test_set_is_processing(self):
4313+ """When setting 'is_processing', the spinner is shown."""
4314+ self.ui.is_processing = False
4315+ self.ui.is_processing = True
4316+
4317+ self.assertTrue(self.ui.message.get_visible())
4318+ self.assertTrue(self.ui.message.active)
4319+ self.assertFalse(self.ui.is_sensitive())
4320+
4321+ def test_unset_is_processing(self):
4322+ """When unsetting 'is_processing', the spinner is not shown."""
4323+ self.ui.is_processing = True
4324+ self.ui.is_processing = False
4325+
4326+ self.assertTrue(self.ui.message.get_visible())
4327+ self.assertFalse(self.ui.message.active)
4328+ self.assertTrue(self.ui.is_sensitive())
4329+
4330+
4331+class OverwiewPanelTestCase(ControlPanelMixinTestCase):
4332+ """The test suite for the overview panel."""
4333+
4334+ klass = gui.OverviewPanel
4335+ kwargs = {'main_window': gui.gtk.Window()}
4336+ ui_filename = 'overview.ui'
4337+
4338+ def test_is_a_greyable_bin(self):
4339+ """Inherits from GreyableBin."""
4340+ self.assertIsInstance(self.ui, gui.GreyableBin)
4341+
4342+ def test_inner_widget_is_packed(self):
4343+ """The 'itself' vbox is packed into the widget."""
4344+ self.assertIn(self.ui.itself, self.ui.get_children())
4345+
4346+ def test_join_now_is_default(self):
4347+ """The 'join_now' button is the default widget."""
4348+ self.assertTrue(self.ui.join_now_button.get_property('can-default'))
4349+
4350+ def test_sso_backend(self):
4351+ """Has a correct SSO backend."""
4352+ self.assertIsInstance(self.ui.sso_backend, FakedSSOBackend)
4353+
4354+ def test_sso_backend_signals(self):
4355+ """The proper signals are connected to the backend."""
4356+ self.assertEqual(self.ui.sso_backend._signals['CredentialsFound'],
4357+ [self.ui.on_credentials_found])
4358+ self.assertEqual(self.ui.sso_backend._signals['CredentialsNotFound'],
4359+ [self.ui.on_credentials_not_found])
4360+ self.assertEqual(self.ui.sso_backend._signals['CredentialsError'],
4361+ [self.ui.on_credentials_error])
4362+ self.assertEqual(self.ui.sso_backend._signals['AuthorizationDenied'],
4363+ [self.ui.on_authorization_denied])
4364+
4365+
4366+class OverwiewNetworkStatePanelTestCase(OverwiewPanelTestCase):
4367+ """The test suite for the overview panel regarding network state."""
4368+
4369+ def test_network_state_is_created(self):
4370+ """The network state is created."""
4371+ self.assertIsInstance(self.ui.network_manager_state,
4372+ gui.networkstate.NetworkManagerState)
4373+ self.assertEqual(self.ui.network_manager_state._kwargs['result_cb'],
4374+ self.ui.on_network_state_changed)
4375+
4376+ def test_network_state_is_queried_at_startup(self):
4377+ """The network state is asked to the NetworkManagerState."""
4378+ self.assertTrue('find_online_state' in
4379+ self.ui.network_manager_state._called)
4380+
4381+ def test_state_online(self):
4382+ """Network connection is online."""
4383+ self.ui.on_network_state_changed(gui.networkstate.ONLINE)
4384+ # all green, no warning
4385+ self.assertEqual(self.ui.warning_label.get_text(), '')
4386+ self.assertTrue(self.ui.get_sensitive())
4387+
4388+ def test_state_offline(self):
4389+ """Network connection is offline."""
4390+ self.ui.on_network_state_changed(gui.networkstate.OFFLINE)
4391+ msg = self.ui.NETWORK_OFFLINE % {'app_name': gui.U1_APP_NAME}
4392+
4393+ self.assert_warning_correct(self.ui.warning_label, msg)
4394+ self.assertFalse(self.ui.get_sensitive())
4395+
4396+ def test_state_unknown(self):
4397+ """Network connection is unknown. Assume that connection is present."""
4398+ self.ui.on_network_state_changed(gui.networkstate.UNKNOWN)
4399+
4400+ self.assertEqual(self.ui.warning_label.get_text(), '')
4401+ self.assertTrue(self.ui.get_sensitive())
4402+
4403+
4404+class OverwiewPanelOnlineTestCase(OverwiewPanelTestCase):
4405+ """The test suite for the overview panel."""
4406+
4407+ def setUp(self):
4408+ super(OverwiewPanelOnlineTestCase, self).setUp()
4409+ self.ui.on_network_state_changed(gui.networkstate.ONLINE)
4410+
4411+ def test_find_credentials_is_called(self):
4412+ """Credentials are asked to SSO backend."""
4413+ self.assertFalse(self.ui._credentials_are_new)
4414+ self.assert_backend_called('find_credentials', (gui.U1_APP_NAME, {}),
4415+ backend=self.ui.sso_backend)
4416+
4417+ def test_on_credentials_found(self):
4418+ """Callback 'on_credentials_found' is correct."""
4419+ self.ui.connect('credentials-found', self._set_called)
4420+
4421+ self.ui.on_credentials_found(gui.U1_APP_NAME, TOKEN)
4422+
4423+ self.assertFalse(self.ui.get_property('greyed'), 'Must not be greyed.')
4424+ # assume credentials were in local keyring
4425+ self.assertEqual(self._called, ((self.ui, False, TOKEN), {}))
4426+
4427+ def test_on_credentials_found_when_creds_are_not_new(self):
4428+ """Callback 'on_credentials_found' distinguish if creds are new."""
4429+ self.ui.connect('credentials-found', self._set_called)
4430+
4431+ # credentials weren't in the system
4432+ self.ui.on_credentials_not_found(gui.U1_APP_NAME)
4433+ # now they are!
4434+ self.ui.on_credentials_found(gui.U1_APP_NAME, TOKEN)
4435+
4436+ self.assertFalse(self.ui.get_property('greyed'), 'Must not be greyed.')
4437+ # assume credentials were not in local keyring
4438+ self.assertEqual(self._called, ((self.ui, True, TOKEN), {}))
4439+
4440+ def test_on_credentials_not_found(self):
4441+ """Callback 'on_credentials_not_found' is correct."""
4442+ self.ui.on_credentials_not_found(gui.U1_APP_NAME)
4443+ self.assertTrue(self.ui.get_visible())
4444+ self.assertTrue(self.ui._credentials_are_new)
4445+
4446+ def test_on_credentials_error(self):
4447+ """Callback 'on_credentials_error' is correct."""
4448+ self.ui.on_credentials_error(gui.U1_APP_NAME, {})
4449+ self.assertTrue(self.ui.get_visible())
4450+ self.assert_warning_correct(self.ui.warning_label,
4451+ self.ui.CREDENTIALS_ERROR)
4452+
4453+ def test_on_authorization_denied(self):
4454+ """Callback 'on_authorization_denied' is correct."""
4455+ self.ui.on_authorization_denied(gui.U1_APP_NAME)
4456+ self.assertTrue(self.ui.get_visible())
4457+ self.assertFalse(self.ui.get_property('greyed'), 'Must not be greyed.')
4458+ self.assertEqual(self.ui.warning_label.get_text(), '')
4459+
4460+
4461+class OverwiewPanelAppNameMismatchTestCase(OverwiewPanelTestCase):
4462+ """The test suite for the overview panel when the app_name won't match."""
4463+
4464+ NOT_U1_APP = 'Not ' + gui.U1_APP_NAME
4465+
4466+ def test_filter_by_app_name(self):
4467+ """The filter_by_app_name decorator is correct."""
4468+ f = gui.filter_by_app_name(self._set_called)
4469+ f(self.ui, self.NOT_U1_APP)
4470+ self.assertFalse(self._called)
4471+ self.assertTrue(self.memento.check_info('ignoring', self.NOT_U1_APP))
4472+
4473+ args = ('test', object())
4474+ kwargs = {'really': 'AWESOME'}
4475+ f(self.ui, gui.U1_APP_NAME, *args, **kwargs)
4476+ self.assertEqual(self._called,
4477+ ((self.ui, gui.U1_APP_NAME,) + args, kwargs))
4478+
4479+ def test_on_credentials_found(self):
4480+ """Callback 'on_credentials_found' is not executed."""
4481+ self.assert_function_decorated(gui.filter_by_app_name,
4482+ self.ui.on_credentials_found)
4483+
4484+ def test_on_credentials_not_found(self):
4485+ """Callback 'on_credentials_not_found' is not executed."""
4486+ self.assert_function_decorated(gui.filter_by_app_name,
4487+ self.ui.on_credentials_not_found)
4488+
4489+ def test_on_credentials_error(self):
4490+ """Callback 'on_credentials_error' is not executed."""
4491+ self.assert_function_decorated(gui.filter_by_app_name,
4492+ self.ui.on_credentials_error)
4493+
4494+ def test_on_authorization_denied(self):
4495+ """Callback 'on_authorization_denied' is not executed."""
4496+ self.assert_function_decorated(gui.filter_by_app_name,
4497+ self.ui.on_authorization_denied)
4498+
4499+
4500+class OverwiewPanelNoCredsTestCase(OverwiewPanelTestCase):
4501+ """The test suite for the overview panel when no credentials are found."""
4502+
4503+ def setUp(self):
4504+ super(OverwiewPanelNoCredsTestCase, self).setUp()
4505+ self.ui.on_credentials_not_found(gui.U1_APP_NAME)
4506+
4507+ def test_startup_visibility(self):
4508+ """The widget is visible at startup."""
4509+ self.assertTrue(self.ui.get_visible(),
4510+ 'must be visible at startup if credentials not found.')
4511+
4512+ def test_warning_label_is_hidden(self):
4513+ """The warning label is not shown by default."""
4514+ self.assertEqual(self.ui.warning_label.get_text(), '')
4515+
4516+ def test_image_is_correct(self):
4517+ """There is an image attribute and is correct."""
4518+ self.assert_image_equal(self.ui.image, 'overview.png')
4519+
4520+ def test_join_now_is_default_widget(self):
4521+ """The join now button is the default widget."""
4522+ self.assertTrue(self.ui.join_now_button.get_property('can_default'))
4523+
4524+ def test_join_now_button_clicked(self):
4525+ """Test the 'join now' button callback."""
4526+ self.kwargs['main_window'].show() # ensure parent window is realized
4527+ self.addCleanup(self.kwargs['main_window'].hide)
4528+
4529+ self.ui.join_now_button.clicked()
4530+
4531+ window_id = self.kwargs['main_window'].window.xid
4532+ args = (gui.U1_APP_NAME,
4533+ {gui.TC_URL_KEY: gui.U1_TC_URL,
4534+ gui.HELP_TEXT_KEY: gui.U1_DESCRIPTION,
4535+ gui.WINDOW_ID_KEY: str(window_id),
4536+ gui.PING_URL_KEY: gui.U1_PING_URL})
4537+ self.assert_backend_called('register', args,
4538+ backend=self.ui.sso_backend)
4539+
4540+ def test_connect_button_clicked(self):
4541+ """Test the 'join now' button callback."""
4542+ self.kwargs['main_window'].show() # ensure parent window is realized
4543+ self.addCleanup(self.kwargs['main_window'].hide)
4544+
4545+ self.ui.connect_button.clicked()
4546+
4547+ window_id = self.kwargs['main_window'].window.xid
4548+ args = (gui.U1_APP_NAME,
4549+ {gui.TC_URL_KEY: gui.U1_TC_URL,
4550+ gui.HELP_TEXT_KEY: gui.U1_DESCRIPTION,
4551+ gui.WINDOW_ID_KEY: str(window_id),
4552+ gui.PING_URL_KEY: gui.U1_PING_URL})
4553+ self.assert_backend_called('login', args,
4554+ backend=self.ui.sso_backend)
4555+
4556+ def test_join_now_button_clicked_set_greyed(self):
4557+ """Clicking on 'join_now' self is greyed."""
4558+ self.ui.join_now_button.clicked()
4559+ self.assertTrue(self.ui.get_property('greyed'), 'Must be greyed.')
4560+
4561+ def test_join_now_button_clicked_removes_warning(self):
4562+ """Clicking on 'join_now' the warnings are removed."""
4563+ self.ui.on_authorization_denied(gui.U1_APP_NAME) # show warning
4564+ self.ui.join_now_button.clicked()
4565+
4566+ self.assertEqual(self.ui.warning_label.get_text(), '')
4567+
4568+ def test_connect_button_clicked_set_greyed(self):
4569+ """Clicking on 'connect' self is greyed."""
4570+ self.ui.connect_button.clicked()
4571+ self.assertTrue(self.ui.get_property('greyed'), 'Must be greyed.')
4572+
4573+ def test_connect_button_clicked_removes_warning(self):
4574+ """Clicking on 'connect' the warnings are removed."""
4575+ self.ui.on_authorization_denied(gui.U1_APP_NAME) # show warning
4576+ self.ui.connect_button.clicked()
4577+
4578+ self.assertEqual(self.ui.warning_label.get_text(), '')
4579+
4580+ def test_learn_more_button_clicked(self):
4581+ """Test the 'learn more' button callback."""
4582+ self.patch(gui, 'uri_hook', self._set_called)
4583+ self.ui.learn_more_button.clicked()
4584+
4585+ expected = (self.ui.learn_more_button, self.ui.LEARN_MORE_LINK)
4586+ self.assertEqual(self._called, (expected, {}))
4587+
4588+ def test_on_credentials_not_found_unset_greyed(self):
4589+ """Callback 'on_credentials_not_found' unsets the 'greyed' prop."""
4590+ self.ui.connect_button.clicked()
4591+ self.ui.on_credentials_not_found(gui.U1_APP_NAME)
4592+
4593+ self.assertFalse(self.ui.get_property('greyed'), 'Must not be greyed.')
4594+
4595+ def test_on_credentials_error_unset_greyed(self):
4596+ """Callback 'on_credentials_error' unsets the 'greyed' prop."""
4597+ self.ui.connect_button.clicked()
4598+ self.ui.on_credentials_error(gui.U1_APP_NAME, {})
4599+
4600+ self.assertFalse(self.ui.get_property('greyed'), 'Must not be greyed.')
4601+
4602+ def test_on_authorization_denied_unset_greyed(self):
4603+ """Callback 'on_authorization_denied' unsets the 'greyed' prop."""
4604+ self.ui.connect_button.clicked()
4605+ self.ui.on_authorization_denied(gui.U1_APP_NAME)
4606+
4607+ self.assertFalse(self.ui.get_property('greyed'), 'Must not be greyed.')
4608+
4609+ def test_buttons_disabled_when_greyed(self):
4610+ """Buttons should be disabled when widget is greyed."""
4611+ self.ui.set_sensitive(True)
4612+ self.ui.set_property('greyed', True)
4613+
4614+ self.assertFalse(self.ui.join_now_button.is_sensitive())
4615+ self.assertFalse(self.ui.connect_button.is_sensitive())
4616+
4617+ def test_buttons_enabled_when_not_greyed(self):
4618+ """Buttons should be enabled when widget is not greyed."""
4619+ self.ui.set_sensitive(False)
4620+ self.ui.set_property('greyed', False)
4621+
4622+ self.assertTrue(self.ui.join_now_button.is_sensitive())
4623+ self.assertTrue(self.ui.connect_button.is_sensitive())
4624
4625=== modified file 'ubuntuone/controlpanel/tests/__init__.py'
4626--- ubuntuone/controlpanel/tests/__init__.py 2011-02-23 13:57:42 +0000
4627+++ ubuntuone/controlpanel/tests/__init__.py 2011-03-10 03:11:54 +0000
4628@@ -88,6 +88,8 @@
4629 "email": "andrewpz@protocultura.net",
4630 }
4631
4632+
4633+# note that local computer is not first, do not change!
4634 SAMPLE_DEVICES_JSON = """
4635 [
4636 {
4637@@ -108,15 +110,9 @@
4638 ]
4639 """
4640
4641+# note that local computer should be first, do not change!
4642 EXPECTED_DEVICES_INFO = [
4643 {
4644- "device_id": "ComputerABCDEF01234token",
4645- "name": "Ubuntu One @ darkstar",
4646- "type": "Computer",
4647- "is_local": '',
4648- "configurable": '',
4649- },
4650- {
4651 'is_local': 'True',
4652 'configurable': 'True',
4653 'device_id': 'ComputerABCDEF01234-localtoken',
4654@@ -125,7 +121,14 @@
4655 'max_download_speed': '-1',
4656 'max_upload_speed': '-1',
4657 'name': 'Ubuntu One @ localhost',
4658- 'type': 'Computer'
4659+ 'type': 'Computer',
4660+ },
4661+ {
4662+ "device_id": "ComputerABCDEF01234token",
4663+ "name": "Ubuntu One @ darkstar",
4664+ "type": "Computer",
4665+ "is_local": '',
4666+ "configurable": '',
4667 },
4668 {
4669 "device_id": "Phone1000",
4670
4671=== modified file 'ubuntuone/controlpanel/tests/test_backend.py'
4672--- ubuntuone/controlpanel/tests/test_backend.py 2011-02-23 13:57:42 +0000
4673+++ ubuntuone/controlpanel/tests/test_backend.py 2011-03-10 03:11:54 +0000
4674@@ -274,7 +274,7 @@
4675 result = yield self.be.device_is_local(self.local_token)
4676 self.assertTrue(result)
4677
4678- did = EXPECTED_DEVICES_INFO[0]['device_id']
4679+ did = EXPECTED_DEVICES_INFO[-1]['device_id']
4680 result = yield self.be.device_is_local(did)
4681 self.assertFalse(result)
4682

Subscribers

People subscribed via source and target branches

to all changes: