Merge lp:~dobey/ubuntuone-client/update-4-0 into lp:ubuntuone-client/stable-4-0

Proposed by dobey on 2012-09-21
Status: Merged
Merged at revision: 1264
Proposed branch: lp:~dobey/ubuntuone-client/update-4-0
Merge into: lp:ubuntuone-client/stable-4-0
Diff against target: 1376 lines (+1192/-40)
9 files modified
data/ubuntuone-icons.svg (+486/-38)
po/POTFILES.in (+1/-0)
tests/platform/sync_menu/__init__.py (+27/-0)
tests/platform/sync_menu/test_linux.py (+332/-0)
tests/syncdaemon/test_main.py (+14/-0)
ubuntuone/platform/sync_menu/__init__.py (+41/-0)
ubuntuone/platform/sync_menu/common.py (+43/-0)
ubuntuone/platform/sync_menu/linux.py (+234/-0)
ubuntuone/syncdaemon/main.py (+14/-2)
To merge this branch: bzr merge lp:~dobey/ubuntuone-client/update-4-0
Reviewer Review Type Date Requested Status
Brian Curtin (community) 2012-09-21 Approve on 2012-09-21
Review via email: mp+125781@code.launchpad.net

Commit Message

[Diego Sarmentero]

    - Fixed dummy init (LP: #1053775).
    - Adding SyncMenu to u1-client (LP: #1042343).

[Rodney Dawes]

    - Render the 256 app icon first to avoid breaking the installation.
    - Add a 256x256 icon for the app.

[Mike McCracken]

    - Generate MODIFY event after CREATE when we see RENAME from system into watched path. (LP: #1052713)

To post a comment you must log in.
review: Approve
Ubuntu One Auto Pilot (otto-pilot) wrote :
Download full text (261.5 KiB)

The attempt to merge lp:~dobey/ubuntuone-client/update-4-0 into lp:ubuntuone-client/stable-4-0 failed. Below is the output from the failed tests.

/usr/bin/gnome-autogen.sh
checking for autoconf >= 2.53...
  testing autoconf2.50... not found.
  testing autoconf... found 2.69
checking for automake >= 1.10...
  testing automake-1.12... not found.
  testing automake-1.11... found 1.11.5
checking for libtool >= 1.5...
  testing libtoolize... found 2.4.2
checking for intltool >= 0.30...
  testing intltoolize... found 0.50.2
checking for pkg-config >= 0.14.0...
  testing pkg-config... found 0.26
checking for gtk-doc >= 1.0...
  testing gtkdocize... found 1.18
Checking for required M4 macros...
Checking for forbidden M4 macros...
Processing ./configure.ac
Running libtoolize...
libtoolize: putting auxiliary files in `.'.
libtoolize: copying file `./ltmain.sh'
libtoolize: putting macros in AC_CONFIG_MACRO_DIR, `m4'.
libtoolize: copying file `m4/libtool.m4'
libtoolize: copying file `m4/ltoptions.m4'
libtoolize: copying file `m4/ltsugar.m4'
libtoolize: copying file `m4/ltversion.m4'
libtoolize: copying file `m4/lt~obsolete.m4'
Running intltoolize...
Running gtkdocize...
Running aclocal-1.11...
Running autoconf...
Running autoheader...
Running automake-1.11...
Running ./configure --enable-gtk-doc --enable-debug ...
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... /bin/mkdir -p
checking for gawk... no
checking for mawk... mawk
checking whether make sets $(MAKE)... yes
checking how to create a ustar tar archive... gnutar
checking whether make supports nested variables... yes
checking for style of include used by make... GNU
checking for gcc... gcc
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables...
checking whether we are cross compiling... no
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether gcc accepts -g... yes
checking for gcc option to accept ISO C89... none needed
checking dependency style of gcc... gcc3
checking for library containing strerror... none required
checking for gcc... (cached) gcc
checking whether we are using the GNU C compiler... (cached) yes
checking whether gcc accepts -g... (cached) yes
checking for gcc option to accept ISO C89... (cached) none needed
checking dependency style of gcc... (cached) gcc3
checking build system type... x86_64-unknown-linux-gnu
checking host system type... x86_64-unknown-linux-gnu
checking how to print strings... printf
checking for a sed that does not truncate output... /bin/sed
checking for grep that handles long lines and -e... /bin/grep
checking for egrep... /bin/grep -E
checking for fgrep... /bin/grep -F
checking for ld used by gcc... /usr/bin/ld
checking if the linker (/usr/bin/ld) is GNU ld... yes
checking for BSD- or MS-compatible name lister (nm)... /usr/bin/nm -B
checking the name lister (/usr/bin/nm -B) interface... BSD nm
checking whether ln -s works... yes
checking the maximum length of command line arguments... 1572864
checking whether the sh...

1264. By dobey on 2012-09-21

[Diego Sarmentero]

    - Fixed dummy init (LP: #1053775).
    - Adding SyncMenu to u1-client (LP: #1042343).

[Rodney Dawes]

    - Render the 256 app icon first to avoid breaking the installation.
    - Add a 256x256 icon for the app.

[Mike McCracken]

    - Generate MODIFY event after CREATE when we see RENAME from system into watched path. (LP: #1052713)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'data/ubuntuone-icons.svg'
2--- data/ubuntuone-icons.svg 2011-03-09 18:45:00 +0000
3+++ data/ubuntuone-icons.svg 2012-09-21 17:55:37 +0000
4@@ -17,7 +17,7 @@
5 height="600"
6 id="svg11300"
7 sodipodi:version="0.32"
8- inkscape:version="0.48.1 r9760"
9+ inkscape:version="0.48.3.1 r9886"
10 sodipodi:docname="ubuntuone-icons.svg"
11 inkscape:output_extension="org.inkscape.output.svg.inkscape"
12 version="1.0"
13@@ -15080,6 +15080,357 @@
14 id="linearGradient7730"
15 xlink:href="#linearGradient8265-821-176-38-919-66-249-7-7-6"
16 inkscape:collect="always" />
17+ <linearGradient
18+ inkscape:collect="always"
19+ xlink:href="#linearGradient5683"
20+ id="linearGradient5689"
21+ x1="1432"
22+ y1="1256"
23+ x2="1432"
24+ y2="1168"
25+ gradientUnits="userSpaceOnUse" />
26+ <linearGradient
27+ inkscape:collect="always"
28+ id="linearGradient5683">
29+ <stop
30+ style="stop-color:#000000;stop-opacity:1;"
31+ offset="0"
32+ id="stop5685" />
33+ <stop
34+ style="stop-color:#000000;stop-opacity:0;"
35+ offset="1"
36+ id="stop5687" />
37+ </linearGradient>
38+ <filter
39+ color-interpolation-filters="sRGB"
40+ inkscape:collect="always"
41+ id="filter5695">
42+ <feGaussianBlur
43+ inkscape:collect="always"
44+ stdDeviation="3.12"
45+ id="feGaussianBlur5697" />
46+ </filter>
47+ <linearGradient
48+ inkscape:collect="always"
49+ xlink:href="#linearGradient5707"
50+ id="linearGradient5705"
51+ x1="152"
52+ y1="1021.6122"
53+ x2="204"
54+ y2="1021.6122"
55+ gradientUnits="userSpaceOnUse"
56+ spreadMethod="reflect" />
57+ <linearGradient
58+ id="linearGradient5707">
59+ <stop
60+ id="stop5709"
61+ offset="0"
62+ style="stop-color:#000000;stop-opacity:1;" />
63+ <stop
64+ style="stop-color:#000000;stop-opacity:0.81896549;"
65+ offset="0.80000001"
66+ id="stop5713" />
67+ <stop
68+ id="stop5711"
69+ offset="1"
70+ style="stop-color:#000000;stop-opacity:0;" />
71+ </linearGradient>
72+ <filter
73+ color-interpolation-filters="sRGB"
74+ inkscape:collect="always"
75+ id="filter5673"
76+ x="-0.033028841"
77+ width="1.0660577"
78+ y="-0.32714337"
79+ height="1.6542867">
80+ <feGaussianBlur
81+ inkscape:collect="always"
82+ stdDeviation="1.4312498"
83+ id="feGaussianBlur5675" />
84+ </filter>
85+ <filter
86+ inkscape:collect="always"
87+ id="filter5031-2"
88+ color-interpolation-filters="sRGB">
89+ <feGaussianBlur
90+ inkscape:collect="always"
91+ stdDeviation="1.08"
92+ id="feGaussianBlur5033-5" />
93+ </filter>
94+ <linearGradient
95+ inkscape:collect="always"
96+ xlink:href="#linearGradient4910-3"
97+ id="linearGradient5434"
98+ gradientUnits="userSpaceOnUse"
99+ gradientTransform="matrix(2.5712119,0,0,2.571428,-279.9636,-241.35181)"
100+ x1="156.00049"
101+ y1="450.98816"
102+ x2="156.00049"
103+ y2="453" />
104+ <linearGradient
105+ id="linearGradient4910-3">
106+ <stop
107+ offset="0"
108+ style="stop-color:#ffaf85;stop-opacity:1;"
109+ id="stop4912-3" />
110+ <stop
111+ offset="1"
112+ style="stop-color:#ff915b;stop-opacity:1;"
113+ id="stop4914-8" />
114+ </linearGradient>
115+ <linearGradient
116+ inkscape:collect="always"
117+ xlink:href="#linearGradient4933-4"
118+ id="linearGradient5436"
119+ gradientUnits="userSpaceOnUse"
120+ gradientTransform="matrix(2.5712119,0,0,2.571428,-351.9636,-781.71399)"
121+ x1="152.05421"
122+ y1="452.16666"
123+ x2="148.55392"
124+ y2="452.16666" />
125+ <linearGradient
126+ id="linearGradient4933-4">
127+ <stop
128+ id="stop4935-7"
129+ style="stop-color:#ffa87b;stop-opacity:0;"
130+ offset="0" />
131+ <stop
132+ id="stop4937-1"
133+ style="stop-color:#ff7d3e;stop-opacity:1;"
134+ offset="1" />
135+ </linearGradient>
136+ <linearGradient
137+ inkscape:collect="always"
138+ xlink:href="#linearGradient4933-4"
139+ id="linearGradient5439"
140+ gradientUnits="userSpaceOnUse"
141+ gradientTransform="matrix(-2.5712119,0,0,2.571428,583.96358,-241.35181)"
142+ x1="152.05421"
143+ y1="452.16666"
144+ x2="148.55392"
145+ y2="452.16666" />
146+ <linearGradient
147+ id="linearGradient6864">
148+ <stop
149+ id="stop6866"
150+ style="stop-color:#ffa87b;stop-opacity:0;"
151+ offset="0" />
152+ <stop
153+ id="stop6868"
154+ style="stop-color:#ff7d3e;stop-opacity:1;"
155+ offset="1" />
156+ </linearGradient>
157+ <linearGradient
158+ inkscape:collect="always"
159+ xlink:href="#linearGradient4644-104-3-3-6-1-1"
160+ id="linearGradient5442-5"
161+ gradientUnits="userSpaceOnUse"
162+ gradientTransform="matrix(2.5714286,0,0,2.5750001,-280.00001,-243.11285)"
163+ x1="155.04018"
164+ y1="453.00262"
165+ x2="165.61214"
166+ y2="492.4577" />
167+ <linearGradient
168+ id="linearGradient4644-104-3-3-6-1-1">
169+ <stop
170+ offset="0"
171+ style="stop-color:#ff7a35;stop-opacity:1;"
172+ id="stop5237-6-5-1-7-1" />
173+ <stop
174+ offset="1"
175+ style="stop-color:#f0431a;stop-opacity:1;"
176+ id="stop5239-4-6-4-7-8" />
177+ </linearGradient>
178+ <linearGradient
179+ inkscape:collect="always"
180+ xlink:href="#linearGradient8265-821-176-38-919-66-249-7-7-6-5-7"
181+ id="linearGradient5444"
182+ gradientUnits="userSpaceOnUse"
183+ gradientTransform="matrix(2.3446832,0,0,3.6982468,23.617962,361.87484)"
184+ x1="16.626165"
185+ y1="15.298182"
186+ x2="20.054544"
187+ y2="24.627615" />
188+ <linearGradient
189+ id="linearGradient8265-821-176-38-919-66-249-7-7-6-5-7">
190+ <stop
191+ offset="0"
192+ style="stop-color:#ffffff;stop-opacity:1"
193+ id="stop2687-1-9-3-0-1" />
194+ <stop
195+ offset="1"
196+ style="stop-color:#ffffff;stop-opacity:0"
197+ id="stop2689-5-4-9-8-1" />
198+ </linearGradient>
199+ <filter
200+ color-interpolation-filters="sRGB"
201+ inkscape:collect="always"
202+ id="filter5472"
203+ x="-0.13482843"
204+ width="1.2696569"
205+ y="-0.10811005"
206+ height="1.2162201">
207+ <feGaussianBlur
208+ inkscape:collect="always"
209+ stdDeviation="3.4830741"
210+ id="feGaussianBlur5474" />
211+ </filter>
212+ <filter
213+ color-interpolation-filters="sRGB"
214+ inkscape:collect="always"
215+ id="filter5458">
216+ <feGaussianBlur
217+ inkscape:collect="always"
218+ stdDeviation="0.69661483"
219+ id="feGaussianBlur5460" />
220+ </filter>
221+ <linearGradient
222+ inkscape:collect="always"
223+ xlink:href="#linearGradient5105-5"
224+ id="linearGradient5452"
225+ gradientUnits="userSpaceOnUse"
226+ x1="149"
227+ y1="936.36218"
228+ x2="149"
229+ y2="1013.3622" />
230+ <linearGradient
231+ id="linearGradient5105-5">
232+ <stop
233+ style="stop-color:#f5f5f5;stop-opacity:1;"
234+ offset="0"
235+ id="stop5107-0" />
236+ <stop
237+ style="stop-color:#ebebeb;stop-opacity:1;"
238+ offset="1"
239+ id="stop5109-5" />
240+ </linearGradient>
241+ <radialGradient
242+ inkscape:collect="always"
243+ xlink:href="#linearGradient4918-7"
244+ id="radialGradient5456"
245+ gradientUnits="userSpaceOnUse"
246+ gradientTransform="matrix(1,0,0,0.09375,0,347.54687)"
247+ cx="56"
248+ cy="383.5"
249+ fx="56"
250+ fy="383.5"
251+ r="16" />
252+ <linearGradient
253+ inkscape:collect="always"
254+ id="linearGradient4918-7">
255+ <stop
256+ style="stop-color:#ffffff;stop-opacity:1;"
257+ offset="0"
258+ id="stop4920-8" />
259+ <stop
260+ style="stop-color:#ffffff;stop-opacity:0;"
261+ offset="1"
262+ id="stop4922-1" />
263+ </linearGradient>
264+ <clipPath
265+ clipPathUnits="userSpaceOnUse"
266+ id="clipPath5480">
267+ <path
268+ sodipodi:nodetypes="ccsccccccccc"
269+ inkscape:connector-curvature="0"
270+ id="path5482"
271+ d="m 120.99989,936.41601 0.1873,50.81094 c 0,0 -0.26854,21.31825 22.94772,25.81115 13.35265,2.5843 38.86509,-3.408 38.86509,-3.408 l 0,-73.21409 -21,-0.0538 0,54 -20,0 0,-33 -8,0 11.17715,-21 z"
272+ style="fill:#ff00ff;fill-opacity:1;stroke:none" />
273+ </clipPath>
274+ <filter
275+ color-interpolation-filters="sRGB"
276+ inkscape:collect="always"
277+ id="filter5627"
278+ x="-0.10786275"
279+ width="1.2157255"
280+ y="-0.086488038"
281+ height="1.1729761">
282+ <feGaussianBlur
283+ inkscape:collect="always"
284+ stdDeviation="2.7864593"
285+ id="feGaussianBlur5629" />
286+ </filter>
287+ <linearGradient
288+ inkscape:collect="always"
289+ xlink:href="#linearGradient5367"
290+ id="linearGradient5556"
291+ x1="1424"
292+ y1="1028"
293+ x2="1424"
294+ y2="1181.5043"
295+ gradientUnits="userSpaceOnUse" />
296+ <linearGradient
297+ inkscape:collect="always"
298+ id="linearGradient5367">
299+ <stop
300+ style="stop-color:#ffffff;stop-opacity:1;"
301+ offset="0"
302+ id="stop5369" />
303+ <stop
304+ style="stop-color:#ffffff;stop-opacity:0;"
305+ offset="1"
306+ id="stop5371" />
307+ </linearGradient>
308+ <clipPath
309+ clipPathUnits="userSpaceOnUse"
310+ id="clipPath5623">
311+ <rect
312+ ry="12.016669"
313+ y="1037"
314+ x="1316"
315+ height="206"
316+ width="216"
317+ id="rect5625"
318+ style="fill:#ff00ff;fill-opacity:1;stroke:none" />
319+ </clipPath>
320+ <filter
321+ color-interpolation-filters="sRGB"
322+ inkscape:collect="always"
323+ id="filter5585">
324+ <feGaussianBlur
325+ inkscape:collect="always"
326+ stdDeviation="1.1525"
327+ id="feGaussianBlur5587" />
328+ </filter>
329+ <clipPath
330+ clipPathUnits="userSpaceOnUse"
331+ id="clipPath5606">
332+ <path
333+ style="fill:#ff00ff;fill-opacity:1;stroke:none"
334+ d="m 109.15625,918.36217 c -5.15625,0 -7.36925,2.80945 -8.875,5.0625 -1.07965,1.6155 -1.94945,2.5964 -2.20315,5.0469 0.4327,-2.8977 2.9156,-5.1094 5.9375,-5.1094 l 95.9688,0 c 3.0219,0 5.5048,2.2117 5.9375,5.1094 -0.2535,-2.4505 -1.1235,-3.4314 -2.20315,-5.0469 -1.50575,-2.25305 -3.71875,-5.0625 -8.875,-5.0625 l -42.84375,0 -42.84375,0 z"
335+ id="path5608"
336+ inkscape:connector-curvature="0" />
337+ </clipPath>
338+ <filter
339+ color-interpolation-filters="sRGB"
340+ inkscape:collect="always"
341+ id="filter5614">
342+ <feGaussianBlur
343+ inkscape:collect="always"
344+ stdDeviation="0.5275"
345+ id="feGaussianBlur5616" />
346+ </filter>
347+ <clipPath
348+ clipPathUnits="userSpaceOnUse"
349+ id="clipPath5651">
350+ <rect
351+ style="fill:#ff00ff;fill-opacity:1;stroke:none"
352+ id="rect5653"
353+ width="216"
354+ height="206"
355+ x="1316"
356+ y="1039"
357+ ry="12.016669" />
358+ </clipPath>
359+ <filter
360+ color-interpolation-filters="sRGB"
361+ inkscape:collect="always"
362+ id="filter5655">
363+ <feGaussianBlur
364+ inkscape:collect="always"
365+ stdDeviation="0.620078"
366+ id="feGaussianBlur5657" />
367+ </filter>
368 </defs>
369 <sodipodi:namedview
370 stroke="#ef2929"
371@@ -15091,17 +15442,17 @@
372 inkscape:pageopacity="0.0"
373 inkscape:pageshadow="2"
374 inkscape:zoom="0.99999996"
375- inkscape:cx="789.01509"
376- inkscape:cy="352.58651"
377- inkscape:current-layer="layer1"
378+ inkscape:cx="828.5984"
379+ inkscape:cy="285.52011"
380+ inkscape:current-layer="svg11300"
381 showgrid="false"
382 inkscape:grid-bbox="true"
383 inkscape:document-units="px"
384 inkscape:showpageshadow="false"
385 inkscape:window-width="2048"
386- inkscape:window-height="1098"
387+ inkscape:window-height="1106"
388 inkscape:window-x="0"
389- inkscape:window-y="26"
390+ inkscape:window-y="24"
391 width="400px"
392 height="300px"
393 inkscape:snap-nodes="false"
394@@ -15240,14 +15591,6 @@
395 x="703"
396 y="519"
397 inkscape:label="16x16" />
398- <rect
399- style="fill:#eeeeec;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
400- id="rect6409-8"
401- width="256"
402- height="256"
403- x="420"
404- y="330"
405- inkscape:label="256x256" />
406 <text
407 xml:space="preserve"
408 style="font-size:18.30070686px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;display:inline;enable-background:new;font-family:Bitstream Vera Sans"
409@@ -16775,14 +17118,6 @@
410 x="1523"
411 y="219"
412 inkscape:label="16x16" />
413- <rect
414- style="fill:#eeeeec;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
415- id="rect8150"
416- width="256"
417- height="256"
418- x="1240"
419- y="30"
420- inkscape:label="256x256" />
421 <text
422 xml:space="preserve"
423 style="font-size:18.30070686px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;display:inline;enable-background:new;font-family:Bitstream Vera Sans"
424@@ -17631,14 +17966,6 @@
425 x="1103"
426 y="214"
427 inkscape:label="16x16" />
428- <rect
429- style="fill:#eeeeec;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
430- id="rect3508-6"
431- width="256"
432- height="256"
433- x="820"
434- y="25"
435- inkscape:label="256x256" />
436 <text
437 xml:space="preserve"
438 style="font-size:18.30070686px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;display:inline;enable-background:new;font-family:Bitstream Vera Sans"
439@@ -18502,14 +18829,6 @@
440 x="703"
441 y="219"
442 inkscape:label="16x16" />
443- <rect
444- style="fill:#eeeeec;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
445- id="rect3508"
446- width="256"
447- height="256"
448- x="420"
449- y="30"
450- inkscape:label="256x256" />
451 <text
452 xml:space="preserve"
453 style="font-size:18.30070686px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;display:inline;enable-background:new;font-family:Bitstream Vera Sans"
454@@ -18986,4 +19305,133 @@
455 inkscape:connector-curvature="0" />
456 </g>
457 </g>
458+ <g
459+ transform="matrix(2,0,0,2,-156,-1786.7242)"
460+ inkscape:export-ydpi="90"
461+ inkscape:export-xdpi="90"
462+ inkscape:export-filename="../04 - Review/Icons/128/ubuntuone@2x.png"
463+ id="g5384">
464+ <path
465+ style="opacity:0.1;color:#000000;fill:url(#linearGradient5689);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter5695);enable-background:accumulate"
466+ d="m 1312,1168 4,68 c 0,19.8533 48.3532,20 108,20 59.6468,0 108,-0.356 108,-20 l 4,-68 -224,0 z"
467+ transform="matrix(0.5,0,0,0.5,-560,404.36217)"
468+ id="path5677"
469+ inkscape:connector-curvature="0" />
470+ <rect
471+ style="opacity:0.5;color:#000000;fill:url(#linearGradient5705);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter5673);enable-background:accumulate"
472+ id="rect5659"
473+ width="104"
474+ height="10.499982"
475+ x="100"
476+ y="1016.3622"
477+ ry="5.2499909" />
478+ <path
479+ inkscape:connector-curvature="0"
480+ id="path5394"
481+ transform="translate(72,540.36218)"
482+ d="m 37.15625,378 c -5.15625,0 -7.36925,2.80946 -8.875,5.0625 -1.024771,1.53336 -1.856706,2.49909 -2.15625,4.6875 -0.04297,0.20564 -0.07226,0.44333 -0.09375,0.65625 -8.67e-4,0.0111 8.44e-4,0.0201 0,0.0312 C 26.012876,388.62965 26,388.80288 26,389 l 0,0.71875 L 26,480 c 0,3.32862 2.671383,6 6,6 l 96,0 c 3.32862,0 6,-2.67138 6,-6 L 134,389.71875 134,389 c 0,-0.19712 -0.0129,-0.37035 -0.0312,-0.5625 -0.0189,-0.24996 -0.0633,-0.46498 -0.0937,-0.6875 -0.29954,-2.18841 -1.13148,-3.15414 -2.15625,-4.6875 C 130.213,380.80946 128,378 122.84375,378 L 80,378 37.15625,378 z"
483+ style="opacity:0.2;fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter5031-2)" />
484+ <path
485+ inkscape:connector-curvature="0"
486+ id="path5396"
487+ d="m 109.15625,918.36218 c -5.15625,0 -7.36925,2.80946 -8.875,5.0625 -1.29025,1.93059 -2.285796,2.94195 -2.28125,6.65625 l 54,-1 54,1 c 0.005,-3.7143 -0.991,-4.72566 -2.28125,-6.65625 -1.50575,-2.25304 -3.71875,-5.0625 -8.875,-5.0625 l -42.84375,0 -42.84375,0 z"
488+ style="fill:url(#linearGradient5434);fill-opacity:1;stroke:none" />
489+ <path
490+ inkscape:connector-curvature="0"
491+ id="path5398"
492+ transform="translate(72,540.36218)"
493+ d="M 37.15625,378 C 32,378 29.787,380.80946 28.28125,383.0625 26.991,384.99309 25.995454,386.00445 26,389.71875 l 14,-0.25 L 40,378 l -2.84375,0 z"
494+ style="fill:url(#linearGradient5436);fill-opacity:1;stroke:none" />
495+ <path
496+ inkscape:connector-curvature="0"
497+ style="fill:url(#linearGradient5439);fill-opacity:1;stroke:none"
498+ d="m 194.84373,918.36218 c 5.15625,0 7.36925,2.80946 8.875,5.0625 1.29025,1.93059 2.2858,2.94195 2.28125,6.65625 l -14,-0.25 0,-11.46875 2.84375,0 z"
499+ id="path5400" />
500+ <rect
501+ style="fill:url(#linearGradient5442-5);fill-opacity:1;stroke:none"
502+ id="rect5402"
503+ width="108"
504+ height="103"
505+ x="98"
506+ y="923.36218"
507+ ry="6.0083346" />
508+ <path
509+ inkscape:connector-curvature="0"
510+ id="path5404"
511+ transform="translate(72,540.36218)"
512+ d="m 32,383 c -3.328617,0 -6,2.67138 -6,6 l 0.03125,62.21875 C 29.13816,451.15075 131.64179,426.08119 134,424.875 L 134,389 c 0,-3.32862 -2.67138,-6 -6,-6 l -96,0 z"
513+ style="opacity:0.2;fill:url(#linearGradient5444);fill-opacity:1;fill-rule:evenodd;stroke:none;display:inline;enable-background:new" />
514+ <path
515+ sodipodi:nodetypes="ccsccccccccc"
516+ inkscape:connector-curvature="0"
517+ id="path5466"
518+ d="m 120.99989,937.41601 0.1873,50.81094 c 0,0 -0.26854,21.31825 22.94772,25.81115 13.35265,2.5843 38.86509,-3.408 38.86509,-3.408 l 0,-73.21409 -21,-0.0538 0,54 -20,0 0,-33 -8,0 11.17715,-21 z"
519+ style="opacity:0.1;fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter5472)" />
520+ <path
521+ style="opacity:0.3;fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter5458)"
522+ d="m 120.99989,937.41601 0.1873,50.81094 c 0,0 -0.26854,21.31825 22.94772,25.81115 13.35265,2.5843 38.86509,-3.408 38.86509,-3.408 l 0,-73.21409 -21,-0.0538 0,54 -20,0 0,-33 -8,0 11.17715,-21 z"
523+ id="path5413"
524+ inkscape:connector-curvature="0"
525+ sodipodi:nodetypes="ccsccccccccc" />
526+ <path
527+ style="fill:#ffffff;fill-opacity:1;stroke:none"
528+ d="m 121.99989,935.41601 -0.99652,0.94346 0.18382,49.86748 c 0,0 -0.26854,21.31825 22.94772,25.81115 13.35265,2.5843 38.86509,-3.408 38.86509,-3.408 l 0,-72.24441 -1,-0.96968 -19.8,-0.0538 -0.2,1.02348 0,52.97652 -20,0 0,-32.03302 -0.1005,-0.96698 -7.8995,0 11.178,-20.06159 -0.1015,-0.93841 z"
529+ id="path5415"
530+ inkscape:connector-curvature="0"
531+ sodipodi:nodetypes="cccsccccccccccccc" />
532+ <path
533+ sodipodi:nodetypes="ccsccccccccc"
534+ inkscape:connector-curvature="0"
535+ id="path5417"
536+ d="m 120.99989,936.41601 0.1873,50.81094 c 0,0 -0.26854,21.31825 22.94772,25.81115 13.35265,2.5843 38.86509,-3.408 38.86509,-3.408 l 0,-73.21409 -21,-0.0538 0,54 -20,0 0,-33 -8,0 11.17715,-21 z"
537+ style="fill:url(#linearGradient5452);fill-opacity:1;stroke:none" />
538+ <path
539+ transform="matrix(1.75,0,0,1,42,539.86218)"
540+ d="m 72,383.5 c 0,0.82843 -7.163444,1.5 -16,1.5 -8.836556,0 -16,-0.67157 -16,-1.5 0,-0.82843 7.163444,-1.5 16,-1.5 8.836556,0 16,0.67157 16,1.5 z"
541+ sodipodi:ry="1.5"
542+ sodipodi:rx="16"
543+ sodipodi:cy="383.5"
544+ sodipodi:cx="56"
545+ id="path5422"
546+ style="opacity:0.6;color:#000000;fill:url(#radialGradient5456);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.25;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
547+ sodipodi:type="arc" />
548+ <rect
549+ y="908.36212"
550+ x="88"
551+ height="128"
552+ width="128"
553+ id="rect5426"
554+ style="opacity:0.5;color:#000000;fill:none;stroke:none;stroke-width:1.25;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
555+ <path
556+ style="opacity:0.2;fill:none;stroke:#999999;filter:url(#filter5627)"
557+ d="m 120.99989,936.41601 0.1873,50.81094 c 0,0 -0.26854,21.31825 22.94772,25.81115 13.35265,2.5843 38.86509,-3.408 38.86509,-3.408 l 0,-73.21409 -21,-0.0538 0,54 -20,0 0,-33 -8,0 11.17715,-21 z"
558+ id="path5476"
559+ inkscape:connector-curvature="0"
560+ sodipodi:nodetypes="ccsccccccccc"
561+ clip-path="url(#clipPath5480)" />
562+ <path
563+ style="opacity:0.3;fill:url(#linearGradient5556);fill-opacity:1;stroke:none;filter:url(#filter5585)"
564+ d="m 1328,1028.2812 c -11.8577,0 -21.75,9.8924 -21.75,21.75 l 0,181.9376 c 0,11.8576 9.8316,21.8125 21.75,21.8124 l 192,0 c 11.9184,0 21.75,-9.9548 21.75,-21.8124 l 0,-181.9376 c 0,-11.8576 -9.8923,-21.75 -21.75,-21.75 z m 0.5314,9.7188 190.937,0 c 6.6572,0 12.0312,5.374 12.0312,12.0312 l 0,181.9376 c 0,6.6572 -5.374,12.0312 -12.0312,12.0312 l -190.937,0 c -6.657,0 -12.031,-5.374 -12.031,-12.0312 l 0,-181.9376 c 0,-6.6572 5.374,-12.0312 12.0312,-12.0312 z"
565+ transform="matrix(0.5,0,0,0.5,-560,404.86217)"
566+ id="path5539"
567+ clip-path="url(#clipPath5623)"
568+ inkscape:connector-curvature="0"
569+ sodipodi:nodetypes="ssssssssscssssssscc" />
570+ <rect
571+ ry="6.0083346"
572+ y="923.36218"
573+ x="98"
574+ height="103"
575+ width="108"
576+ id="rect5595"
577+ style="fill:#ffffff;fill-opacity:1;stroke:none;filter:url(#filter5614)"
578+ clip-path="url(#clipPath5606)" />
579+ <path
580+ style="opacity:0.3;fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter5655)"
581+ d="m 1316,1231.9688 0,20 c 0,6.6572 5.374,12.0312 12.0312,12.0312 l 191.9376,0 c 6.6572,0 12.0312,-5.374 12.0312,-12.0312 l 0,-20 c 0,6.6572 -5.374,12.0312 -12.0312,12.0312 l -191.9376,0 C 1321.374,1244 1316,1238.626 1316,1231.9688 z"
582+ transform="matrix(0.5,0,0,0.5,-560,403.86217)"
583+ id="rect5633"
584+ clip-path="url(#clipPath5651)"
585+ inkscape:connector-curvature="0" />
586+ </g>
587 </svg>
588
589=== modified file 'po/POTFILES.in'
590--- po/POTFILES.in 2011-08-16 13:44:20 +0000
591+++ po/POTFILES.in 2012-09-21 17:55:37 +0000
592@@ -1,6 +1,7 @@
593 ubuntuone/clientdefs.py.in
594 ubuntuone/status/aggregator.py
595 ubuntuone/platform/credentials/__init__.py
596+ubuntuone/platform/sync_menu/linux.py
597 data/emblem-ubuntuone-downloading.icon.in
598 data/emblem-ubuntuone-unsynchronized.icon.in
599 data/emblem-ubuntuone-uploading.icon.in
600
601=== added directory 'tests/platform/sync_menu'
602=== added file 'tests/platform/sync_menu/__init__.py'
603--- tests/platform/sync_menu/__init__.py 1970-01-01 00:00:00 +0000
604+++ tests/platform/sync_menu/__init__.py 2012-09-21 17:55:37 +0000
605@@ -0,0 +1,27 @@
606+# Copyright 2012 Canonical Ltd.
607+#
608+# This program is free software: you can redistribute it and/or modify it
609+# under the terms of the GNU General Public License version 3, as published
610+# by the Free Software Foundation.
611+#
612+# This program is distributed in the hope that it will be useful, but
613+# WITHOUT ANY WARRANTY; without even the implied warranties of
614+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
615+# PURPOSE. See the GNU General Public License for more details.
616+#
617+# You should have received a copy of the GNU General Public License along
618+# with this program. If not, see <http://www.gnu.org/licenses/>.
619+#
620+# In addition, as a special exception, the copyright holders give
621+# permission to link the code of portions of this program with the
622+# OpenSSL library under certain conditions as described in each
623+# individual source file, and distribute linked combinations
624+# including the two.
625+# You must obey the GNU General Public License in all respects
626+# for all of the code used other than OpenSSL. If you modify
627+# file(s) with this exception, you may extend this exception to your
628+# version of the file(s), but you are not obligated to do so. If you
629+# do not wish to do so, delete this exception statement from your
630+# version. If you delete this exception statement from all source
631+# files in the program, then also delete it here.
632+"""SyncMenu test code."""
633
634=== added file 'tests/platform/sync_menu/test_linux.py'
635--- tests/platform/sync_menu/test_linux.py 1970-01-01 00:00:00 +0000
636+++ tests/platform/sync_menu/test_linux.py 2012-09-21 17:55:37 +0000
637@@ -0,0 +1,332 @@
638+# -*- coding: utf-8 *-*
639+#
640+# Copyright 2012 Canonical Ltd.
641+#
642+# This program is free software: you can redistribute it and/or modify it
643+# under the terms of the GNU General Public License version 3, as published
644+# by the Free Software Foundation.
645+#
646+# This program is distributed in the hope that it will be useful, but
647+# WITHOUT ANY WARRANTY; without even the implied warranties of
648+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
649+# PURPOSE. See the GNU General Public License for more details.
650+#
651+# You should have received a copy of the GNU General Public License along
652+# with this program. If not, see <http://www.gnu.org/licenses/>.
653+#
654+# In addition, as a special exception, the copyright holders give
655+# permission to link the code of portions of this program with the
656+# OpenSSL library under certain conditions as described in each
657+# individual source file, and distribute linked combinations
658+# including the two.
659+# You must obey the GNU General Public License in all respects
660+# for all of the code used other than OpenSSL. If you modify
661+# file(s) with this exception, you may extend this exception to your
662+# version of the file(s), but you are not obligated to do so. If you
663+# do not wish to do so, delete this exception statement from your
664+# version. If you delete this exception statement from all source
665+# files in the program, then also delete it here.
666+"""Test the Sync Menu."""
667+
668+from collections import Callable
669+
670+from twisted.internet import defer
671+from twisted.trial.unittest import TestCase
672+
673+from ubuntuone.platform import sync_menu
674+from ubuntuone.platform.sync_menu import linux
675+
676+
677+def fake_call_later(*args):
678+ """Fake reactor.callLater."""
679+
680+
681+class FakeStatusFrontend(object):
682+ """Fake StatusFrontend."""
683+
684+ def __init__(self):
685+ self.recent_transfers_data = []
686+ self.uploading_data = []
687+
688+ def recent_transfers(self):
689+ """Return the fake recent transfers files."""
690+ return self.recent_transfers_data
691+
692+ def files_uploading(self):
693+ """Return the fake files being upload."""
694+ return self.uploading_data
695+
696+
697+class FakeStatusListener(object):
698+ """Fake StatusListener."""
699+
700+ def __init__(self):
701+ self.status_frontend = FakeStatusFrontend()
702+
703+
704+class FakeMain(object):
705+ """Fake Main."""
706+
707+ def __init__(self):
708+ self.status_listener = FakeStatusListener()
709+
710+
711+class FakeSyncMenuApp(object):
712+ """Fake SyncMenu."""
713+
714+ data = {}
715+
716+ @classmethod
717+ def new(cls, *args):
718+ return FakeSyncMenuApp()
719+
720+ @classmethod
721+ def clean(cls):
722+ """Clear the values stored in data."""
723+
724+ def set_menu(self, server):
725+ """Set the menu for SyncMenu App."""
726+ self.data['server'] = server
727+
728+ def connect(self, signal, callback):
729+ """Fake connect."""
730+ self.data['connect'] = (signal, callback)
731+
732+
733+class SyncMenuDummyTestCase(TestCase):
734+ """Test the SyncMenu."""
735+
736+ def test_dummy_support(self):
737+ """Check that the Dummy object can be created properly."""
738+ dummy = linux.DummySyncMenu('random', 'args')
739+ self.assertIsInstance(dummy, linux.DummySyncMenu)
740+
741+ def test_dummy_has_start_timer(self):
742+ """Check that the dummy has the proper methods required by the API."""
743+ dummy = linux.DummySyncMenu('random', 'args')
744+ self.assertIsInstance(dummy.start_timer, Callable)
745+
746+
747+class SyncMenuTestCase(TestCase):
748+ """Test the SyncMenu."""
749+
750+ skip = None if linux.use_syncmenu else "SyncMenu not installed."
751+
752+ @defer.inlineCallbacks
753+ def setUp(self):
754+ yield super(SyncMenuTestCase, self).setUp()
755+ self.patch(linux.SyncMenu, "App", FakeSyncMenuApp)
756+ FakeSyncMenuApp.clean()
757+ self.main = FakeMain()
758+ self.status_frontend = self.main.status_listener.status_frontend
759+ self._paused = False
760+ self.patch(sync_menu.UbuntuOneSyncMenu, "change_sync_status",
761+ self._change_sync_status)
762+ self.sync_menu = sync_menu.UbuntuOneSyncMenu(self.main)
763+
764+ def _change_sync_status(self, *args):
765+ """Fake change_sync_status."""
766+ if self._paused:
767+ self._paused = False
768+ else:
769+ self._paused = True
770+
771+ def test_init(self):
772+ """Check that the menu is properly initialized."""
773+ self.assertIsInstance(FakeSyncMenuApp.data['server'],
774+ linux.Dbusmenu.Server)
775+ self.assertEqual(self.sync_menu.open_u1.get_parent(),
776+ self.sync_menu.root_menu)
777+ self.assertEqual(self.sync_menu.go_to_web.get_parent(),
778+ self.sync_menu.root_menu)
779+ self.assertEqual(self.sync_menu.more_storage.get_parent(),
780+ self.sync_menu.root_menu)
781+ self.assertEqual(self.sync_menu.get_help.get_parent(),
782+ self.sync_menu.root_menu)
783+ self.assertEqual(self.sync_menu.transfers.get_parent(),
784+ self.sync_menu.root_menu)
785+
786+ self.assertEqual(self.sync_menu.open_u1.property_get(
787+ linux.Dbusmenu.MENUITEM_PROP_LABEL), linux.OPEN_U1)
788+ self.assertEqual(self.sync_menu.go_to_web.property_get(
789+ linux.Dbusmenu.MENUITEM_PROP_LABEL), linux.GO_TO_WEB)
790+ self.assertEqual(self.sync_menu.transfers.property_get(
791+ linux.Dbusmenu.MENUITEM_PROP_LABEL), linux.TRANSFERS)
792+ self.assertEqual(self.sync_menu.more_storage.property_get(
793+ linux.Dbusmenu.MENUITEM_PROP_LABEL), linux.MORE_STORAGE)
794+ self.assertEqual(self.sync_menu.get_help.property_get(
795+ linux.Dbusmenu.MENUITEM_PROP_LABEL), linux.GET_HELP)
796+
797+ self.assertEqual(self.sync_menu.app.data['connect'],
798+ ("notify::paused", self.sync_menu.change_sync_status))
799+ self.sync_menu.app.data['connect'][1]()
800+ self.assertTrue(self._paused)
801+ self.sync_menu.app.data['connect'][1]()
802+ self.assertFalse(self._paused)
803+
804+ def test_open_u1(self):
805+ """Check that the proper action is executed."""
806+ data = []
807+
808+ self.patch(linux.glib, "spawn_command_line_async", data.append)
809+ self.sync_menu.open_control_panel()
810+ self.assertEqual(data, ['ubuntuone-installer'])
811+
812+ def test_go_to_web(self):
813+ """Check that the proper action is executed."""
814+ data = []
815+
816+ self.patch(linux.webbrowser, "open", data.append)
817+ self.sync_menu.open_go_to_web()
818+ self.assertEqual(data, [linux.DASHBOARD])
819+
820+ def test_get_help(self):
821+ """Check that the proper action is executed."""
822+ data = []
823+
824+ self.patch(linux.webbrowser, "open", data.append)
825+ self.sync_menu.open_web_help()
826+ self.assertEqual(data, [linux.HELP_LINK])
827+
828+ def test_more_storage(self):
829+ """Check that the proper action is executed."""
830+ data = []
831+
832+ self.patch(linux.webbrowser, "open", data.append)
833+ self.sync_menu.open_get_more_storage()
834+ self.assertEqual(data, [linux.GET_STORAGE_LINK])
835+
836+ def test_empty_transfers(self):
837+ """Check that the Transfers menu is empty."""
838+ self.assertEqual(self.sync_menu.transfers.get_children(), [])
839+
840+ def test_only_recent(self):
841+ """Check that only recent transfers items are loaded."""
842+ data = ['file1', 'file2', 'file3']
843+ self.status_frontend.recent_transfers_data = data
844+ self.sync_menu.transfers.update_progress()
845+ children = self.sync_menu.transfers.get_children()
846+ self.assertEqual(len(children), 3)
847+ data.reverse()
848+ for itemM, itemD in zip(children, data):
849+ self.assertEqual(itemM.property_get(
850+ linux.Dbusmenu.MENUITEM_PROP_LABEL), itemD)
851+
852+ def test_only_progress(self):
853+ """Check that only progress items are loaded."""
854+ data = [
855+ ('file1', 3000, 400),
856+ ('file2', 2000, 100),
857+ ('file3', 5000, 4600)]
858+ uploading_data = {}
859+ for filename, size, written in data:
860+ uploading_data[filename] = (size, written)
861+ self.status_frontend.uploading_data = data
862+ self.sync_menu.transfers.update_progress()
863+ children = self.sync_menu.transfers.get_children()
864+ self.assertEqual(len(children), 3)
865+ data.reverse()
866+ for item in children:
867+ text = item.property_get(linux.Dbusmenu.MENUITEM_PROP_LABEL)
868+ self.assertIn(text, uploading_data)
869+ size, written = uploading_data[text]
870+ percentage = written * 100 / size
871+ self.assertEqual(item.property_get_int(
872+ linux.SyncMenu.PROGRESS_MENUITEM_PROP_PERCENT_DONE),
873+ percentage)
874+
875+ def test_full_transfers(self):
876+ """Check that the transfers menu contains the maximum transfers."""
877+ # The api of recent transfers always returns a maximum of 5 items
878+ data_recent = ['file1', 'file2', 'file3', 'file4', 'file5']
879+ self.status_frontend.recent_transfers_data = \
880+ data_recent
881+ self.sync_menu.transfers.update_progress()
882+ children = self.sync_menu.transfers.get_children()
883+ self.assertEqual(len(children), 5)
884+ data_recent.reverse()
885+ for itemM, itemD in zip(children, data_recent):
886+ self.assertEqual(itemM.property_get(
887+ linux.Dbusmenu.MENUITEM_PROP_LABEL), itemD)
888+
889+ data_current = [
890+ ('file0', 1200, 600),
891+ ('file1', 3000, 400),
892+ ('file2', 2000, 100),
893+ ('file3', 2500, 150),
894+ ('file4', 1000, 600),
895+ ('file5', 5000, 4600)]
896+ uploading_data = {}
897+ for filename, size, written in data_current:
898+ uploading_data[filename] = (size, written)
899+ self.status_frontend.uploading_data = data_current
900+ self.sync_menu.transfers.update_progress()
901+ children = self.sync_menu.transfers.get_children()
902+ # The menu should only show 5 current transfers.
903+ self.assertEqual(len(children), 10)
904+ data_current.reverse()
905+ for item in children[5:]:
906+ text = item.property_get(linux.Dbusmenu.MENUITEM_PROP_LABEL)
907+ self.assertIn(text, uploading_data)
908+ size, written = uploading_data[text]
909+ percentage = written * 100 / size
910+ self.assertEqual(item.property_get_int(
911+ linux.SyncMenu.PROGRESS_MENUITEM_PROP_PERCENT_DONE),
912+ percentage)
913+
914+ def test_update_transfers(self):
915+ """Check that everything is ok when updating the transfers value."""
916+ data_current = [
917+ ('file0', 1200, 600),
918+ ('file1', 3000, 400),
919+ ('file4', 1000, 600),
920+ ('file5', 5000, 4600)]
921+ uploading_data = {}
922+ for filename, size, written in data_current:
923+ uploading_data[filename] = (size, written)
924+ self.status_frontend.uploading_data = data_current
925+ self.sync_menu.transfers.update_progress()
926+ children = self.sync_menu.transfers.get_children()
927+ # The menu should only show 5 current transfers.
928+ self.assertEqual(len(children), 4)
929+ data_current.reverse()
930+ for item in children:
931+ text = item.property_get(linux.Dbusmenu.MENUITEM_PROP_LABEL)
932+ self.assertIn(text, uploading_data)
933+ size, written = uploading_data[text]
934+ percentage = written * 100 / size
935+ self.assertEqual(item.property_get_int(
936+ linux.SyncMenu.PROGRESS_MENUITEM_PROP_PERCENT_DONE),
937+ percentage)
938+
939+ data_recent = ['file5']
940+ self.status_frontend.recent_transfers_data = data_recent
941+ self.sync_menu.transfers.update_progress()
942+ children = self.sync_menu.transfers.get_children()
943+ self.assertEqual(len(children), 5)
944+ data_recent.reverse()
945+ for itemM, itemD in zip(children, data_recent):
946+ self.assertEqual(itemM.property_get(
947+ linux.Dbusmenu.MENUITEM_PROP_LABEL), itemD)
948+
949+ data_current = [
950+ ('file0', 1200, 700),
951+ ('file1', 3000, 600),
952+ ('file4', 1000, 800)]
953+ uploading_data = {}
954+ for filename, size, written in data_current:
955+ uploading_data[filename] = (size, written)
956+ self.status_frontend.uploading_data = data_current
957+ self.sync_menu.transfers.update_progress()
958+ children = self.sync_menu.transfers.get_children()
959+ # The menu should only show 5 current transfers.
960+ self.assertEqual(len(children), 4)
961+ data_current.reverse()
962+ for item in children[5:]:
963+ text = item.property_get(linux.Dbusmenu.MENUITEM_PROP_LABEL)
964+ self.assertIn(text, uploading_data)
965+ size, written = uploading_data[text]
966+ percentage = written * 100 / size
967+ self.assertEqual(item.property_get_int(
968+ linux.SyncMenu.PROGRESS_MENUITEM_PROP_PERCENT_DONE),
969+ percentage)
970
971=== modified file 'tests/syncdaemon/test_main.py'
972--- tests/syncdaemon/test_main.py 2012-06-21 18:58:50 +0000
973+++ tests/syncdaemon/test_main.py 2012-09-21 17:55:37 +0000
974@@ -70,6 +70,19 @@
975 return defer.succeed(None)
976
977
978+class FakedSyncMenu(object):
979+ """Do nothing."""
980+
981+ def __init__(self, *args, **kwargs):
982+ self.arguments = (args, kwargs)
983+
984+ def update_progress(self):
985+ """Do nothing."""
986+
987+ def start_timer(self):
988+ """Do nothing."""
989+
990+
991 class MainTests(BaseTwistedTestCase):
992 """ Basic tests to check main.Main """
993
994@@ -87,6 +100,7 @@
995 self.patch(main_mod.event_logging, "get_listener", lambda *a: None)
996 # no status listener by default
997 self.patch(main_mod.status_listener, "get_listener", lambda *a: None)
998+ self.patch(main_mod.sync_menu, "UbuntuOneSyncMenu", FakedSyncMenu)
999
1000 self.handler = MementoHandler()
1001 self.handler.setLevel(logging.DEBUG)
1002
1003=== added directory 'ubuntuone/platform/sync_menu'
1004=== added file 'ubuntuone/platform/sync_menu/__init__.py'
1005--- ubuntuone/platform/sync_menu/__init__.py 1970-01-01 00:00:00 +0000
1006+++ ubuntuone/platform/sync_menu/__init__.py 2012-09-21 17:55:37 +0000
1007@@ -0,0 +1,41 @@
1008+# -*- coding: utf-8 *-*
1009+#
1010+# Copyright 2012 Canonical Ltd.
1011+#
1012+# This program is free software: you can redistribute it and/or modify it
1013+# under the terms of the GNU General Public License version 3, as published
1014+# by the Free Software Foundation.
1015+#
1016+# This program is distributed in the hope that it will be useful, but
1017+# WITHOUT ANY WARRANTY; without even the implied warranties of
1018+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1019+# PURPOSE. See the GNU General Public License for more details.
1020+#
1021+# You should have received a copy of the GNU General Public License along
1022+# with this program. If not, see <http://www.gnu.org/licenses/>.
1023+#
1024+# In addition, as a special exception, the copyright holders give
1025+# permission to link the code of portions of this program with the
1026+# OpenSSL library under certain conditions as described in each
1027+# individual source file, and distribute linked combinations
1028+# including the two.
1029+# You must obey the GNU General Public License in all respects
1030+# for all of the code used other than OpenSSL. If you modify
1031+# file(s) with this exception, you may extend this exception to your
1032+# version of the file(s), but you are not obligated to do so. If you
1033+# do not wish to do so, delete this exception statement from your
1034+# version. If you delete this exception statement from all source
1035+# files in the program, then also delete it here.
1036+"""Use SyncMenu lib to integrate U1 with the Systray Sync Icon."""
1037+
1038+import sys
1039+
1040+
1041+if sys.platform in ("win32", "darwin"):
1042+ from ubuntuone.platform.sync_menu import common
1043+ source = common
1044+else:
1045+ from ubuntuone.platform.sync_menu import linux
1046+ source = linux
1047+
1048+UbuntuOneSyncMenu = source.UbuntuOneSyncMenu
1049
1050=== added file 'ubuntuone/platform/sync_menu/common.py'
1051--- ubuntuone/platform/sync_menu/common.py 1970-01-01 00:00:00 +0000
1052+++ ubuntuone/platform/sync_menu/common.py 2012-09-21 17:55:37 +0000
1053@@ -0,0 +1,43 @@
1054+# -*- coding: utf-8 *-*
1055+#
1056+# Copyright 2012 Canonical Ltd.
1057+#
1058+# This program is free software: you can redistribute it and/or modify it
1059+# under the terms of the GNU General Public License version 3, as published
1060+# by the Free Software Foundation.
1061+#
1062+# This program is distributed in the hope that it will be useful, but
1063+# WITHOUT ANY WARRANTY; without even the implied warranties of
1064+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1065+# PURPOSE. See the GNU General Public License for more details.
1066+#
1067+# You should have received a copy of the GNU General Public License along
1068+# with this program. If not, see <http://www.gnu.org/licenses/>.
1069+#
1070+# In addition, as a special exception, the copyright holders give
1071+# permission to link the code of portions of this program with the
1072+# OpenSSL library under certain conditions as described in each
1073+# individual source file, and distribute linked combinations
1074+# including the two.
1075+# You must obey the GNU General Public License in all respects
1076+# for all of the code used other than OpenSSL. If you modify
1077+# file(s) with this exception, you may extend this exception to your
1078+# version of the file(s), but you are not obligated to do so. If you
1079+# do not wish to do so, delete this exception statement from your
1080+# version. If you delete this exception statement from all source
1081+# files in the program, then also delete it here.
1082+"""Use SyncMenu lib to integrate U1 with the Systray Sync Icon."""
1083+
1084+
1085+class UbuntuOneSyncMenu(object):
1086+ """Integrate U1 with the Ubuntu Sync Menu."""
1087+
1088+ def __init__(self, *args):
1089+ self.transfers = DummyTransfersMenu()
1090+
1091+
1092+class DummyTransfersMenu(object):
1093+ """Dummy Transfers Menu."""
1094+
1095+ def start_timer(self):
1096+ """Empty start timer, this is not needed on windows."""
1097
1098=== added file 'ubuntuone/platform/sync_menu/linux.py'
1099--- ubuntuone/platform/sync_menu/linux.py 1970-01-01 00:00:00 +0000
1100+++ ubuntuone/platform/sync_menu/linux.py 2012-09-21 17:55:37 +0000
1101@@ -0,0 +1,234 @@
1102+# -*- coding: utf-8 *-*
1103+#
1104+# Copyright 2012 Canonical Ltd.
1105+#
1106+# This program is free software: you can redistribute it and/or modify it
1107+# under the terms of the GNU General Public License version 3, as published
1108+# by the Free Software Foundation.
1109+#
1110+# This program is distributed in the hope that it will be useful, but
1111+# WITHOUT ANY WARRANTY; without even the implied warranties of
1112+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1113+# PURPOSE. See the GNU General Public License for more details.
1114+#
1115+# You should have received a copy of the GNU General Public License along
1116+# with this program. If not, see <http://www.gnu.org/licenses/>.
1117+#
1118+# In addition, as a special exception, the copyright holders give
1119+# permission to link the code of portions of this program with the
1120+# OpenSSL library under certain conditions as described in each
1121+# individual source file, and distribute linked combinations
1122+# including the two.
1123+# You must obey the GNU General Public License in all respects
1124+# for all of the code used other than OpenSSL. If you modify
1125+# file(s) with this exception, you may extend this exception to your
1126+# version of the file(s), but you are not obligated to do so. If you
1127+# do not wish to do so, delete this exception statement from your
1128+# version. If you delete this exception statement from all source
1129+# files in the program, then also delete it here.
1130+"""Use SyncMenu lib to integrate U1 with the Systray Sync Icon."""
1131+
1132+import gettext
1133+import sys
1134+import webbrowser
1135+
1136+glib = None
1137+try:
1138+ if 'gobject' in sys.modules and sys.modules['gobject'] is not None:
1139+ import glib as GLib
1140+ glib = GLib
1141+ else:
1142+ from gi.repository import GLib
1143+ glib = GLib
1144+except ImportError:
1145+ pass
1146+try:
1147+ from gi.repository import (
1148+ Dbusmenu,
1149+ SyncMenu,
1150+ )
1151+ use_syncmenu = True
1152+except:
1153+ use_syncmenu = False
1154+from twisted.internet import reactor
1155+
1156+from ubuntuone.clientdefs import GETTEXT_PACKAGE
1157+
1158+
1159+Q_ = lambda string: gettext.dgettext(GETTEXT_PACKAGE, string)
1160+
1161+OPEN_U1 = Q_("Open Ubuntu One")
1162+GO_TO_WEB = Q_("Go to the Ubuntu One Website")
1163+TRANSFERS = Q_("Current and Recent Transfers")
1164+MORE_STORAGE = Q_("Get More Space")
1165+GET_HELP = Q_("Get Help on the Web")
1166+
1167+UBUNTUONE_LINK = u'https://one.ubuntu.com/'
1168+DASHBOARD = UBUNTUONE_LINK + u'dashboard/'
1169+HELP_LINK = UBUNTUONE_LINK + u'support/'
1170+GET_STORAGE_LINK = UBUNTUONE_LINK + u'services/#storage_panel'
1171+
1172+
1173+class UbuntuOneSyncMenuLinux(object):
1174+ """Integrate U1 with the Ubuntu Sync Menu."""
1175+
1176+ def __init__(self, main):
1177+ """Initialize menu."""
1178+ self._main = main
1179+ self.status_listener = main.status_listener
1180+ self._paused = False
1181+ self.root_menu = Dbusmenu.Menuitem()
1182+
1183+ self.open_u1 = Dbusmenu.Menuitem()
1184+ self.open_u1.property_set(Dbusmenu.MENUITEM_PROP_LABEL, OPEN_U1)
1185+
1186+ self.go_to_web = Dbusmenu.Menuitem()
1187+ self.go_to_web.property_set(Dbusmenu.MENUITEM_PROP_LABEL,
1188+ GO_TO_WEB)
1189+
1190+ self.transfers = TransfersMenu(self.status_listener)
1191+ self.transfers.property_set(Dbusmenu.MENUITEM_PROP_LABEL,
1192+ TRANSFERS)
1193+
1194+ self.more_storage = Dbusmenu.Menuitem()
1195+ self.more_storage.property_set(Dbusmenu.MENUITEM_PROP_LABEL,
1196+ MORE_STORAGE)
1197+
1198+ self.get_help = Dbusmenu.Menuitem()
1199+ self.get_help.property_set(Dbusmenu.MENUITEM_PROP_LABEL,
1200+ GET_HELP)
1201+
1202+ # Connect signals
1203+ self.open_u1.connect(Dbusmenu.MENUITEM_SIGNAL_ITEM_ACTIVATED,
1204+ self.open_control_panel)
1205+ self.go_to_web.connect(Dbusmenu.MENUITEM_SIGNAL_ITEM_ACTIVATED,
1206+ self.open_go_to_web)
1207+ self.get_help.connect(Dbusmenu.MENUITEM_SIGNAL_ITEM_ACTIVATED,
1208+ self.open_web_help)
1209+ self.more_storage.connect(Dbusmenu.MENUITEM_SIGNAL_ITEM_ACTIVATED,
1210+ self.open_get_more_storage)
1211+
1212+ # Add items
1213+ self.root_menu.child_append(self.open_u1)
1214+ self.root_menu.child_append(self.go_to_web)
1215+ self.root_menu.child_append(self.transfers)
1216+ self.root_menu.child_append(self.more_storage)
1217+ self.root_menu.child_append(self.get_help)
1218+
1219+ self.server = Dbusmenu.Server()
1220+ self.server.set_root(self.root_menu)
1221+ self.app = SyncMenu.App.new("ubuntuone-installer.desktop")
1222+ self.app.set_menu(self.server)
1223+ self.app.connect("notify::paused", self.change_sync_status)
1224+
1225+ def start_timer(self):
1226+ """Start the transfers timer."""
1227+ self.transfers.start_timer()
1228+
1229+ def change_sync_status(self, *args):
1230+ """Triggered when the sync status is changed fromm the menu."""
1231+ if self._paused:
1232+ self._main.external.connect()
1233+ self._paused = False
1234+ else:
1235+ self._main.external.disconnect()
1236+ self._paused = True
1237+
1238+ def open_control_panel(self, *args):
1239+ """Open the Ubuntu One Control Panel."""
1240+ glib.spawn_command_line_async('ubuntuone-installer')
1241+
1242+ def open_go_to_web(self, *args):
1243+ """Open the Ubunto One Help Page"""
1244+ webbrowser.open(DASHBOARD)
1245+
1246+ def open_web_help(self, *args):
1247+ """Open the Ubunto One Help Page"""
1248+ webbrowser.open(HELP_LINK)
1249+
1250+ def open_get_more_storage(self, *args):
1251+ """Open the Ubunto One Help Page"""
1252+ webbrowser.open(GET_STORAGE_LINK)
1253+
1254+
1255+class TransfersMenu(Dbusmenu.Menuitem):
1256+ """Menu that handles the recent and current transfers."""
1257+
1258+ def __init__(self, listener):
1259+ super(TransfersMenu, self).__init__()
1260+ self.listener = listener
1261+ self.uploading = {}
1262+ self.previous_transfers = []
1263+ self._transfers_items = {}
1264+ self._uploading_items = {}
1265+
1266+ def start_timer(self):
1267+ """Trigger an update in one second."""
1268+ self.update_progress()
1269+ reactor.callLater(3, self.start_timer)
1270+
1271+ def update_progress(self):
1272+ """Update the list of recent transfers and current transfers."""
1273+ current_transfers = self.listener.status_frontend.recent_transfers()
1274+ uploading_data = {}
1275+ for filename, size, written in \
1276+ self.listener.status_frontend.files_uploading():
1277+ uploading_data[filename] = (size, written)
1278+
1279+ temp_transfers = {}
1280+ if current_transfers != self.previous_transfers:
1281+ for item_transfer in self._transfers_items:
1282+ self.child_delete(self._transfers_items[item_transfer])
1283+ for item in current_transfers:
1284+ recent_file = Dbusmenu.Menuitem()
1285+ recent_file.property_set(Dbusmenu.MENUITEM_PROP_LABEL,
1286+ item)
1287+ self.child_add_position(recent_file, 0)
1288+ temp_transfers[item] = recent_file
1289+ self._transfers_items = temp_transfers
1290+
1291+ items_added = 0
1292+ remove = []
1293+ for item in self._uploading_items:
1294+ if item in uploading_data:
1295+ size, written = uploading_data[item]
1296+ percentage = written * 100 / size
1297+ upload_item = self._uploading_items[item]
1298+ upload_item.property_set_int(
1299+ SyncMenu.PROGRESS_MENUITEM_PROP_PERCENT_DONE,
1300+ percentage)
1301+ items_added += 1
1302+ else:
1303+ self.child_delete(self._uploading_items[item])
1304+ remove.append(item)
1305+ for item in remove:
1306+ self._uploading_items.pop(item)
1307+ if items_added < 5:
1308+ for item in uploading_data:
1309+ if item not in self._uploading_items and items_added < 5:
1310+ size, written = uploading_data[item]
1311+ percentage = written * 100 / size
1312+ uploading_file = Dbusmenu.Menuitem()
1313+ uploading_file.property_set(Dbusmenu.MENUITEM_PROP_LABEL,
1314+ item)
1315+ uploading_file.property_set(Dbusmenu.MENUITEM_PROP_TYPE,
1316+ SyncMenu.PROGRESS_MENUITEM_TYPE)
1317+ uploading_file.property_set_int(
1318+ SyncMenu.PROGRESS_MENUITEM_PROP_PERCENT_DONE,
1319+ percentage)
1320+ self.child_append(uploading_file)
1321+ self._uploading_items[item] = uploading_file
1322+ items_added += 1
1323+
1324+
1325+class DummySyncMenu(object):
1326+ """Dummy SyncMenu"""
1327+
1328+ def __init__(self, *args, **kwargs):
1329+ """Initialize menu."""
1330+
1331+ def start_timer(self):
1332+ """Do nothing."""
1333+
1334+
1335+UbuntuOneSyncMenu = UbuntuOneSyncMenuLinux if use_syncmenu else DummySyncMenu
1336
1337=== modified file 'ubuntuone/syncdaemon/main.py'
1338--- ubuntuone/syncdaemon/main.py 2012-07-12 16:04:44 +0000
1339+++ ubuntuone/syncdaemon/main.py 2012-09-21 17:55:37 +0000
1340@@ -48,7 +48,10 @@
1341 volume_manager,
1342 )
1343 from ubuntuone import syncdaemon, clientdefs
1344-from ubuntuone.platform import event_logging
1345+from ubuntuone.platform import (
1346+ event_logging,
1347+ sync_menu,
1348+)
1349 from ubuntuone.syncdaemon import status_listener
1350 from ubuntuone.syncdaemon.interaction_interfaces import SyncdaemonService
1351 from ubuntuone.syncdaemon.states import StateManager, QueueManager
1352@@ -90,7 +93,8 @@
1353 handshake_timeout=30,
1354 shares_symlink_name='Shared With Me',
1355 read_limit=None, write_limit=None, throttling_enabled=False,
1356- ignore_files=None, oauth_credentials=None, monitor_class=None):
1357+ ignore_files=None, oauth_credentials=None,
1358+ monitor_class=None):
1359 self.root_dir = root_dir
1360 self.shares_dir = shares_dir
1361 self.shares_dir_link = os.path.join(self.root_dir, shares_symlink_name)
1362@@ -155,6 +159,14 @@
1363 self.mark = task.LoopingCall(self.log_mark)
1364 self.mark.start(mark_interval)
1365
1366+ self.sync_menu = None
1367+ self.start_sync_menu()
1368+
1369+ def start_sync_menu(self):
1370+ """Create the sync menu and run the loop."""
1371+ self.sync_menu = sync_menu.UbuntuOneSyncMenu(self)
1372+ self.sync_menu.start_timer()
1373+
1374 def start_event_logger(self):
1375 """Start the event logger if it's available for this platform."""
1376 self.eventlog_listener = event_logging.get_listener(self.fs, self.vm)

Subscribers

People subscribed via source and target branches

to all changes: