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

Proposed by dobey
Status: Merged
Approved by: dobey
Approved revision: no longer in the source branch.
Merged at revision: 375
Proposed branch: lp:~dobey/ubuntuone-control-panel/update-4-2
Merge into: lp:ubuntuone-control-panel/stable-4-2
Diff against target: 2467 lines (+1014/-333)
34 files modified
data/qt/images.qrc (+1/-0)
data/qt/share_links.ui (+175/-20)
data/qt/ubuntuone.qss (+1/-1)
run-tests (+1/-1)
setup.py (+5/-7)
ubuntuone-control-panel-qt-gnome.desktop.in (+11/-0)
ubuntuone-control-panel.in (+0/-1)
ubuntuone-installer.desktop.in (+1/-0)
ubuntuone/controlpanel/backend.py (+13/-4)
ubuntuone/controlpanel/gui/__init__.py (+7/-1)
ubuntuone/controlpanel/gui/qt/controlpanel.py (+10/-5)
ubuntuone/controlpanel/gui/qt/folders.py (+4/-3)
ubuntuone/controlpanel/gui/qt/gui.py (+16/-0)
ubuntuone/controlpanel/gui/qt/main/__init__.py (+22/-6)
ubuntuone/controlpanel/gui/qt/main/darwin.py (+52/-8)
ubuntuone/controlpanel/gui/qt/main/tests/test_darwin.py (+104/-5)
ubuntuone/controlpanel/gui/qt/main/tests/test_main.py (+18/-8)
ubuntuone/controlpanel/gui/qt/share_links.py (+86/-20)
ubuntuone/controlpanel/gui/qt/share_links_search.py (+52/-32)
ubuntuone/controlpanel/gui/qt/systray.py (+53/-68)
ubuntuone/controlpanel/gui/qt/tests/test_controlpanel.py (+3/-4)
ubuntuone/controlpanel/gui/qt/tests/test_folders.py (+6/-5)
ubuntuone/controlpanel/gui/qt/tests/test_share_links.py (+51/-9)
ubuntuone/controlpanel/gui/qt/tests/test_share_links_search.py (+47/-41)
ubuntuone/controlpanel/gui/qt/tests/test_start.py (+11/-0)
ubuntuone/controlpanel/gui/qt/tests/test_systray.py (+45/-57)
ubuntuone/controlpanel/gui/qt/tests/test_wizard.py (+16/-5)
ubuntuone/controlpanel/gui/qt/uniqueapp/__init__.py (+56/-0)
ubuntuone/controlpanel/gui/qt/uniqueapp/tests/test_unique_app.py (+99/-4)
ubuntuone/controlpanel/gui/qt/wizard.py (+7/-3)
ubuntuone/controlpanel/gui/tests/__init__.py (+7/-7)
ubuntuone/controlpanel/tests/test_backend.py (+30/-4)
ubuntuone/controlpanel/utils/darwin.py (+2/-2)
ubuntuone/controlpanel/web_client.py (+2/-2)
To merge this branch: bzr merge lp:~dobey/ubuntuone-control-panel/update-4-2
Reviewer Review Type Date Requested Status
Diego Sarmentero (community) Approve
Review via email: mp+138747@code.launchpad.net

Commit message

[Mike McCracken]

    - Trivial fix to make a test in test_folders actually run.
    - Add support for menu in separate process on darwin. (LP: #1045939)
    - Connect files service in wizard to enable display of remote folders. (LP: #978043)

[Rodney Dawes]

    - Remove messaging menu integration file.
    - Ignore some of the new pep8 warnings, and fix some others.

[Brian Curtin]

    - Encode the volume path before checking whether or not it is a link.

[Barry Warsaw]

    - Replace the third party simplejson module with the stdlib json module.

[Jeremy Bicha]

    - Don't show Ubuntu One in System Settings when running GNOME.

[Diego Sarmentero]

    - Adding IPC, so the new instances can send messages to the already running instance (LP: #1063927).
    - Remove the text on x button pressed (LP: #1070917).
    - Implementing new share tab design (LP: #1065194, #1056194).
    - Accessing the Share tab from the systray menu and avoid refreshing the menu unnecesary (LP: #1072859 #1063781).

To post a comment you must log in.
Revision history for this message
Diego Sarmentero (diegosarmentero) wrote :

+1
"For a few to be immortal, many must die." [In Time]

review: Approve
Revision history for this message
Ubuntu One Auto Pilot (otto-pilot) wrote :

There was a problem validating some authors of the branch. Authors must be either one of the listed Launchpad users, or a member of one of the listed teams on Launchpad.

Persons or Teams:

    contributor-agreement-canonical
    ubuntuone-hackers

Unaccepted Authors:

    Barry Warsaw <email address hidden>

375. By Diego Sarmentero

[Mike McCracken]

    - Trivial fix to make a test in test_folders actually run.
    - Add support for menu in separate process on darwin. (LP: #1045939)
    - Connect files service in wizard to enable display of remote folders. (LP: #978043)

[Rodney Dawes]

    - Remove messaging menu integration file.
    - Ignore some of the new pep8 warnings, and fix some others.

[Brian Curtin]

    - Encode the volume path before checking whether or not it is a link.

[Barry Warsaw]

    - Replace the third party simplejson module with the stdlib json module.

[Jeremy Bicha]

    - Don't show Ubuntu One in System Settings when running GNOME.

[Diego Sarmentero]

    - Adding IPC, so the new instances can send messages to the already running instance (LP: #1063927).
    - Remove the text on x button pressed (LP: #1070917).
    - Implementing new share tab design (LP: #1065194, #1056194).
    - Accessing the Share tab from the systray menu and avoid refreshing the menu unnecesary (LP: #1072859 #1063781).

Preview Diff

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

Subscribers

People subscribed via source and target branches

to all changes: