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

Proposed by dobey
Status: Merged
Approved by: Alejandro J. Cura
Approved revision: no longer in the source branch.
Merged at revision: 337
Proposed branch: lp:~dobey/ubuntuone-control-panel/update-4-0
Merge into: lp:ubuntuone-control-panel/stable-4-0
Diff against target: 2170 lines (+1686/-51)
23 files modified
data/qt/controlpanel.ui (+1/-1)
data/qt/images.qrc (+1/-0)
data/qt/share_file.ui (+94/-0)
data/qt/share_links.ui (+196/-10)
data/qt/ubuntuone.qss (+65/-0)
ubuntuone/controlpanel/backend.py (+36/-1)
ubuntuone/controlpanel/gui/__init__.py (+2/-0)
ubuntuone/controlpanel/gui/qt/addfolder.py (+3/-3)
ubuntuone/controlpanel/gui/qt/main/__init__.py (+2/-0)
ubuntuone/controlpanel/gui/qt/main/tests/test_main.py (+20/-2)
ubuntuone/controlpanel/gui/qt/preferences.py (+4/-2)
ubuntuone/controlpanel/gui/qt/share_file.py (+63/-0)
ubuntuone/controlpanel/gui/qt/share_links.py (+189/-1)
ubuntuone/controlpanel/gui/qt/share_links_search.py (+318/-0)
ubuntuone/controlpanel/gui/qt/tests/__init__.py (+18/-0)
ubuntuone/controlpanel/gui/qt/tests/test_preferences.py (+8/-2)
ubuntuone/controlpanel/gui/qt/tests/test_share_file.py (+76/-0)
ubuntuone/controlpanel/gui/qt/tests/test_share_links.py (+161/-3)
ubuntuone/controlpanel/gui/qt/tests/test_share_links_search.py (+302/-0)
ubuntuone/controlpanel/gui/qt/tests/test_systray.py (+15/-26)
ubuntuone/controlpanel/sd_client/__init__.py (+20/-0)
ubuntuone/controlpanel/tests/test_backend.py (+64/-0)
ubuntuone/controlpanel/tests/test_sd_client.py (+28/-0)
To merge this branch: bzr merge lp:~dobey/ubuntuone-control-panel/update-4-0
Reviewer Review Type Date Requested Status
Alejandro J. Cura (community) trivial Approve
Review via email: mp+121914@code.launchpad.net

Commit message

[Diego Sarmentero]

    - Adding functionality to the Share Links Tab (LP: #1039142).

[Roberto Alsina]

    - Fixed saving/restoring the speed throttling (Fixes LP:1040899)
    - Switched to non-native dialog on darwin (LP:1040905).
    - Fixed share/UDF list scrolling on Mac (LP:1018918)

[Manuel de la Peña]

    - Fixed the tab aligment by asserting that tabs are on the left via the qss (LP: #1019224).

To post a comment you must log in.
Revision history for this message
Alejandro J. Cura (alecu) wrote :

Looks just like trunk.

review: Approve (trivial)
337. By Diego Sarmentero

[Diego Sarmentero]

    - Adding functionality to the Share Links Tab (LP: #1039142).

[Roberto Alsina]

    - Fixed saving/restoring the speed throttling (Fixes LP:1040899)
    - Switched to non-native dialog on darwin (LP:1040905).
    - Fixed share/UDF list scrolling on Mac (LP:1018918)

[Manuel de la Peña]

    - Fixed the tab aligment by asserting that tabs are on the left via the qss (LP: #1019224).

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'data/delete_search.png'
2Binary files data/delete_search.png 1970-01-01 00:00:00 +0000 and data/delete_search.png 2012-08-29 18:07:21 +0000 differ
3=== modified file 'data/qt/controlpanel.ui'
4--- data/qt/controlpanel.ui 2012-08-20 20:17:35 +0000
5+++ data/qt/controlpanel.ui 2012-08-29 18:07:21 +0000
6@@ -229,7 +229,7 @@
7 </sizepolicy>
8 </property>
9 <property name="currentIndex">
10- <number>1</number>
11+ <number>0</number>
12 </property>
13 <widget class="FoldersPanel" name="folders_tab">
14 <attribute name="title">
15
16=== modified file 'data/qt/images.qrc'
17--- data/qt/images.qrc 2012-05-29 14:30:36 +0000
18+++ data/qt/images.qrc 2012-08-29 18:07:21 +0000
19@@ -25,6 +25,7 @@
20 <file>../sync_status_syncing.png</file>
21 <file>../twitter.png</file>
22 <file>../facebook.png</file>
23+ <file>../delete_search.png</file>
24 <file>../Ubuntu-R.ttf</file>
25 <file>../Ubuntu-B.ttf</file>
26 <file>ubuntuone.qss</file>
27
28=== added file 'data/qt/share_file.ui'
29--- data/qt/share_file.ui 1970-01-01 00:00:00 +0000
30+++ data/qt/share_file.ui 2012-08-29 18:07:21 +0000
31@@ -0,0 +1,94 @@
32+<?xml version="1.0" encoding="UTF-8"?>
33+<ui version="4.0">
34+ <class>Form</class>
35+ <widget class="QWidget" name="Form">
36+ <property name="geometry">
37+ <rect>
38+ <x>0</x>
39+ <y>0</y>
40+ <width>519</width>
41+ <height>58</height>
42+ </rect>
43+ </property>
44+ <property name="windowTitle">
45+ <string>Form</string>
46+ </property>
47+ <layout class="QHBoxLayout" name="horizontalLayout">
48+ <item>
49+ <layout class="QHBoxLayout" name="horizontalLayout_2">
50+ <item>
51+ <widget class="QLabel" name="lbl_icon">
52+ <property name="text">
53+ <string/>
54+ </property>
55+ </widget>
56+ </item>
57+ <item>
58+ <layout class="QVBoxLayout" name="verticalLayout_3">
59+ <item>
60+ <widget class="QLabel" name="lbl_filename">
61+ <property name="text">
62+ <string>filename</string>
63+ </property>
64+ </widget>
65+ </item>
66+ <item>
67+ <widget class="QLabel" name="lbl_path">
68+ <property name="sizePolicy">
69+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
70+ <horstretch>0</horstretch>
71+ <verstretch>0</verstretch>
72+ </sizepolicy>
73+ </property>
74+ <property name="font">
75+ <font>
76+ <pointsize>9</pointsize>
77+ </font>
78+ </property>
79+ <property name="text">
80+ <string>path</string>
81+ </property>
82+ </widget>
83+ </item>
84+ </layout>
85+ </item>
86+ </layout>
87+ </item>
88+ <item>
89+ <spacer name="horizontalSpacer">
90+ <property name="orientation">
91+ <enum>Qt::Horizontal</enum>
92+ </property>
93+ <property name="sizeHint" stdset="0">
94+ <size>
95+ <width>40</width>
96+ <height>20</height>
97+ </size>
98+ </property>
99+ </spacer>
100+ </item>
101+ <item>
102+ <widget class="QPushButton" name="btn_open">
103+ <property name="sizePolicy">
104+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
105+ <horstretch>0</horstretch>
106+ <verstretch>0</verstretch>
107+ </sizepolicy>
108+ </property>
109+ <property name="text">
110+ <string>Open</string>
111+ </property>
112+ </widget>
113+ </item>
114+ <item>
115+ <widget class="QPushButton" name="btn_disable">
116+ <property name="text">
117+ <string>Disable link</string>
118+ </property>
119+ </widget>
120+ </item>
121+ </layout>
122+ </widget>
123+ <resources/>
124+ <connections/>
125+</ui>
126
127=== modified file 'data/qt/share_links.ui'
128--- data/qt/share_links.ui 2012-08-20 17:51:54 +0000
129+++ data/qt/share_links.ui 2012-08-29 18:07:21 +0000
130@@ -6,13 +6,16 @@
131 <rect>
132 <x>0</x>
133 <y>0</y>
134- <width>532</width>
135- <height>335</height>
136+ <width>567</width>
137+ <height>341</height>
138 </rect>
139 </property>
140 <property name="windowTitle">
141 <string>Form</string>
142 </property>
143+ <property name="styleSheet">
144+ <string notr="true"/>
145+ </property>
146 <layout class="QVBoxLayout" name="verticalLayout">
147 <property name="spacing">
148 <number>15</number>
149@@ -38,7 +41,7 @@
150 <enum>QLayout::SetDefaultConstraint</enum>
151 </property>
152 <property name="leftMargin">
153- <number>10</number>
154+ <number>15</number>
155 </property>
156 <item>
157 <widget class="QLabel" name="search_files_lbl">
158@@ -61,7 +64,7 @@
159 </widget>
160 </item>
161 <item>
162- <widget class="QLineEdit" name="lineEdit">
163+ <widget class="SearchBox" name="line_search">
164 <property name="sizePolicy">
165 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
166 <horstretch>0</horstretch>
167@@ -82,7 +85,10 @@
168 </layout>
169 </item>
170 <item>
171- <widget class="QStackedWidget" name="stackedWidget">
172+ <widget class="QStackedWidget" name="stacked_widget">
173+ <property name="currentIndex">
174+ <number>0</number>
175+ </property>
176 <widget class="QWidget" name="page">
177 <layout class="QVBoxLayout" name="verticalLayout_4">
178 <property name="spacing">
179@@ -107,13 +113,38 @@
180 <number>0</number>
181 </property>
182 <item>
183- <widget class="QTreeWidget" name="treeWidget">
184+ <widget class="QTreeWidget" name="tree_shared_files">
185+ <property name="alternatingRowColors">
186+ <bool>true</bool>
187+ </property>
188+ <property name="indentation">
189+ <number>15</number>
190+ </property>
191+ <property name="rootIsDecorated">
192+ <bool>false</bool>
193+ </property>
194 <attribute name="headerVisible">
195 <bool>false</bool>
196 </attribute>
197- <column>
198- <property name="text">
199- <string notr="true">1</string>
200+ <attribute name="headerDefaultSectionSize">
201+ <number>250</number>
202+ </attribute>
203+ <attribute name="headerStretchLastSection">
204+ <bool>true</bool>
205+ </attribute>
206+ <column>
207+ <property name="text">
208+ <string notr="true">Filename</string>
209+ </property>
210+ </column>
211+ <column>
212+ <property name="text">
213+ <string>Path</string>
214+ </property>
215+ </column>
216+ <column>
217+ <property name="text">
218+ <string notr="true">Actions</string>
219 </property>
220 </column>
221 </widget>
222@@ -123,11 +154,166 @@
223 </item>
224 </layout>
225 </widget>
226- <widget class="QWidget" name="page_2"/>
227+ <widget class="QWidget" name="page_2">
228+ <layout class="QVBoxLayout" name="verticalLayout_5">
229+ <property name="spacing">
230+ <number>0</number>
231+ </property>
232+ <property name="margin">
233+ <number>0</number>
234+ </property>
235+ <item>
236+ <layout class="QHBoxLayout" name="horizontalLayout">
237+ <property name="spacing">
238+ <number>0</number>
239+ </property>
240+ <property name="leftMargin">
241+ <number>15</number>
242+ </property>
243+ <item>
244+ <widget class="QLabel" name="label_share_file">
245+ <property name="sizePolicy">
246+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
247+ <horstretch>0</horstretch>
248+ <verstretch>0</verstretch>
249+ </sizepolicy>
250+ </property>
251+ <property name="font">
252+ <font>
253+ <weight>75</weight>
254+ <bold>true</bold>
255+ </font>
256+ </property>
257+ <property name="text">
258+ <string>Share this file</string>
259+ </property>
260+ </widget>
261+ </item>
262+ <item>
263+ <spacer name="horizontalSpacer">
264+ <property name="orientation">
265+ <enum>Qt::Horizontal</enum>
266+ </property>
267+ <property name="sizeType">
268+ <enum>QSizePolicy::Expanding</enum>
269+ </property>
270+ <property name="sizeHint" stdset="0">
271+ <size>
272+ <width>40</width>
273+ <height>0</height>
274+ </size>
275+ </property>
276+ </spacer>
277+ </item>
278+ <item>
279+ <widget class="QPushButton" name="back_to_file_list">
280+ <property name="text">
281+ <string>Back to file list</string>
282+ </property>
283+ </widget>
284+ </item>
285+ </layout>
286+ </item>
287+ <item>
288+ <layout class="QVBoxLayout" name="verticalLayout">
289+ <property name="spacing">
290+ <number>0</number>
291+ </property>
292+ <item>
293+ <widget class="QFrame" name="frame_share_file">
294+ <property name="frameShape">
295+ <enum>QFrame::StyledPanel</enum>
296+ </property>
297+ <property name="frameShadow">
298+ <enum>QFrame::Raised</enum>
299+ </property>
300+ <layout class="QVBoxLayout" name="verticalLayout_7">
301+ <property name="spacing">
302+ <number>0</number>
303+ </property>
304+ <property name="margin">
305+ <number>0</number>
306+ </property>
307+ <item>
308+ <layout class="QHBoxLayout" name="hbox_share_file">
309+ <property name="spacing">
310+ <number>0</number>
311+ </property>
312+ </layout>
313+ </item>
314+ <item>
315+ <layout class="QVBoxLayout" name="verticalLayout_6">
316+ <property name="leftMargin">
317+ <number>15</number>
318+ </property>
319+ <property name="topMargin">
320+ <number>15</number>
321+ </property>
322+ <item>
323+ <widget class="QLineEdit" name="line_copy_link">
324+ <property name="sizePolicy">
325+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
326+ <horstretch>0</horstretch>
327+ <verstretch>0</verstretch>
328+ </sizepolicy>
329+ </property>
330+ <property name="minimumSize">
331+ <size>
332+ <width>470</width>
333+ <height>0</height>
334+ </size>
335+ </property>
336+ <property name="readOnly">
337+ <bool>true</bool>
338+ </property>
339+ </widget>
340+ </item>
341+ <item>
342+ <widget class="QPushButton" name="open_in_browser">
343+ <property name="sizePolicy">
344+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
345+ <horstretch>0</horstretch>
346+ <verstretch>0</verstretch>
347+ </sizepolicy>
348+ </property>
349+ <property name="text">
350+ <string>Open in browser</string>
351+ </property>
352+ </widget>
353+ </item>
354+ <item>
355+ <spacer name="verticalSpacer">
356+ <property name="orientation">
357+ <enum>Qt::Vertical</enum>
358+ </property>
359+ <property name="sizeHint" stdset="0">
360+ <size>
361+ <width>0</width>
362+ <height>1</height>
363+ </size>
364+ </property>
365+ </spacer>
366+ </item>
367+ </layout>
368+ </item>
369+ </layout>
370+ </widget>
371+ </item>
372+ </layout>
373+ </item>
374+ </layout>
375+ </widget>
376 </widget>
377 </item>
378 </layout>
379 </widget>
380+ <customwidgets>
381+ <customwidget>
382+ <class>SearchBox</class>
383+ <extends>QLineEdit</extends>
384+ <header>ubuntuone.controlpanel.gui.qt.share_links_search</header>
385+ </customwidget>
386+ </customwidgets>
387 <resources/>
388 <connections/>
389 </ui>
390
391=== modified file 'data/qt/ubuntuone.qss'
392--- data/qt/ubuntuone.qss 2012-05-15 18:43:52 +0000
393+++ data/qt/ubuntuone.qss 2012-08-29 18:07:21 +0000
394@@ -19,6 +19,12 @@
395 border-color: #333333;
396 }
397
398+QFrame#frame_share_file {
399+ border-top-style: solid;
400+ border-width: 1px;
401+ border-color: #727272;
402+}
403+
404 UbuntuOneWizard,
405 QFrame#frame_header {
406 background: white;
407@@ -259,6 +265,10 @@
408 text-decoration: underline;
409 }
410
411+QTabWidget::tab-bar {
412+ alignment: left;
413+}
414+
415 QGroupBox {
416 padding-top: 30px;
417 border: none;
418@@ -312,6 +322,21 @@
419 color: #df2d1f;
420 }
421
422+FilesPopup > QListView {
423+ border-style: solid;
424+ border-width: 1px;
425+ border-color: #727272;
426+}
427+
428+FilesPopup > QListView::item {
429+ padding: 5px;
430+}
431+
432+FilesPopup > QListView::item:selected {
433+ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
434+ stop: 0 #dd4814,stop: 1.0 #b93f14);
435+}
436+
437 QAbstractItemView {
438 border-style: solid;
439 border-top-width: 1px;
440@@ -358,3 +383,43 @@
441 padding-bottom: 5px;
442 /* end of hack */
443 }
444+
445+QPushButton#action_button {
446+ margin: 0px;
447+ padding: 5 5 5 5;
448+}
449+
450+QPushButton#open_in_browser {
451+ background: transparent;
452+ border: none;
453+ text-decoration: underline;
454+ color: #dd4814;
455+ padding-left: 0px;
456+ padding-right: 25px;
457+ background-image: url(:/external_icon_orange.png);
458+ background-repeat: no-repeat;
459+ background-position: right;
460+ background-origin: margin;
461+}
462+
463+QPushButton#back_to_file_list {
464+ background: transparent;
465+ border: none;
466+ color: #dd4814;
467+ padding-left: 10px;
468+ padding-right: 15px;
469+}
470+
471+QPushButton#enhanced_borderless {
472+ background: transparent;
473+ border: none;
474+ padding: 0 5 0 0;
475+}
476+
477+QLabel#lbl_path {
478+ color: gray;
479+}
480+
481+QLineEdit#line_copy_link {
482+ color: #dd4814;
483+}
484
485=== modified file 'ubuntuone/controlpanel/backend.py'
486--- ubuntuone/controlpanel/backend.py 2012-08-16 17:12:44 +0000
487+++ ubuntuone/controlpanel/backend.py 2012-08-29 18:07:21 +0000
488@@ -829,12 +829,47 @@
489
490 @log_call(logger.info)
491 @inlineCallbacks
492+ def get_public_files(self):
493+ """Trigger the action to get the public files."""
494+ yield self.sd_client.get_public_files()
495+
496+ @log_call(logger.info)
497+ @inlineCallbacks
498+ def set_public_files_list_handler(self, handler):
499+ """Return the handler to be called for the public files list."""
500+ result = yield self.sd_client.set_public_files_list_handler(handler)
501+ returnValue(result)
502+
503+ @log_call(logger.info)
504+ @inlineCallbacks
505 def get_shares(self):
506- """Obtain the data to create the sync menu."""
507+ """Get the information of the shares."""
508 result = yield self.sd_client.get_shares()
509 returnValue(result)
510
511 @log_call(logger.info)
512+ @inlineCallbacks
513+ def change_public_access(self, path, is_public):
514+ """Change the type access of a file."""
515+ yield self.sd_client.change_public_access(path, is_public)
516+
517+ @log_call(logger.info)
518+ @inlineCallbacks
519+ def set_public_access_changed_handler(self, handler):
520+ """Return the handler to be called when a access type change."""
521+ result = yield self.sd_client.set_public_access_changed_handler(
522+ handler)
523+ returnValue(result)
524+
525+ @log_call(logger.info)
526+ @inlineCallbacks
527+ def set_public_access_change_error_handler(self, handler):
528+ """Return the handler to be called on access type change error."""
529+ result = yield self.sd_client.set_public_access_change_error_handler(
530+ handler)
531+ returnValue(result)
532+
533+ @log_call(logger.info)
534 def shutdown(self):
535 """Stop this service."""
536 # do any other needed cleanup
537
538=== modified file 'ubuntuone/controlpanel/gui/__init__.py'
539--- ubuntuone/controlpanel/gui/__init__.py 2012-08-20 21:02:06 +0000
540+++ ubuntuone/controlpanel/gui/__init__.py 2012-08-29 18:07:21 +0000
541@@ -105,6 +105,7 @@
542 COMPUTER_TO_CLOUD_TITLE = _('Syncing your computer with the cloud')
543 CONNECT_BUTTON_LABEL = _('Connect to Ubuntu One')
544 CONTACTS = _('Thunderbird plug-in')
545+COPY_LINK = _('Copy link')
546 CREDENTIALS_ERROR = _('There was a problem while retrieving the credentials.')
547 DASHBOARD_BUTTON_TOOLTIP = _('View your personal details and service '
548 'summary')
549@@ -226,6 +227,7 @@
550 NO_DEVICES = _('No devices to show.')
551 NO_FOLDERS = _('No folders to show.')
552 NO_PAIRING_RECORD = _('There is no Ubuntu One pairing record.')
553+OPEN = _('Open')
554 OPEN_UBUNTU_ONE = _('Open Ubuntu One')
555 OPEN_UBUNTU_ONE_FOLDER = _('Open the Ubuntu One Folder')
556 PERCENTAGE_LABEL = _('%(percentage)s used')
557
558=== modified file 'ubuntuone/controlpanel/gui/qt/addfolder.py'
559--- ubuntuone/controlpanel/gui/qt/addfolder.py 2012-03-19 20:55:52 +0000
560+++ ubuntuone/controlpanel/gui/qt/addfolder.py 2012-08-29 18:07:21 +0000
561@@ -33,13 +33,13 @@
562 logger = setup_logging('qt.addfolder')
563
564 CLOSE = QtGui.QMessageBox.Close
565-# NOTE: this is temporary because of a Qt bug that will be fixed
566-# on Qt 4.9. You cannot, in general, use sys.platform in these
567+# NOTE: this is temporary because of Qt bugs.
568+# You cannot, in general, use sys.platform in these
569 # modules. Do not do this. Please. This is an exception, one
570 # time only, pinky-swear, cross my heart and hope to die if
571 # I lie.
572 FILE_CHOOSER_OPTIONS = QtGui.QFileDialog.ShowDirsOnly
573-if sys.platform == 'win32':
574+if sys.platform in ('win32', 'darwin'):
575 FILE_CHOOSER_OPTIONS |= QtGui.QFileDialog.DontUseNativeDialog
576 # Yes, that is true. If you do that again, you will be hunted
577 # down and taught a lesson. You will be sorry.
578
579=== modified file 'ubuntuone/controlpanel/gui/qt/main/__init__.py'
580--- ubuntuone/controlpanel/gui/qt/main/__init__.py 2012-08-20 20:30:39 +0000
581+++ ubuntuone/controlpanel/gui/qt/main/__init__.py 2012-08-29 18:07:21 +0000
582@@ -85,6 +85,8 @@
583 # because u1trial already provides a reactor.
584
585 args = ['ubuntuone-installer'] + args
586+ if sys.platform == 'darwin':
587+ args += ['-graphicssystem', 'raster']
588 app = UniqueApplication(args, "ubuntuone-control-panel")
589
590 # on darwin, must install qt4reactor after UniqueApplication init.
591
592=== modified file 'ubuntuone/controlpanel/gui/qt/main/tests/test_main.py'
593--- ubuntuone/controlpanel/gui/qt/main/tests/test_main.py 2012-08-04 00:05:23 +0000
594+++ ubuntuone/controlpanel/gui/qt/main/tests/test_main.py 2012-08-29 18:07:21 +0000
595@@ -47,11 +47,17 @@
596 self.style = None
597 self.translator = None
598 self.new_instance = FakeSignal()
599+ self.raster = False
600
601 def __call__(self, argv, *args, **kwargs):
602 """Fake arg filtering function."""
603- if '-title' in argv:
604- argv.remove('-title')
605+ if 'raster' in argv and '-graphicssystem' in argv:
606+ self.raster = True
607+ for arg in ('-title', '-graphicssystem', 'raster'):
608+ try:
609+ argv.remove(arg)
610+ except ValueError:
611+ pass
612 self.args = (argv, args, kwargs)
613 return self
614
615@@ -222,6 +228,18 @@
616 self.assertEqual(self.start.args[1],
617 {'minimized': True, 'with_icon': True, 'installer': False})
618
619+ def test_nondarwin_is_not_raster(self):
620+ """Ensure the raster argument is not injected in non-darwin."""
621+ self.patch(sys, 'platform', 'not-darwin')
622+ main.main([sys.argv[0]])
623+ self.assertFalse(self.app.raster)
624+
625+ def test_darwin_is_raster(self):
626+ """Ensure the raster argument is injected in darwin."""
627+ self.patch(sys, 'platform', 'darwin')
628+ main.main([sys.argv[0]])
629+ self.assertTrue(self.app.raster)
630+
631 def test_translator(self):
632 """Ensure the Qt translator is loaded."""
633 main.main([sys.argv[0]])
634
635=== modified file 'ubuntuone/controlpanel/gui/qt/preferences.py'
636--- ubuntuone/controlpanel/gui/qt/preferences.py 2012-05-15 18:43:52 +0000
637+++ ubuntuone/controlpanel/gui/qt/preferences.py 2012-08-29 18:07:21 +0000
638@@ -121,12 +121,14 @@
639 notifications = self.ui.show_all_notifications_checkbox.checkState()
640 share_autosubscribe = self.ui.share_autosubscribe_checkbox.checkState()
641 udf_autosubscribe = self.ui.udf_autosubscribe_checkbox.checkState()
642- download_speed = self.ui.download_speed_spinbox.value() * KILOBYTES
643 if self.ui.limit_uploads_checkbox.checkState() == UNCHECKED:
644 upload_speed = -1
645- upload_speed = self.ui.upload_speed_spinbox.value() * KILOBYTES
646+ else:
647+ upload_speed = self.ui.upload_speed_spinbox.value() * KILOBYTES
648 if self.ui.limit_downloads_checkbox.checkState() == UNCHECKED:
649 download_speed = -1
650+ else:
651+ download_speed = self.ui.download_speed_spinbox.value() * KILOBYTES
652
653 settings = {
654 backend.AUTOCONNECT_KEY: autoconnect == CHECKED,
655
656=== added file 'ubuntuone/controlpanel/gui/qt/share_file.py'
657--- ubuntuone/controlpanel/gui/qt/share_file.py 1970-01-01 00:00:00 +0000
658+++ ubuntuone/controlpanel/gui/qt/share_file.py 2012-08-29 18:07:21 +0000
659@@ -0,0 +1,63 @@
660+# -*- coding: utf-8 -*-
661+#
662+# Copyright 2012 Canonical Ltd.
663+#
664+# This program is free software: you can redistribute it and/or modify it
665+# under the terms of the GNU General Public License version 3, as published
666+# by the Free Software Foundation.
667+#
668+# This program is distributed in the hope that it will be useful, but
669+# WITHOUT ANY WARRANTY; without even the implied warranties of
670+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
671+# PURPOSE. See the GNU General Public License for more details.
672+#
673+# You should have received a copy of the GNU General Public License along
674+# with this program. If not, see <http://www.gnu.org/licenses/>.
675+
676+"""The UI for Share file widget."""
677+
678+import os
679+
680+from PyQt4 import QtGui, QtCore
681+
682+from ubuntuone.controlpanel import cache
683+from ubuntuone.controlpanel.gui.qt.share_links_search import (
684+ get_system_icon_for_filename,
685+)
686+from ubuntuone.controlpanel.gui.qt.ui import share_file_ui
687+from ubuntuone.controlpanel.logger import setup_logging
688+
689+
690+logger = setup_logging('qt.share_file')
691+
692+
693+class ShareFileWidget(cache.Cache, QtGui.QWidget):
694+ """Widget with the detail information about the shared file."""
695+
696+ linkDisabled = QtCore.pyqtSignal()
697+
698+ def __init__(self, file_path='', *args, **kwargs):
699+ super(ShareFileWidget, self).__init__(*args, **kwargs)
700+ self.ui = share_file_ui.Ui_Form()
701+ self.ui.setupUi(self)
702+ self.file_path = file_path
703+
704+ self.ui.lbl_filename.setText(os.path.basename(file_path))
705+ self.ui.lbl_path.setText(file_path)
706+ icon = get_system_icon_for_filename(os.path.expanduser(file_path))
707+ pixmap = icon.pixmap(24)
708+ self.ui.lbl_icon.setPixmap(pixmap)
709+
710+ self.ui.btn_open.clicked.connect(self.open_file)
711+ self.ui.btn_disable.clicked.connect(self.disable_link)
712+
713+ def open_file(self):
714+ """Open the specified file."""
715+ path = u'file://%s' % self.file_path
716+ QtGui.QDesktopServices.openUrl(QtCore.QUrl(path))
717+
718+ def disable_link(self, val):
719+ """Change the access of the file to Not Public."""
720+ self.backend.change_public_access(
721+ os.path.expanduser(self.file_path), False)
722+ self.linkDisabled.emit()
723
724=== modified file 'ubuntuone/controlpanel/gui/qt/share_links.py'
725--- ubuntuone/controlpanel/gui/qt/share_links.py 2012-08-20 18:22:07 +0000
726+++ ubuntuone/controlpanel/gui/qt/share_links.py 2012-08-29 18:07:21 +0000
727@@ -16,27 +16,215 @@
728
729 """The user interface for the control panel for Ubuntu One."""
730
731+import os
732+
733+from PyQt4 import QtGui, QtCore
734+from twisted.internet.defer import inlineCallbacks
735+
736 from ubuntuone.controlpanel.logger import setup_logging
737 from ubuntuone.controlpanel.gui import (
738+ COPY_LINK,
739+ OPEN,
740 SEARCH_FILES,
741 SHARED_FILES,
742 )
743
744-from ubuntuone.controlpanel.gui.qt.ui import share_links_ui
745+# Unused import images_rc, pylint: disable=W0611
746+from ubuntuone.controlpanel.gui.qt.ui import (
747+ images_rc,
748+ share_links_ui,
749+)
750+# pylint: enable=W0611
751+from ubuntuone.controlpanel.gui.qt.share_file import ShareFileWidget
752+from ubuntuone.controlpanel.gui.qt.share_links_search import (
753+ get_system_icon_for_filename,
754+)
755 from ubuntuone.controlpanel.gui.qt.ubuntuonebin import UbuntuOneBin
756
757
758 logger = setup_logging('qt.share_links')
759
760
761+FILE_NAME_COL = 0
762+PUBLIC_LINK_COL = 1
763+ACTIONS_COL = 2
764+
765+
766 class ShareLinksPanel(UbuntuOneBin):
767 """The Share Links Tab Panel widget"""
768
769 ui_class = share_links_ui
770 logger = logger
771+ _enhanced_line = None
772+ home_dir = ''
773
774 def _setup(self):
775 """Do some extra setupping for the UI."""
776 super(ShareLinksPanel, self)._setup()
777+ self.home_dir = ''
778 self.ui.search_files_lbl.setText(SEARCH_FILES)
779 self.ui.shared_group.setTitle(SHARED_FILES)
780+
781+ # Set enhanced line edits
782+ self._enhanced_line = EnhancedLineEdit(self.ui.line_search,
783+ self._line_close_btn, icon=":/delete_search.png",
784+ style='enhanced_borderless')
785+ self._enhanced_line.btn_operation.hide()
786+ self.ui.line_search.popup.popupHidden.connect(
787+ self._hide_line_btn_close_hide)
788+ self.ui.line_search.popup.popupShown.connect(
789+ self._hide_line_btn_close_show)
790+ EnhancedLineEdit(self.ui.line_copy_link, self._copy_link_from_line,
791+ text=COPY_LINK)
792+
793+ self.ui.line_search.itemSelected.connect(self.share_file)
794+ self.ui.back_to_file_list.clicked.connect(self._move_to_main_list)
795+ self.ui.open_in_browser.clicked.connect(self._open_in_browser)
796+
797+ # Connect backend signals
798+ self.backend.set_public_files_list_handler(self._load_public_files)
799+ self.backend.set_public_access_changed_handler(self._file_shared)
800+ self.backend.set_public_access_change_error_handler(
801+ lambda: self._set_is_processing(False))
802+ self.get_public_files()
803+
804+ @inlineCallbacks
805+ def share_file(self, file_path):
806+ """Clean the previous file share details and publish file_path."""
807+ if self.ui.hbox_share_file.count() > 0:
808+ widget = self.ui.hbox_share_file.takeAt(0).widget()
809+ widget.close()
810+ self.is_processing = True
811+ file_path = unicode(file_path)
812+ share_file_widget = ShareFileWidget(file_path)
813+ self.ui.hbox_share_file.addWidget(share_file_widget)
814+ share_file_widget.linkDisabled.connect(
815+ lambda: self.ui.line_copy_link.setText(''))
816+ yield self.backend.change_public_access(
817+ os.path.expanduser(file_path), True)
818+
819+ def _file_shared(self, info):
820+ """Receive the notification that the file has been published."""
821+ url = info.get("public_url")
822+ self.ui.line_copy_link.setText(url)
823+ self.ui.stacked_widget.setCurrentIndex(1)
824+ self.is_processing = False
825+
826+ def _open_in_browser(self):
827+ """Open the link in line_copy_link in the browser."""
828+ url = self.ui.line_copy_link.text()
829+ QtGui.QDesktopServices.openUrl(QtCore.QUrl(url))
830+
831+ def _copy_link_from_line(self):
832+ """Copy link into clipboard from line edit."""
833+ app = QtGui.QApplication.instance()
834+ app.clipboard().setText(self.ui.line_copy_link.text())
835+
836+ def _move_to_main_list(self):
837+ """Set the share files list as current widget."""
838+ self.ui.stacked_widget.setCurrentIndex(0)
839+ self.get_public_files()
840+
841+ @inlineCallbacks
842+ def get_public_files(self):
843+ """Request the list of public files."""
844+ self.is_processing = True
845+ self.home_dir = yield self.backend.get_home_dir()
846+ yield self.backend.get_public_files()
847+
848+ def _load_public_files(self, publicfiles):
849+ """Load the list of public files."""
850+ self.ui.tree_shared_files.clear()
851+ for pfile in publicfiles:
852+ item = QtGui.QTreeWidgetItem()
853+ path = pfile['path']
854+ public_url = pfile['public_url']
855+ name = os.path.basename(path)
856+ item.setText(FILE_NAME_COL, name)
857+ tooltip = path
858+ if tooltip.startswith(self.home_dir):
859+ tooltip = tooltip.replace(self.home_dir, '~', 1)
860+ item.setToolTip(FILE_NAME_COL, tooltip)
861+ icon = get_system_icon_for_filename(path)
862+ item.setIcon(FILE_NAME_COL, icon)
863+
864+ self.ui.tree_shared_files.setColumnWidth(PUBLIC_LINK_COL, 300)
865+ item.setSizeHint(FILE_NAME_COL, QtCore.QSize(-1, 35))
866+ self.ui.tree_shared_files.addTopLevelItem(item)
867+
868+ link = ('<a href="%s"><span style="font-size: 12px;'
869+ 'color: #dd4814";>%s</span></a>'
870+ % (public_url, public_url))
871+ label = QtGui.QLabel(link, self.ui.tree_shared_files)
872+ label.setOpenExternalLinks(True)
873+ self.ui.tree_shared_files.setItemWidget(item, PUBLIC_LINK_COL,
874+ label)
875+
876+ actions = ActionsButtons(path, public_url,
877+ self.ui.tree_shared_files)
878+ self.ui.tree_shared_files.setItemWidget(item, ACTIONS_COL, actions)
879+ self.is_processing = False
880+
881+ def _line_close_btn(self):
882+ """Close button in the line edit was pressed, hide the popup."""
883+ self.ui.line_search.popup.hide()
884+ self.ui.line_search.setFocus()
885+
886+ def _hide_line_btn_close_hide(self):
887+ """Hide the button inside the search line edit-"""
888+ self._enhanced_line.btn_operation.hide()
889+
890+ def _hide_line_btn_close_show(self):
891+ """Show the button inside the search line edit-"""
892+ self._enhanced_line.btn_operation.show()
893+
894+
895+class ActionsButtons(QtGui.QWidget):
896+ """Widget that contains the open and copy link actions on the list."""
897+
898+ def __init__(self, path, link, parent=None):
899+ super(ActionsButtons, self).__init__(parent)
900+ self.path = path
901+ self.link = link
902+
903+ hbox = QtGui.QHBoxLayout(self)
904+ btn_open = QtGui.QPushButton(OPEN)
905+ btn_copy = QtGui.QPushButton(COPY_LINK)
906+ btn_open.setObjectName('action_button')
907+ btn_copy.setObjectName('action_button')
908+ hbox.addSpacerItem(QtGui.QSpacerItem(1, 0,
909+ QtGui.QSizePolicy.Expanding))
910+ hbox.addWidget(btn_open)
911+ hbox.addWidget(btn_copy)
912+
913+ btn_open.clicked.connect(self.open)
914+ btn_copy.clicked.connect(self.copy)
915+
916+ def open(self):
917+ """Open the file."""
918+ file_path = u'file://%s' % self.path
919+ QtGui.QDesktopServices.openUrl(QtCore.QUrl(file_path))
920+
921+ def copy(self):
922+ """Copy the public link of the file in the clipboard."""
923+ app = QtGui.QApplication.instance()
924+ app.clipboard().setText(self.link)
925+
926+
927+class EnhancedLineEdit(object):
928+ """Add a button inside the QLineEdit received."""
929+
930+ def __init__(self, line_edit, operation, text=None, icon=None, style=None):
931+ hbox = QtGui.QHBoxLayout(line_edit)
932+ hbox.setMargin(0)
933+ line_edit.setLayout(hbox)
934+ hbox.addStretch()
935+ self.btn_operation = QtGui.QPushButton(line_edit)
936+ if text:
937+ self.btn_operation.setText(text)
938+ if icon:
939+ self.btn_operation.setIcon(QtGui.QIcon(icon))
940+ if style:
941+ self.btn_operation.setObjectName(style)
942+ hbox.addWidget(self.btn_operation)
943+ self.btn_operation.clicked.connect(operation)
944
945=== added file 'ubuntuone/controlpanel/gui/qt/share_links_search.py'
946--- ubuntuone/controlpanel/gui/qt/share_links_search.py 1970-01-01 00:00:00 +0000
947+++ ubuntuone/controlpanel/gui/qt/share_links_search.py 2012-08-29 18:07:21 +0000
948@@ -0,0 +1,318 @@
949+# -*- coding: utf-8 *-*
950+
951+# Copyright 2012 Canonical Ltd.
952+#
953+# This program is free software: you can redistribute it and/or modify it
954+# under the terms of the GNU General Public License version 3, as published
955+# by the Free Software Foundation.
956+#
957+# This program is distributed in the hope that it will be useful, but
958+# WITHOUT ANY WARRANTY; without even the implied warranties of
959+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
960+# PURPOSE. See the GNU General Public License for more details.
961+#
962+# You should have received a copy of the GNU General Public License along
963+# with this program. If not, see <http://www.gnu.org/licenses/>.
964+
965+"""The search and popup widgets for the Share Links tab."""
966+
967+import os
968+
969+from PyQt4 import QtGui, QtCore
970+from twisted.internet.defer import inlineCallbacks
971+
972+from ubuntuone.controlpanel import cache
973+
974+
975+def get_system_icon_for_filename(file_path):
976+ """Return the icon used for the system to represent this file."""
977+ fileinfo = QtCore.QFileInfo(os.path.expanduser(file_path))
978+ icon_provider = QtGui.QFileIconProvider()
979+ icon = icon_provider.icon(fileinfo)
980+ return icon
981+
982+
983+# pylint: disable=C0103
984+
985+class SearchBox(QtGui.QLineEdit, cache.Cache):
986+ """Search widget for the synced files."""
987+
988+ itemSelected = QtCore.pyqtSignal(unicode)
989+
990+ def __init__(self, parent=None):
991+ super(SearchBox, self).__init__(parent)
992+ self.popup = FilesPopup()
993+ self.home_dir = ''
994+ self.temp_u1_files = []
995+ self.items_per_page = 0
996+ self.items_step = 20
997+ self.prefix = ''
998+ self._thread_explore = None
999+ self._pre_key_event = {
1000+ QtCore.Qt.Key_Space: self._key_space_pressed,
1001+ }
1002+ self._post_key_event = {
1003+ QtCore.Qt.Key_Escape: lambda *args: self.popup.hide(),
1004+ QtCore.Qt.Key_Down: self._key_down_pressed,
1005+ QtCore.Qt.Key_Up: self._key_up_pressed,
1006+ QtCore.Qt.Key_Return: self._key_return_pressed,
1007+ QtCore.Qt.Key_Enter: self._key_return_pressed,
1008+ }
1009+
1010+ self.textChanged.connect(self.filter)
1011+
1012+ self._get_volumes_info()
1013+
1014+ @inlineCallbacks
1015+ def _get_volumes_info(self):
1016+ """Get the volumes info."""
1017+ self.home_dir = yield self.backend.get_home_dir()
1018+ self._thread_explore = ThreadExploreFolder(self.home_dir)
1019+ info = yield self.backend.volumes_info()
1020+ self._process_volumes_info(info)
1021+
1022+ def _process_volumes_info(self, info):
1023+ """Get the volumes paths and process them."""
1024+ folders = []
1025+ for _, _, data in info:
1026+ for d in data:
1027+ folder = d.get('path', d.get('realpath'))
1028+ folders.append(folder)
1029+ self.get_folders_files(folders)
1030+
1031+ def get_folders_files(self, folders):
1032+ """Get the list of files in each folder and index them."""
1033+ self._thread_explore.set_folders(folders)
1034+ self._thread_explore.folderDataObtained.connect(
1035+ self._folder_content_obtained)
1036+ self._thread_explore.start()
1037+
1038+ def _folder_content_obtained(self):
1039+ """Receive the content of the folders from the thread."""
1040+ files = self._get_filtered_list(self._thread_explore.u1_files)
1041+ self.popup.load_items(files)
1042+ text = self.text()
1043+ self.filter(text)
1044+
1045+ def filter(self, text):
1046+ """Filter the content of the popup with the text entered."""
1047+ text = unicode(text)
1048+ self.items_per_page = 0
1049+ if text and (self.prefix == '' or self.prefix != text[:-1]):
1050+ self.prefix = text
1051+ self.temp_u1_files = self._thread_explore.u1_files
1052+ self._show_filter()
1053+ elif text != '':
1054+ self.prefix = text
1055+ self._show_filter()
1056+ else:
1057+ self.popup.hide()
1058+
1059+ def _show_filter(self):
1060+ """Load the items in the popup and display it."""
1061+ if not self.popup.isVisible():
1062+ self.popup.setFixedWidth(self.width())
1063+ point = self.parent().mapToGlobal(self.pos())
1064+ self.popup.show()
1065+ self.popup.move(point.x(), point.y() + self.height())
1066+
1067+ self.temp_u1_files = [filename for filename in self.temp_u1_files
1068+ if os.path.basename(filename).find(self.prefix) > -1]
1069+ files = self._get_filtered_list(self.temp_u1_files)
1070+ self.popup.load_items(files)
1071+
1072+ def _get_filtered_list(self, filenames):
1073+ """Get pages of results."""
1074+ begin = self.items_per_page
1075+ self.items_per_page += self.items_step
1076+ files = [filename for filename in filenames[begin:self.items_per_page]]
1077+ return files
1078+
1079+ def _key_space_pressed(self):
1080+ """The user pressed the space key."""
1081+ item = self.popup.list_widget.currentItem()
1082+ widget = self.popup.list_widget.itemWidget(item)
1083+ self.setText(widget.name)
1084+ self.popup.hide()
1085+ return True
1086+
1087+ def _key_down_pressed(self, current):
1088+ """The user pressed the down key."""
1089+ #While the current position is lower that the list size go to next
1090+ if current != self.popup.list_widget.count() - 1:
1091+ self.popup.list_widget.setCurrentRow(
1092+ self.popup.list_widget.currentRow() + 1)
1093+ #If the current position is greater than the amount of items in
1094+ #the list - 6, then try to fetch more items in the list.
1095+ if current >= (self.popup.list_widget.count() - 6):
1096+ filenames = self._get_filtered_list(self.temp_u1_files)
1097+ self.popup.fetch_more(filenames)
1098+
1099+ def _key_up_pressed(self, current):
1100+ """The user pressed the up key."""
1101+ #while the current position is greater than 0, go to previous
1102+ if current > 0:
1103+ self.popup.list_widget.setCurrentRow(
1104+ self.popup.list_widget.currentRow() - 1)
1105+
1106+ def _key_return_pressed(self, current):
1107+ """The user pressed the return key."""
1108+ #If the user pressed enter, go to the item selected
1109+ item = self.popup.list_widget.currentItem()
1110+ self._set_selected_item(item)
1111+
1112+ def keyPressEvent(self, event):
1113+ """Process the different behaviour for the keyPress event."""
1114+ if self._pre_key_event.get(event.key(), lambda: False)():
1115+ return
1116+
1117+ super(SearchBox, self).keyPressEvent(event)
1118+ current = self.popup.list_widget.currentRow()
1119+ self._post_key_event.get(event.key(), lambda *args: None)(current)
1120+
1121+ def focusOutEvent(self, event):
1122+ """Hide the popup when the window loses the focus."""
1123+ super(SearchBox, self).focusOutEvent(event)
1124+ self.popup.hide()
1125+
1126+ def _set_selected_item(self, item):
1127+ """Notify of the selected item."""
1128+ widget = self.popup.list_widget.itemWidget(item)
1129+ self.itemSelected.emit(widget.file_path)
1130+ self.setText('')
1131+ self.popup.hide()
1132+
1133+ def moveEvent(self, event):
1134+ """Move the popup when the windows is moved."""
1135+ super(SearchBox, self).moveEvent(event)
1136+ if self.popup.isVisible():
1137+ point = self.mapToGlobal(self.line_search.pos())
1138+ self.popup.move(point.x(), point.y() + self.line_search.height())
1139+
1140+
1141+class FileItem(QtGui.QLabel):
1142+ """Create a styled QLabel that will show the info."""
1143+
1144+ def __init__(self, file_path):
1145+ super(FileItem, self).__init__()
1146+ self.name = os.path.basename(file_path)
1147+ self.file_path = file_path
1148+ self.text_style = (u"<span style='color: {2};'>{0}</span><br>"
1149+ "<span style='font-size: 13px; color: {3};'>({1})</span>")
1150+ self.setText(self.text_style.format(
1151+ self.name, self.file_path, '#333333', 'grey'))
1152+
1153+ def set_selected(self):
1154+ """Set a style for the text when the item is selected."""
1155+ self.setText(self.text_style.format(
1156+ self.name, self.file_path, 'white', 'white'))
1157+
1158+ def set_not_selected(self):
1159+ """Set a style for the text when the item is not selected."""
1160+ self.setText(self.text_style.format(
1161+ self.name, self.file_path, '#333333', 'grey'))
1162+
1163+
1164+class FilesPopup(QtGui.QFrame):
1165+ """Filter popup where the file names are shown."""
1166+
1167+ popupShown = QtCore.pyqtSignal()
1168+ popupHidden = QtCore.pyqtSignal()
1169+
1170+ def __init__(self):
1171+ super(FilesPopup, self).__init__(None,
1172+ QtCore.Qt.FramelessWindowHint | QtCore.Qt.ToolTip)
1173+ vbox = QtGui.QVBoxLayout(self)
1174+ vbox.setContentsMargins(0, 0, 0, 0)
1175+ vbox.setSpacing(0)
1176+ self.list_widget = QtGui.QListWidget()
1177+ self.list_widget.setMinimumHeight(270)
1178+ vbox.addWidget(self.list_widget)
1179+
1180+ self.list_widget.currentItemChanged.connect(self._repaint_items)
1181+
1182+ def _repaint_items(self, current, previous):
1183+ """Set the proper style for the current and previous items."""
1184+ if current is not None:
1185+ widget = self.list_widget.itemWidget(current)
1186+ widget.set_selected()
1187+ if previous is not None:
1188+ widget = self.list_widget.itemWidget(previous)
1189+ widget.set_not_selected()
1190+
1191+ def load_items(self, file_items):
1192+ """Load the initial items."""
1193+ self.list_widget.clear()
1194+ for file_ in file_items:
1195+ item = QtGui.QListWidgetItem("\n")
1196+ file_widget = FileItem(file_)
1197+ self.list_widget.addItem(item)
1198+ self.list_widget.setItemWidget(item, file_widget)
1199+ icon = get_system_icon_for_filename(file_)
1200+ item.setIcon(icon)
1201+ if file_items:
1202+ self.list_widget.setCurrentRow(0)
1203+
1204+ def fetch_more(self, file_items):
1205+ """Add more items to the list on user scroll."""
1206+ for file_ in file_items:
1207+ item = QtGui.QListWidgetItem("\n")
1208+ file_widget = FileItem(file_)
1209+ self.list_widget.addItem(item)
1210+ self.list_widget.setItemWidget(item, file_widget)
1211+ icon = get_system_icon_for_filename(file_)
1212+ item.setIcon(icon)
1213+
1214+ def showEvent(self, event):
1215+ """Notify when the popup is shown."""
1216+ super(FilesPopup, self).showEvent(event)
1217+ self.popupShown.emit()
1218+
1219+ def hideEvent(self, event):
1220+ """Notify when the popup is hidden."""
1221+ super(FilesPopup, self).hideEvent(event)
1222+ self.popupHidden.emit()
1223+
1224+
1225+class ThreadExploreFolder(QtCore.QThread):
1226+ """Retrieve the data of the folders asynchronously."""
1227+
1228+ folderDataObtained = QtCore.pyqtSignal()
1229+
1230+ def __init__(self, home_dir=''):
1231+ super(ThreadExploreFolder, self).__init__()
1232+ self.home_dir = home_dir
1233+ self.u1_files = []
1234+ self.folders = []
1235+
1236+ def set_folders(self, folders):
1237+ """Set the folders to be explored."""
1238+ self.folders = folders
1239+
1240+ def run(self):
1241+ """Execute the thread to obtain the folders data."""
1242+ folders_data = []
1243+ for folder in self.folders:
1244+ folders_data += self.get_folder_info(folder)
1245+ temp_files = []
1246+ for file_ in folders_data:
1247+ if file_.startswith(self.home_dir):
1248+ new_path = file_[len(self.home_dir):]
1249+ if new_path.startswith(os.path.sep):
1250+ new_path = new_path[1:]
1251+ path = os.path.join('~', new_path)
1252+ temp_files.append(path)
1253+ else:
1254+ temp_files.append(file_)
1255+ folders_data = temp_files
1256+ folders_data.sort()
1257+ self.u1_files = folders_data
1258+ self.folderDataObtained.emit()
1259+
1260+ def get_folder_info(self, folder):
1261+ """Return the list of files in the proper folder."""
1262+ files_list = []
1263+ if os.path.exists(folder):
1264+ for root, _, files in os.walk(folder):
1265+ files_list.extend([os.path.join(root, f) for f in files])
1266+ return files_list
1267
1268=== modified file 'ubuntuone/controlpanel/gui/qt/tests/__init__.py'
1269--- ubuntuone/controlpanel/gui/qt/tests/__init__.py 2012-08-20 14:09:45 +0000
1270+++ ubuntuone/controlpanel/gui/qt/tests/__init__.py 2012-08-29 18:07:21 +0000
1271@@ -126,6 +126,7 @@
1272 'build_signed_iri',
1273 'change_device_settings',
1274 'change_file_sync_settings',
1275+ 'change_public_access',
1276 'change_replication_settings',
1277 'change_volume_settings',
1278 'connect_files',
1279@@ -137,12 +138,16 @@
1280 'enable_files',
1281 'file_sync_settings_info',
1282 'file_sync_status',
1283+ 'get_public_files',
1284 'login',
1285 'register',
1286 'remove_device',
1287 'replications_info',
1288 'restart_files',
1289 'restore_file_sync_settings',
1290+ 'set_public_access_changed_handler',
1291+ 'set_public_access_change_error_handler',
1292+ 'set_public_files_list_handler',
1293 'shutdown',
1294 'start_files',
1295 'stop_files',
1296@@ -379,3 +384,16 @@
1297 """The backend instance is correct."""
1298 if getattr(self.ui, 'backend', None) is not None:
1299 self.assertIsInstance(self.ui.backend, FakedControlPanelBackend)
1300+
1301+
1302+class FakeDesktopService(object):
1303+ """Fake QDesktopService."""
1304+
1305+ def __init__(self):
1306+ self.opened_url = None
1307+
1308+ # pylint: disable=C0103
1309+
1310+ def openUrl(self, url):
1311+ """Fake openUrl."""
1312+ self.opened_url = url
1313
1314=== modified file 'ubuntuone/controlpanel/gui/qt/tests/test_preferences.py'
1315--- ubuntuone/controlpanel/gui/qt/tests/test_preferences.py 2012-05-15 18:43:52 +0000
1316+++ ubuntuone/controlpanel/gui/qt/tests/test_preferences.py 2012-08-29 18:07:21 +0000
1317@@ -47,8 +47,14 @@
1318 notifs = self.ui.ui.show_all_notifications_checkbox.checkState()
1319 share_auto = self.ui.ui.share_autosubscribe_checkbox.checkState()
1320 udf_auto = self.ui.ui.udf_autosubscribe_checkbox.checkState()
1321- download_speed = self.ui.ui.download_speed_spinbox.value()
1322- upload_speed = self.ui.ui.upload_speed_spinbox.value()
1323+ if self.ui.ui.limit_downloads_checkbox.checkState() == gui.CHECKED:
1324+ download_speed = self.ui.ui.download_speed_spinbox.value()
1325+ else:
1326+ download_speed = -1
1327+ if self.ui.ui.limit_uploads_checkbox.checkState() == gui.CHECKED:
1328+ upload_speed = self.ui.ui.upload_speed_spinbox.value()
1329+ else:
1330+ upload_speed = -1
1331
1332 result = {
1333 gui.backend.AUTOCONNECT_KEY: autoconnect == gui.CHECKED,
1334
1335=== added file 'ubuntuone/controlpanel/gui/qt/tests/test_share_file.py'
1336--- ubuntuone/controlpanel/gui/qt/tests/test_share_file.py 1970-01-01 00:00:00 +0000
1337+++ ubuntuone/controlpanel/gui/qt/tests/test_share_file.py 2012-08-29 18:07:21 +0000
1338@@ -0,0 +1,76 @@
1339+# -*- coding: utf-8 -*-
1340+#
1341+# Copyright 2012 Canonical Ltd.
1342+#
1343+# This program is free software: you can redistribute it and/or modify it
1344+# under the terms of the GNU General Public License version 3, as published
1345+# by the Free Software Foundation.
1346+#
1347+# This program is distributed in the hope that it will be useful, but
1348+# WITHOUT ANY WARRANTY; without even the implied warranties of
1349+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1350+# PURPOSE. See the GNU General Public License for more details.
1351+#
1352+# You should have received a copy of the GNU General Public License along
1353+# with this program. If not, see <http://www.gnu.org/licenses/>.
1354+
1355+"""Tests for the share file widget."""
1356+
1357+import os
1358+
1359+from PyQt4 import QtGui, QtCore
1360+
1361+from ubuntuone.controlpanel.gui.qt import share_file as gui
1362+from ubuntuone.controlpanel.gui.qt.tests import (
1363+ BaseTestCase,
1364+ FakeDesktopService,
1365+)
1366+
1367+# Access to a protected member
1368+# Instance of 'ControlBackend' has no '_called' member
1369+# pylint: disable=W0212, E1103
1370+
1371+
1372+class ShareFileWidgetTestCase(BaseTestCase):
1373+ """Test the ShareFileWidget class."""
1374+
1375+ innerclass_ui = gui.share_file_ui
1376+ innerclass_name = "Ui_Form"
1377+ class_ui = gui.ShareFileWidget
1378+ file_path = u'/home/ñandú/Ubuntu One/my_file.txt'
1379+ kwargs = {'file_path': file_path}
1380+ logger = gui.logger
1381+
1382+ def test_init(self):
1383+ """The share file widget initialization."""
1384+ self.assertEqual(self.ui.file_path, self.file_path)
1385+ self.assertEqual(self.ui.ui.lbl_filename.text(),
1386+ os.path.basename(self.file_path))
1387+ self.assertEqual(self.ui.ui.lbl_path.text(), self.file_path)
1388+
1389+ def test_open_file(self):
1390+ """Test that open file is called properly."""
1391+ fake_desktop_service = FakeDesktopService()
1392+ self.patch(QtGui, "QDesktopServices", fake_desktop_service)
1393+ self.ui.ui.btn_open.click()
1394+
1395+ expected = QtCore.QUrl(u'file://%s' % self.file_path)
1396+ self.assertEqual(expected, fake_desktop_service.opened_url)
1397+
1398+ def test_disable_link(self):
1399+ """Test the disable link action."""
1400+ data = []
1401+
1402+ def fake_change_access(path, access):
1403+ """Fake change access type callback."""
1404+ data.append(path)
1405+ data.append(access)
1406+
1407+ self.ui.linkDisabled.connect(self._set_called)
1408+ self.patch(self.ui.backend, "change_public_access",
1409+ fake_change_access)
1410+ self.ui.ui.btn_disable.click()
1411+
1412+ expected = [os.path.expanduser(self.file_path), False]
1413+ self.assertEqual(data, expected)
1414+ self.assertEqual(self._called, ((), {}))
1415
1416=== modified file 'ubuntuone/controlpanel/gui/qt/tests/test_share_links.py'
1417--- ubuntuone/controlpanel/gui/qt/tests/test_share_links.py 2012-08-20 18:24:20 +0000
1418+++ ubuntuone/controlpanel/gui/qt/tests/test_share_links.py 2012-08-29 18:07:21 +0000
1419@@ -14,15 +14,25 @@
1420 # You should have received a copy of the GNU General Public License along
1421 # with this program. If not, see <http://www.gnu.org/licenses/>.
1422
1423-"""Tests for the account tab."""
1424+"""Tests for the Share Links tab."""
1425+
1426+import os
1427+
1428+from PyQt4 import QtGui, QtCore
1429
1430 from ubuntuone.controlpanel.gui import (
1431 SEARCH_FILES,
1432 SHARED_FILES,
1433 )
1434+from ubuntuone.controlpanel.gui.tests import USER_HOME
1435 from ubuntuone.controlpanel.gui.qt import share_links as gui
1436-from ubuntuone.controlpanel.gui.qt.tests import BaseTestCase
1437-
1438+from ubuntuone.controlpanel.gui.qt.tests import (
1439+ BaseTestCase,
1440+ FakeDesktopService,
1441+)
1442+
1443+
1444+# pylint: disable=W0212
1445
1446 class ShareLinksTestCase(BaseTestCase):
1447 """Test the qt control panel."""
1448@@ -35,3 +45,151 @@
1449 """Check that the strings are properly setted."""
1450 self.assertEqual(self.ui.ui.search_files_lbl.text(), SEARCH_FILES)
1451 self.assertEqual(self.ui.ui.shared_group.title(), SHARED_FILES)
1452+ self.assertIsInstance(self.ui._enhanced_line, gui.EnhancedLineEdit)
1453+ self.assertEqual(self.ui._enhanced_line.btn_operation.text(), '')
1454+ self.assertFalse(self.ui._enhanced_line.btn_operation.isVisible())
1455+
1456+ def test_share_file(self):
1457+ """Check that the state of the widgets on share_file."""
1458+ path = '/home/user/Ubuntu One/file1.txt'
1459+ self.ui.share_file(path)
1460+ self.assertTrue(self.ui.is_processing)
1461+ widget = self.ui.ui.hbox_share_file.takeAt(0).widget()
1462+ self.assertIsInstance(widget, gui.ShareFileWidget)
1463+ self.assertEqual(widget.ui.lbl_filename.text(),
1464+ os.path.basename(path))
1465+ self.assertEqual(widget.ui.lbl_path.text(), path)
1466+
1467+ def test_share_file_actions(self):
1468+ """Check the behaviour of share_file buttons."""
1469+ path = '/home/user/Ubuntu One/file1.txt'
1470+ self.ui.share_file(path)
1471+ widget = self.ui.ui.hbox_share_file.takeAt(0).widget()
1472+ self.ui.ui.line_copy_link.setText('link')
1473+ self.assertEqual(self.ui.ui.line_copy_link.text(), 'link')
1474+ widget.linkDisabled.emit()
1475+ self.assertNotEqual(self.ui.ui.line_copy_link.text(), 'link')
1476+
1477+ def test_file_shared(self):
1478+ """Check the behavior of the widgets after the file is shared."""
1479+ info = {'public_url': 'http://ubuntuone.com/asd123'}
1480+ self.ui._file_shared(info)
1481+ self.assertEqual(self.ui.ui.line_copy_link.text(), info['public_url'])
1482+ self.assertEqual(self.ui.ui.stacked_widget.currentIndex(), 1)
1483+ self.assertFalse(self.ui.is_processing)
1484+
1485+ def test_open_in_browser(self):
1486+ """Test the execution of open_in_browser."""
1487+ fake_desktop_service = FakeDesktopService()
1488+ self.patch(QtGui, "QDesktopServices", fake_desktop_service)
1489+ url = 'http://ubuntuone.com/asd123'
1490+ self.ui.ui.line_copy_link.setText(url)
1491+ self.ui._open_in_browser()
1492+ expected = QtCore.QUrl(url)
1493+ self.assertEqual(expected, fake_desktop_service.opened_url)
1494+
1495+ def test_copy_link_from_line(self):
1496+ """Test the execution of copy_link_from_line."""
1497+ url = 'http://ubuntuone.com/asd123'
1498+ self.ui.ui.line_copy_link.setText(url)
1499+ self.ui._copy_link_from_line()
1500+ clip = QtGui.QApplication.instance().clipboard()
1501+ self.assertEqual(url, clip.text())
1502+
1503+ def test_move_to_main_list(self):
1504+ """Test that the stacked widget shows the proper index."""
1505+ self.ui._move_to_main_list()
1506+ self.assertEqual(self.ui.ui.stacked_widget.currentIndex(), 0)
1507+
1508+ def test_get_public_files(self):
1509+ """Test that the proper actions are executed on files requested.."""
1510+ self.ui.get_public_files()
1511+ self.assertTrue(self.ui.is_processing)
1512+ self.assertEqual(self.ui.ui.stacked_widget.currentIndex(), 0)
1513+ self.assertEqual(self.ui.home_dir, USER_HOME)
1514+
1515+ def test_line_close_btn(self):
1516+ """Check that the popup is hidden."""
1517+ self.ui.ui.line_search.popup.show()
1518+ self.addCleanup(self.ui.ui.line_search.popup.hide)
1519+ self.ui._line_close_btn()
1520+ self.assertFalse(self.ui.ui.line_search.popup.isVisible())
1521+
1522+ def test_hide_line_btn_close_hide(self):
1523+ """Check the state of the inline button."""
1524+ self.ui._enhanced_line.btn_operation.show()
1525+ self.ui.ui.line_search.popup.popupHidden.emit()
1526+ self.assertFalse(self.ui._enhanced_line.btn_operation.isVisible())
1527+
1528+ def test_hide_line_btn_close_show(self):
1529+ """Check the state of the inline button."""
1530+ self.ui.ui.line_search.popup.popupShown.emit()
1531+ self.assertTrue(self.ui._enhanced_line.btn_operation.isVisible())
1532+
1533+ def test_load_public_files(self):
1534+ """Test if the list of public files is loaded properly."""
1535+ publicfiles = [
1536+ {'path': '/home/file1', 'public_url': 'http:ubuntuone.com/asd123'},
1537+ {'path': '/home/file2', 'public_url': 'http:ubuntuone.com/qwe456'},
1538+ ]
1539+ self.ui._load_public_files(publicfiles)
1540+ item = self.ui.ui.tree_shared_files.topLevelItem(0)
1541+ self.assertEqual(item.text(0), os.path.basename('/home/file1'))
1542+ self.assertEqual(item.toolTip(0), '/home/file1')
1543+ label = self.ui.ui.tree_shared_files.itemWidget(item, 1)
1544+ link = ('<a href="%s"><span style="font-size: 12px;'
1545+ 'color: #dd4814";>%s</span></a>'
1546+ % ('http:ubuntuone.com/asd123', 'http:ubuntuone.com/asd123'))
1547+ self.assertEqual(link, label.text())
1548+ actions = self.ui.ui.tree_shared_files.itemWidget(item, 2)
1549+ self.assertIsInstance(actions, gui.ActionsButtons)
1550+
1551+ item = self.ui.ui.tree_shared_files.topLevelItem(1)
1552+ self.assertEqual(item.text(0), os.path.basename('/home/file2'))
1553+ self.assertEqual(item.toolTip(0), '/home/file2')
1554+ label = self.ui.ui.tree_shared_files.itemWidget(item, 1)
1555+ link = ('<a href="%s"><span style="font-size: 12px;'
1556+ 'color: #dd4814";>%s</span></a>'
1557+ % ('http:ubuntuone.com/qwe456', 'http:ubuntuone.com/qwe456'))
1558+ self.assertEqual(link, label.text())
1559+ actions = self.ui.ui.tree_shared_files.itemWidget(item, 2)
1560+ self.assertIsInstance(actions, gui.ActionsButtons)
1561+
1562+
1563+class ActionsButtonsTestCase(BaseTestCase):
1564+ """Test the Actions Buttons."""
1565+
1566+ def test_open(self):
1567+ """Test the open method."""
1568+ path = '/home/file1'
1569+ link = 'http://ubuntuone.com/asd123'
1570+ actions = gui.ActionsButtons(path, link)
1571+ fake_desktop_service = FakeDesktopService()
1572+ self.patch(QtGui, "QDesktopServices", fake_desktop_service)
1573+ actions.open()
1574+ file_path = QtCore.QUrl(u'file://%s' % path)
1575+ self.assertEqual(file_path, fake_desktop_service.opened_url)
1576+
1577+ def test_copy(self):
1578+ """Test that the link is copied into the clipboard.."""
1579+ path = '/home/file1'
1580+ link = 'http://ubuntuone.com/asd123'
1581+ actions = gui.ActionsButtons(path, link)
1582+ fake_desktop_service = FakeDesktopService()
1583+ self.patch(QtGui, "QDesktopServices", fake_desktop_service)
1584+ actions.copy()
1585+ clip = QtGui.QApplication.instance().clipboard()
1586+ self.assertEqual(link, clip.text())
1587+
1588+
1589+class EnhancedLineEditTestCase(BaseTestCase):
1590+ """Test the EnhancedLineEdit."""
1591+
1592+ def test_initialize(self):
1593+ """Test initialization."""
1594+ line = QtGui.QLineEdit()
1595+ enhanced = gui.EnhancedLineEdit(line, self._set_called, 'text')
1596+ self.assertEqual(line.layout().count(), 2)
1597+ self.assertFalse(self._called)
1598+ enhanced.btn_operation.click()
1599+ self.assertEqual(self._called, ((False, ), {}))
1600
1601=== added file 'ubuntuone/controlpanel/gui/qt/tests/test_share_links_search.py'
1602--- ubuntuone/controlpanel/gui/qt/tests/test_share_links_search.py 1970-01-01 00:00:00 +0000
1603+++ ubuntuone/controlpanel/gui/qt/tests/test_share_links_search.py 2012-08-29 18:07:21 +0000
1604@@ -0,0 +1,302 @@
1605+# -*- coding: utf-8 -*-
1606+
1607+# Copyright 2012 Canonical Ltd.
1608+#
1609+# This program is free software: you can redistribute it and/or modify it
1610+# under the terms of the GNU General Public License version 3, as published
1611+# by the Free Software Foundation.
1612+#
1613+# This program is distributed in the hope that it will be useful, but
1614+# WITHOUT ANY WARRANTY; without even the implied warranties of
1615+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1616+# PURPOSE. See the GNU General Public License for more details.
1617+#
1618+# You should have received a copy of the GNU General Public License along
1619+# with this program. If not, see <http://www.gnu.org/licenses/>.
1620+
1621+"""Tests for the Share Links Search."""
1622+
1623+import os
1624+
1625+from twisted.internet import defer
1626+
1627+from ubuntuone.controlpanel.gui.tests import USER_HOME
1628+from ubuntuone.controlpanel.gui.qt import share_links_search as gui
1629+from ubuntuone.controlpanel.gui.qt.tests import BaseTestCase
1630+
1631+
1632+# pylint: disable=W0212
1633+
1634+class SearchBoxTestCase(BaseTestCase):
1635+ """Test the qt control panel."""
1636+
1637+ class_ui = gui.SearchBox
1638+
1639+ @defer.inlineCallbacks
1640+ def setUp(self):
1641+ yield super(SearchBoxTestCase, self).setUp()
1642+
1643+ self.patch(self.ui._thread_explore, "get_folder_info",
1644+ self.fake_get_folder_info)
1645+ self.patch(self.ui._thread_explore, "start",
1646+ lambda *args, **kwargs: self.ui._thread_explore.run())
1647+ self.folder_info = {
1648+ 'folder1': [
1649+ os.path.join(USER_HOME, 'ubuntu', 'file1'),
1650+ os.path.join(USER_HOME, 'ubuntu', 'file2'),
1651+ os.path.join(USER_HOME, 'one', 'file3'),
1652+ ],
1653+ 'folder2': [
1654+ os.path.join(USER_HOME, 'test', 'asd'),
1655+ os.path.join('other_path', 'test', 'qwe'),
1656+ os.path.join(USER_HOME, 'blabla', 'iop'),
1657+ ]
1658+ }
1659+
1660+ def fake_get_folder_info(self, folder):
1661+ """Fake get_folder_info."""
1662+ return self.folder_info.get(folder, [])
1663+
1664+ def test_initialization(self):
1665+ """Check that the widget is build properly"""
1666+ self.assertEqual(self.ui.home_dir, USER_HOME)
1667+ self.assertEqual(self.ui._thread_explore.u1_files, [])
1668+ self.assertEqual(self.ui.temp_u1_files, [])
1669+ self.assertEqual(self.ui.items_per_page, 0)
1670+ self.assertEqual(self.ui.items_step, 20)
1671+ self.assertEqual(self.ui.prefix, '')
1672+
1673+ def test_key_down_pressed(self):
1674+ """Check the proper action are executed on key down pressed."""
1675+ data1 = [{'path': 'folder1'}]
1676+ data2 = [{'realpath': 'folder2'}]
1677+ self.ui._process_volumes_info([(0, 0, data1), (0, 0, data2)])
1678+ self.ui.popup.list_widget.setCurrentRow(1)
1679+ current = self.ui.popup.list_widget.currentRow()
1680+ self.ui._key_down_pressed(current)
1681+ self.assertEqual(self.ui.popup.list_widget.currentRow(), 2)
1682+
1683+ def test_key_down_pressed_load_more_items(self):
1684+ """Check the proper action are executed on key down pressed."""
1685+ data = []
1686+
1687+ def fake_fetch_more(filenames):
1688+ """Fake fetch_more."""
1689+ data.append(True)
1690+
1691+ self.patch(self.ui.popup, "fetch_more", fake_fetch_more)
1692+ data1 = [{'path': 'folder1'}]
1693+ data2 = [{'realpath': 'folder2'}]
1694+ self.ui._process_volumes_info([(0, 0, data1), (0, 0, data2),
1695+ (0, 0, data1), (0, 0, data2), (0, 0, data1), (0, 0, data2),
1696+ (0, 0, data1), (0, 0, data2), (0, 0, data1), (0, 0, data2),
1697+ (0, 0, data1), (0, 0, data2), (0, 0, data1), (0, 0, data2)])
1698+ self.assertEqual(self.ui.popup.list_widget.count(), 20)
1699+ self.ui.popup.list_widget.setCurrentRow(
1700+ self.ui.popup.list_widget.count() - 5)
1701+ current = self.ui.popup.list_widget.currentRow()
1702+ self.ui._key_down_pressed(current)
1703+ self.assertEqual(data, [True])
1704+
1705+ def test_key_up_pressed(self):
1706+ """Check the proper action are executed on key up pressed."""
1707+ data1 = [{'path': 'folder1'}]
1708+ data2 = [{'realpath': 'folder2'}]
1709+ self.ui._process_volumes_info([(0, 0, data1), (0, 0, data2)])
1710+ self.ui.popup.list_widget.setCurrentRow(1)
1711+ current = self.ui.popup.list_widget.currentRow()
1712+ self.ui._key_up_pressed(current)
1713+ self.assertEqual(self.ui.popup.list_widget.currentRow(), 0)
1714+
1715+ def test_key_up_pressed_stay_in_0(self):
1716+ """Check the proper action are executed on key up pressed."""
1717+ data1 = [{'path': 'folder1'}]
1718+ data2 = [{'realpath': 'folder2'}]
1719+ self.ui._process_volumes_info([(0, 0, data1), (0, 0, data2)])
1720+ self.ui.popup.list_widget.setCurrentRow(0)
1721+ current = self.ui.popup.list_widget.currentRow()
1722+ self.assertEqual(self.ui.popup.list_widget.currentRow(), 0)
1723+ self.ui._key_up_pressed(current)
1724+ self.assertEqual(self.ui.popup.list_widget.currentRow(), 0)
1725+
1726+ def test_key_return_pressed(self):
1727+ """Check the proper action are executed on key return pressed."""
1728+ data1 = [{'path': 'folder1'}]
1729+ data2 = [{'realpath': 'folder2'}]
1730+ self.ui._process_volumes_info([(0, 0, data1), (0, 0, data2)])
1731+ self.ui.popup.list_widget.setCurrentRow(1)
1732+ current = self.ui.popup.list_widget.currentRow()
1733+ self.ui._key_return_pressed(current)
1734+
1735+ def test_key_space_pressed(self):
1736+ """Check the proper action are executed on key space pressed."""
1737+ data = []
1738+
1739+ def fake_set_text(text):
1740+ """fake setText."""
1741+ data.append(text)
1742+
1743+ self.patch(self.ui, "setText", fake_set_text)
1744+ data1 = [{'path': 'folder1'}]
1745+ data2 = [{'realpath': 'folder2'}]
1746+ self.ui._process_volumes_info([(0, 0, data1), (0, 0, data2)])
1747+ self.ui.popup.list_widget.setCurrentRow(1)
1748+
1749+ self.ui._key_space_pressed()
1750+ expected = ['iop']
1751+ self.assertEqual(expected, data)
1752+
1753+ def test_process_volumes_info(self):
1754+ """Check that _process_volumes_info obtain the proper info."""
1755+ data1 = [{'path': 'folder1'}]
1756+ data2 = [{'realpath': 'folder2'}]
1757+ self.ui._process_volumes_info([(0, 0, data1), (0, 0, data2)])
1758+ expected = [
1759+ 'other_path/test/qwe',
1760+ '~/blabla/iop',
1761+ '~/one/file3',
1762+ '~/test/asd',
1763+ '~/ubuntu/file1',
1764+ '~/ubuntu/file2']
1765+ self.assertEqual(self.ui._thread_explore.u1_files, expected)
1766+ self.assertEqual(self.ui.popup.list_widget.count(), 6)
1767+
1768+ def test_filter(self):
1769+ """Check the results of the filter."""
1770+ # This tests the behavior of:
1771+ # filter
1772+ # _show_filter
1773+ # _get_filtered_list
1774+ self.patch(self.ui.popup, "isVisible", lambda: True)
1775+ data1 = [{'path': 'folder1'}]
1776+ data2 = [{'realpath': 'folder2'}]
1777+ self.ui._process_volumes_info([(0, 0, data1), (0, 0, data2)])
1778+ self.ui.filter('p')
1779+ expected = ['~/blabla/iop']
1780+ self.assertEqual(expected, self.ui.temp_u1_files)
1781+
1782+ self.ui.filter('i')
1783+ expected = [
1784+ '~/blabla/iop',
1785+ '~/one/file3',
1786+ '~/ubuntu/file1',
1787+ '~/ubuntu/file2']
1788+ self.assertEqual(expected, self.ui.temp_u1_files)
1789+
1790+ def test_set_selected_item(self):
1791+ """Check the notification of the selected item."""
1792+ self.ui.itemSelected.connect(self._set_called)
1793+ self.patch(self.ui.popup, "isVisible", lambda: True)
1794+ data1 = [{'path': 'folder1'}]
1795+ data2 = [{'realpath': 'folder2'}]
1796+ self.ui._process_volumes_info([(0, 0, data1), (0, 0, data2)])
1797+ self.ui.popup.list_widget.setCurrentRow(0)
1798+ item = self.ui.popup.list_widget.currentItem()
1799+ self.ui._set_selected_item(item)
1800+ self.assertEqual(self._called, ((u'other_path/test/qwe',), {}))
1801+ self.assertEqual(self.ui.text(), '')
1802+
1803+
1804+class FileItemTestCase(BaseTestCase):
1805+ """Test the File Item."""
1806+
1807+ class_ui = gui.FileItem
1808+ file_path = '/home/tester/ubuntu/file1.txt'
1809+ kwargs = {'file_path': file_path}
1810+
1811+ def test_default(self):
1812+ """Check the default style of the item"""
1813+ name = os.path.basename(self.file_path)
1814+ style = self.ui.text_style.format(name, self.file_path,
1815+ '#333333', 'grey')
1816+ self.assertEqual(self.ui.text(), style)
1817+
1818+ def test_selected(self):
1819+ """Check the default style of the item"""
1820+ self.ui.set_selected()
1821+ name = os.path.basename(self.file_path)
1822+ style = self.ui.text_style.format(name, self.file_path,
1823+ 'white', 'white')
1824+ self.assertEqual(self.ui.text(), style)
1825+
1826+ def test_not_selected(self):
1827+ """Check the default style of the item"""
1828+ self.ui.set_not_selected()
1829+ name = os.path.basename(self.file_path)
1830+ style = self.ui.text_style.format(name, self.file_path,
1831+ '#333333', 'grey')
1832+ self.assertEqual(self.ui.text(), style)
1833+
1834+
1835+class FilesPopupTestCase(BaseTestCase):
1836+
1837+ """FilesPopup tests."""
1838+
1839+ class_ui = gui.FilesPopup
1840+ text_style = (u"<span style='color: {2};'>{0}</span><br>"
1841+ "<span style='font-size: 13px; color: {3};'>({1})</span>")
1842+
1843+ def test_load_items(self):
1844+ """Tests that the items are loaded properly."""
1845+ items = [
1846+ '/home/tester/file1',
1847+ '/home/tester/file2',
1848+ '/home/tester/file3',
1849+ ]
1850+
1851+ self.assertEqual(self.ui.list_widget.count(), 0)
1852+ self.ui.load_items(items)
1853+ self.assertEqual(self.ui.list_widget.count(), 3)
1854+ # Check that we erase the list on reload
1855+ self.ui.load_items(items)
1856+ self.assertEqual(self.ui.list_widget.count(), 3)
1857+
1858+ def test_fetch_more(self):
1859+ """Tests that the items are loaded properly."""
1860+ items = [
1861+ '/home/tester/file1',
1862+ '/home/tester/file2',
1863+ '/home/tester/file3',
1864+ ]
1865+
1866+ self.assertEqual(self.ui.list_widget.count(), 0)
1867+ self.ui.load_items(items)
1868+ self.assertEqual(self.ui.list_widget.count(), 3)
1869+ self.ui.fetch_more(items)
1870+ self.assertEqual(self.ui.list_widget.count(), 6)
1871+
1872+ def test_repaint_items(self):
1873+ """Check the style of the items change acording to the selection."""
1874+ items = [
1875+ '/home/tester/file1',
1876+ '/home/tester/file2',
1877+ '/home/tester/file3',
1878+ ]
1879+
1880+ self.ui.load_items(items)
1881+ current = self.ui.list_widget.item(0)
1882+ widget = self.ui.list_widget.itemWidget(current)
1883+ next_ = self.ui.list_widget.item(1)
1884+ widget2 = self.ui.list_widget.itemWidget(next_)
1885+ name = os.path.basename('/home/tester/file1')
1886+ style = self.text_style.format(name, '/home/tester/file1',
1887+ 'white', 'white')
1888+ name2 = os.path.basename('/home/tester/file2')
1889+ style2 = self.text_style.format(name2, '/home/tester/file2',
1890+ '#333333', 'grey')
1891+ self.assertEqual(widget.text(), style)
1892+ self.assertEqual(widget2.text(), style2)
1893+
1894+ self.ui.list_widget.setCurrentRow(1)
1895+ current = self.ui.list_widget.item(1)
1896+ widget = self.ui.list_widget.itemWidget(current)
1897+ previous = self.ui.list_widget.item(0)
1898+ widget2 = self.ui.list_widget.itemWidget(previous)
1899+ name = os.path.basename('/home/tester/file2')
1900+ style = self.text_style.format(name, '/home/tester/file2',
1901+ 'white', 'white')
1902+ name2 = os.path.basename('/home/tester/file1')
1903+ style2 = self.text_style.format(name2, '/home/tester/file1',
1904+ '#333333', 'grey')
1905+ self.assertEqual(widget.text(), style)
1906+ self.assertEqual(widget2.text(), style2)
1907
1908=== modified file 'ubuntuone/controlpanel/gui/qt/tests/test_systray.py'
1909--- ubuntuone/controlpanel/gui/qt/tests/test_systray.py 2012-08-20 14:09:45 +0000
1910+++ ubuntuone/controlpanel/gui/qt/tests/test_systray.py 2012-08-29 18:07:21 +0000
1911@@ -31,7 +31,10 @@
1912 UPLOADING,
1913 )
1914 from ubuntuone.controlpanel.gui.qt import systray
1915-from ubuntuone.controlpanel.gui.qt.tests import BaseTestCase
1916+from ubuntuone.controlpanel.gui.qt.tests import (
1917+ BaseTestCase,
1918+ FakeDesktopService,
1919+)
1920 from ubuntuone.controlpanel.tests import ROOT_PATH
1921
1922
1923@@ -59,19 +62,6 @@
1924 super(FakeMainWindow, self).__init__()
1925
1926
1927-class FakeDesktopService(object):
1928-
1929- """Fake QDesktopService."""
1930-
1931- data = {}
1932-
1933- @classmethod
1934- def openUrl(cls, url):
1935- """Fake openUrl."""
1936- FakeDesktopService.data['cls'] = cls
1937- FakeDesktopService.data['url'] = url
1938-
1939-
1940 class SystrayTestCase(BaseTestCase):
1941
1942 """Test the notification area icon."""
1943@@ -85,7 +75,8 @@
1944 self.patch(systray.TrayIcon, "startTimer", lambda s, x: None)
1945 self.patch(systray.TransfersMenu, "startTimer", lambda s, x: None)
1946 yield super(SystrayTestCase, self).setUp()
1947- self.patch(QtGui, "QDesktopServices", FakeDesktopService)
1948+ self.fake_desktop_service = FakeDesktopService()
1949+ self.patch(QtGui, "QDesktopServices", self.fake_desktop_service)
1950
1951 def assert_status_correct(self, status_bd, status_ui, action,
1952 callback=None):
1953@@ -278,30 +269,26 @@
1954 def test_open_u1_folder_action(self):
1955 """Test open_u1_folder_action."""
1956 self.ui.open_u1_folder.trigger()
1957- self.assertEqual(FakeDesktopService.data['cls'], FakeDesktopService)
1958 expected_url = QtCore.QUrl(u'file://%s' % ROOT_PATH)
1959- self.assertEqual(FakeDesktopService.data['url'], expected_url)
1960+ self.assertEqual(self.fake_desktop_service.opened_url, expected_url)
1961
1962 def test_get_more_storage_action(self):
1963 """Test get_more_storage."""
1964 self.ui.get_more_storage.trigger()
1965- self.assertEqual(FakeDesktopService.data['cls'], FakeDesktopService)
1966 expected_url = QtCore.QUrl(systray.GET_STORAGE_LINK)
1967- self.assertEqual(FakeDesktopService.data['url'], expected_url)
1968+ self.assertEqual(self.fake_desktop_service.opened_url, expected_url)
1969
1970 def test_go_to_web_action(self):
1971 """Test go_to_web."""
1972 self.ui.go_to_web.trigger()
1973- self.assertEqual(FakeDesktopService.data['cls'], FakeDesktopService)
1974 expected_url = QtCore.QUrl(systray.DASHBOARD)
1975- self.assertEqual(FakeDesktopService.data['url'], expected_url)
1976+ self.assertEqual(self.fake_desktop_service.opened_url, expected_url)
1977
1978 def test_get_help_action(self):
1979 """Test get_help_online."""
1980 self.ui.get_help_online.trigger()
1981- self.assertEqual(FakeDesktopService.data['cls'], FakeDesktopService)
1982 expected_url = QtCore.QUrl(systray.GET_SUPPORT_LINK)
1983- self.assertEqual(FakeDesktopService.data['url'], expected_url)
1984+ self.assertEqual(self.fake_desktop_service.opened_url, expected_url)
1985
1986 def test_initialization(self):
1987 """Test that everything initializes correctly."""
1988@@ -353,7 +340,8 @@
1989 self.patch(systray.TrayIcon, "startTimer", lambda s, x: None)
1990 self.patch(systray.TransfersMenu, "startTimer", lambda s, x: None)
1991 yield super(TransfersMenuTestCase, self).setUp()
1992- self.patch(QtGui, "QDesktopServices", FakeDesktopService)
1993+ self.fake_desktop_service = FakeDesktopService()
1994+ self.patch(QtGui, "QDesktopServices", self.fake_desktop_service)
1995 self.patch(self.ui.transfers._parent.backend, "sync_menu",
1996 self.fake_sync_menu)
1997 self.recent_transfers = []
1998@@ -558,7 +546,8 @@
1999 self.patch(systray.TrayIcon, "startTimer", lambda s, x: None)
2000 self.patch(systray.TransfersMenu, "startTimer", lambda s, x: None)
2001 yield super(SharesTestCase, self).setUp()
2002- self.patch(QtGui, "QDesktopServices", FakeDesktopService)
2003+ self.fake_desktop_service = FakeDesktopService()
2004+ self.patch(QtGui, "QDesktopServices", self.fake_desktop_service)
2005 self.patch(self.ui.backend, "get_shares", self.fake_get_backend)
2006 self._shares_info = []
2007
2008@@ -596,4 +585,4 @@
2009 share_action = actions[7]
2010 share_action.trigger()
2011 expected = QtCore.QUrl(systray.ACCEPT_SHARES % volume_id)
2012- self.assertEqual(FakeDesktopService.data['url'], expected)
2013+ self.assertEqual(self.fake_desktop_service.opened_url, expected)
2014
2015=== modified file 'ubuntuone/controlpanel/sd_client/__init__.py'
2016--- ubuntuone/controlpanel/sd_client/__init__.py 2012-08-08 18:44:57 +0000
2017+++ ubuntuone/controlpanel/sd_client/__init__.py 2012-08-29 18:07:21 +0000
2018@@ -209,3 +209,23 @@
2019 def sync_menu(self):
2020 """Get the sync menu data from syncdaemon."""
2021 return self.proxy.sync_menu()
2022+
2023+ def get_public_files(self):
2024+ """Trigger an action to get the list of public files."""
2025+ return self.proxy.get_public_files()
2026+
2027+ def set_public_files_list_handler(self, handler):
2028+ """Set the public files list handler."""
2029+ return self.proxy.connect_signal('PublicFilesList', handler)
2030+
2031+ def change_public_access(self, path, is_public):
2032+ """Change the access type of the file."""
2033+ return self.proxy.change_public_access(path, is_public)
2034+
2035+ def set_public_access_changed_handler(self, handler):
2036+ """Set the status handler function."""
2037+ return self.proxy.connect_signal('PublicAccessChanged', handler)
2038+
2039+ def set_public_access_change_error_handler(self, handler):
2040+ """Set the status handler function."""
2041+ return self.proxy.connect_signal('PublicAccessChangeError', handler)
2042
2043=== modified file 'ubuntuone/controlpanel/tests/test_backend.py'
2044--- ubuntuone/controlpanel/tests/test_backend.py 2012-08-08 18:36:45 +0000
2045+++ ubuntuone/controlpanel/tests/test_backend.py 2012-08-29 18:07:21 +0000
2046@@ -161,6 +161,8 @@
2047 'is_error': '', 'is_connected': 'True', 'is_online': '',
2048 }
2049 self.status_changed_handler = None
2050+ self.public_files_list_handler = None
2051+ self.public_access_changed_handler = None
2052 self.subscribed_folders = []
2053 self.subscribed_shares = []
2054 self.actions = []
2055@@ -168,6 +170,13 @@
2056 self.folders = []
2057 self.volumes_refreshed = False
2058 self.menu_data = {'recent-transfers': (), 'uploading': ()}
2059+ self.publicfiles = [
2060+ {'path': '/home/file1', 'public_url': 'http:ubuntuone.com/asd123'},
2061+ {'path': '/home/file2', 'public_url': 'http:ubuntuone.com/qwe456'},
2062+ ]
2063+ self.public_access_info = {
2064+ 'public_url': 'http:ubuntuone.com/zxc789'
2065+ }
2066
2067 def get_throttling_limits(self):
2068 """Return the sample speed limits."""
2069@@ -331,6 +340,24 @@
2070 """Return the sync menu data."""
2071 return self.menu_data
2072
2073+ def get_public_files(self):
2074+ """Trigger the action to get the public files."""
2075+ if self.public_files_list_handler is not None:
2076+ self.public_files_list_handler(self.publicfiles)
2077+
2078+ def set_public_files_list_handler(self, handler):
2079+ """Return the handler to be called for the public files list."""
2080+ self.public_files_list_handler = handler
2081+
2082+ def change_public_access(self, path, is_public):
2083+ """Change the type access of a file."""
2084+ if self.public_access_changed_handler is not None and is_public:
2085+ self.public_access_changed_handler(self.public_access_info)
2086+
2087+ def set_public_access_changed_handler(self, handler):
2088+ """Return the handler to be called when a access type change."""
2089+ self.public_access_changed_handler = handler
2090+
2091
2092 class MockReplicationClient(CallRecorder):
2093 """A mock replication_client module."""
2094@@ -1709,3 +1736,40 @@
2095 result = yield self.be.sync_menu()
2096 expected = {'recent-transfers': (), 'uploading': ()}
2097 self.assertEqual(result, expected)
2098+
2099+ @inlineCallbacks
2100+ def test_get_public_files(self):
2101+ """Check that we get list of public files."""
2102+ data = []
2103+
2104+ def get_public_files_handler(publicfiles):
2105+ """Callback for get_public_files."""
2106+ data.append(publicfiles)
2107+
2108+ self.be.set_public_files_list_handler(get_public_files_handler)
2109+ yield self.be.get_public_files()
2110+ expected = [[{'path': '/home/file1',
2111+ 'public_url': 'http:ubuntuone.com/asd123'},
2112+ {'path': '/home/file2',
2113+ 'public_url': 'http:ubuntuone.com/qwe456'}]]
2114+ self.assertEqual(expected, data)
2115+
2116+ @inlineCallbacks
2117+ def test_get_shares(self):
2118+ """Check that we get the list of shares."""
2119+ result = yield self.be.get_shares()
2120+ self.assertEqual(result, [])
2121+
2122+ @inlineCallbacks
2123+ def test_change_public_access_set_public(self):
2124+ """Check that we get a notification when the access type change."""
2125+ data = []
2126+
2127+ def public_access_change_handler(info):
2128+ """Callback for get_public_files."""
2129+ data.append(info)
2130+
2131+ self.be.set_public_access_changed_handler(public_access_change_handler)
2132+ yield self.be.change_public_access('file1', True)
2133+ expected = {'public_url': 'http:ubuntuone.com/zxc789'}
2134+ self.assertEqual([expected], data)
2135
2136=== modified file 'ubuntuone/controlpanel/tests/test_sd_client.py'
2137--- ubuntuone/controlpanel/tests/test_sd_client.py 2012-03-29 21:37:22 +0000
2138+++ ubuntuone/controlpanel/tests/test_sd_client.py 2012-08-29 18:07:21 +0000
2139@@ -746,3 +746,31 @@
2140 """Handle error when retrieving current syncdaemon status."""
2141 self.patch(self.sd.proxy, 'get_status', self.fake_fail)
2142 yield self.assertFailure(self.sd.get_current_status(), CustomError)
2143+
2144+
2145+class PublicFilesTestCase(BaseTestCase):
2146+ """Test for the public files syncdaemon client methods."""
2147+
2148+ @inlineCallbacks
2149+ def test_set_public_files_list_handler(self):
2150+ """Connect a handler to the public files signal."""
2151+ sample_handler = object()
2152+ yield self.sd.set_public_files_list_handler(sample_handler)
2153+ self.assertEqual(sample_handler,
2154+ self.sd.proxy.called['PublicFilesList'])
2155+
2156+ @inlineCallbacks
2157+ def test_set_public_access_changed_handler(self):
2158+ """Connect a handler to the public access changed signal."""
2159+ sample_handler = object()
2160+ yield self.sd.set_public_access_changed_handler(sample_handler)
2161+ self.assertEqual(sample_handler,
2162+ self.sd.proxy.called['PublicAccessChanged'])
2163+
2164+ @inlineCallbacks
2165+ def test_set_public_access_change_error_handler(self):
2166+ """Connect a handler to the public access change error signal."""
2167+ sample_handler = object()
2168+ yield self.sd.set_public_access_change_error_handler(sample_handler)
2169+ self.assertEqual(sample_handler,
2170+ self.sd.proxy.called['PublicAccessChangeError'])

Subscribers

People subscribed via source and target branches

to all changes: