Merge lp:~3v1n0/unity/volume-icon-dnd into lp:unity

Proposed by Marco Trevisan (Treviño) on 2013-10-07
Status: Merged
Approved by: Brandon Schaefer on 2013-10-07
Approved revision: 3572
Merged at revision: 3558
Proposed branch: lp:~3v1n0/unity/volume-icon-dnd
Merge into: lp:unity
Prerequisite: lp:~3v1n0/unity/volume-filemanager-in-icon
Diff against target: 695 lines (+166/-103)
9 files modified
launcher/ApplicationLauncherIcon.cpp (+3/-13)
launcher/ApplicationLauncherIcon.h (+0/-1)
launcher/VolumeLauncherIcon.cpp (+40/-4)
launcher/VolumeLauncherIcon.h (+4/-1)
tests/test_mock_filemanager.h (+1/-0)
tests/test_volume_launcher_icon.cpp (+82/-84)
unity-shared/FileManager.h (+2/-0)
unity-shared/GnomeFileManager.cpp (+32/-0)
unity-shared/GnomeFileManager.h (+2/-0)
To merge this branch: bzr merge lp:~3v1n0/unity/volume-icon-dnd
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Approve on 2013-10-08
Brandon Schaefer (community) Approve on 2013-10-07
Andrea Azzarone (community) 2013-10-07 Needs Information on 2013-10-07
Review via email: mp+189687@code.launchpad.net

This proposal supersedes a proposal from 2013-10-07.

Commit message

VolumeLauncherIcon: copy files to device on DnD

Use filemanager to perform the copy action. If needed, delay the procedure until we
don't have the volume mounted (using a magic utility function to share the code).

To post a comment you must log in.
Andrea Azzarone (azzar1) wrote :

Why not using g_file_copy_*?

review: Needs Information
Marco Trevisan (Treviño) (3v1n0) wrote :

> Why not using g_file_copy_*?

Because using it would have needed to add a progress handler inside unity (progress bar over the icon + quicklist items to manage it?) separated to the nautilus one.
Using nautilus, we get instead for free the ability to copy files having a progress dialog and a dialog handling the file conflicts. All this keeping the consistency with the normal copy operation we'd have using the system tools.

So, until we don't reimplement all this inside unity, I think that using the environment facilities is better.

PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
lp:~3v1n0/unity/volume-icon-dnd updated on 2013-10-07
3572. By Marco Trevisan (Treviño) on 2013-10-07

GnomeFileManager: don't try to copy files from o to invalid locations

Brandon Schaefer (brandontschaefer) wrote :

LGTM

review: Approve
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Andrea Azzarone (azzar1) wrote :

Ok makes sense :D

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'launcher/ApplicationLauncherIcon.cpp'
2--- launcher/ApplicationLauncherIcon.cpp 2013-10-04 03:55:52 +0000
3+++ launcher/ApplicationLauncherIcon.cpp 2013-10-07 22:37:56 +0000
4@@ -1112,16 +1112,6 @@
5 return _remote_uri;
6 }
7
8-std::set<std::string> ApplicationLauncherIcon::ValidateUrisForLaunch(DndData const& uris)
9-{
10- std::set<std::string> result;
11-
12- for (auto uri : uris.Uris())
13- result.insert(uri);
14-
15- return result;
16-}
17-
18 void ApplicationLauncherIcon::OnDndHovered()
19 {
20 // for now, let's not do this, it turns out to be quite buggy
21@@ -1159,7 +1149,7 @@
22 {
23 if (IsFileManager())
24 {
25- for (auto uri : dnd_data.Uris())
26+ for (auto const& uri : dnd_data.Uris())
27 {
28 if (boost::algorithm::starts_with(uri, "file://"))
29 return true;
30@@ -1185,7 +1175,7 @@
31 nux::DndAction ApplicationLauncherIcon::OnQueryAcceptDrop(DndData const& dnd_data)
32 {
33 #ifdef USE_X11
34- return ValidateUrisForLaunch(dnd_data).empty() ? nux::DNDACTION_NONE : nux::DNDACTION_COPY;
35+ return dnd_data.Uris().empty() ? nux::DNDACTION_NONE : nux::DNDACTION_COPY;
36 #else
37 return nux::DNDACTION_NONE;
38 #endif
39@@ -1194,7 +1184,7 @@
40 void ApplicationLauncherIcon::OnAcceptDrop(DndData const& dnd_data)
41 {
42 auto timestamp = nux::GetGraphicsDisplay()->GetCurrentEvent().x11_timestamp;
43- OpenInstanceWithUris(ValidateUrisForLaunch(dnd_data), timestamp);
44+ OpenInstanceWithUris(dnd_data.Uris(), timestamp);
45 }
46
47 bool ApplicationLauncherIcon::ShowInSwitcher(bool current)
48
49=== modified file 'launcher/ApplicationLauncherIcon.h'
50--- launcher/ApplicationLauncherIcon.h 2013-09-11 10:18:31 +0000
51+++ launcher/ApplicationLauncherIcon.h 2013-10-07 22:37:56 +0000
52@@ -93,7 +93,6 @@
53 nux::DndAction OnQueryAcceptDrop(DndData const& dnd_data);
54
55 MenuItemsVector GetMenus();
56- std::set<std::string> ValidateUrisForLaunch(DndData const& dnd_data);
57
58 std::string GetRemoteUri() const;
59
60
61=== modified file 'launcher/VolumeLauncherIcon.cpp'
62--- launcher/VolumeLauncherIcon.cpp 2013-10-07 16:49:04 +0000
63+++ launcher/VolumeLauncherIcon.cpp 2013-10-07 22:37:56 +0000
64@@ -133,13 +133,13 @@
65 volume_->StopDrive();
66 }
67
68- void OpenInFileManager(uint64_t timestamp)
69+ void DoActionWhenMounted(std::function<void()> const& callback)
70 {
71 if (!volume_->IsMounted())
72 {
73 auto conn = std::make_shared<sigc::connection>();
74- *conn = volume_->mounted.connect([this, conn, timestamp] {
75- file_manager_->OpenActiveChild(volume_->GetUri(), timestamp);
76+ *conn = volume_->mounted.connect([this, conn, callback] {
77+ callback();
78 conn->disconnect();
79 });
80 connections_.Add(*conn);
81@@ -147,8 +147,22 @@
82 }
83 else
84 {
85+ callback();
86+ }
87+ }
88+
89+ void OpenInFileManager(uint64_t timestamp)
90+ {
91+ DoActionWhenMounted([this, timestamp] {
92 file_manager_->OpenActiveChild(volume_->GetUri(), timestamp);
93- }
94+ });
95+ }
96+
97+ void CopyFilesToVolume(std::set<std::string> const& files, uint64_t timestamp)
98+ {
99+ DoActionWhenMounted([this, files, timestamp] {
100+ file_manager_->CopyFiles(files, volume_->GetUri(), timestamp);
101+ });
102 }
103
104 MenuItemsVector GetMenus()
105@@ -374,6 +388,28 @@
106 SetQuirk(Quirk::VISIBLE, true);
107 }
108
109+bool VolumeLauncherIcon::OnShouldHighlightOnDrag(DndData const& dnd_data)
110+{
111+ for (auto const& uri : dnd_data.Uris())
112+ {
113+ if (uri.find("file://") == 0)
114+ return true;
115+ }
116+
117+ return false;
118+}
119+
120+nux::DndAction VolumeLauncherIcon::OnQueryAcceptDrop(DndData const& dnd_data)
121+{
122+ return dnd_data.Uris().empty() ? nux::DNDACTION_NONE : nux::DNDACTION_COPY;
123+}
124+
125+void VolumeLauncherIcon::OnAcceptDrop(DndData const& dnd_data)
126+{
127+ auto timestamp = nux::GetGraphicsDisplay()->GetCurrentEvent().x11_timestamp;
128+ pimpl_->CopyFilesToVolume(dnd_data.Uris(), timestamp);
129+}
130+
131 //
132 // Introspection
133 //
134
135=== modified file 'launcher/VolumeLauncherIcon.h'
136--- launcher/VolumeLauncherIcon.h 2013-10-07 16:26:18 +0000
137+++ launcher/VolumeLauncherIcon.h 2013-10-07 22:37:56 +0000
138@@ -53,7 +53,10 @@
139 std::string GetRemoteUri() const;
140
141 protected:
142- virtual void ActivateLauncherIcon(ActionArg arg);
143+ void ActivateLauncherIcon(ActionArg arg);
144+ bool OnShouldHighlightOnDrag(DndData const&);
145+ void OnAcceptDrop(DndData const&);
146+ nux::DndAction OnQueryAcceptDrop(DndData const&);
147
148 // Introspection
149 virtual std::string GetName() const;
150
151=== modified file 'tests/test_mock_filemanager.h'
152--- tests/test_mock_filemanager.h 2013-10-07 16:26:18 +0000
153+++ tests/test_mock_filemanager.h 2013-10-07 22:37:56 +0000
154@@ -36,6 +36,7 @@
155 MOCK_METHOD1(OpenTrash, void(uint64_t time));
156 MOCK_METHOD1(TrashFile, bool(std::string const& uri));
157 MOCK_METHOD1(EmptyTrash, void(uint64_t time));
158+ MOCK_METHOD3(CopyFiles, void(std::set<std::string> const& files, std::string const& dest, uint64_t time));
159 MOCK_CONST_METHOD0(OpenedLocations, std::vector<std::string>());
160 MOCK_CONST_METHOD1(IsPrefixOpened, bool(std::string const& uri));
161 MOCK_CONST_METHOD0(IsTrashOpened, bool());
162
163=== modified file 'tests/test_volume_launcher_icon.cpp'
164--- tests/test_volume_launcher_icon.cpp 2013-10-07 16:49:04 +0000
165+++ tests/test_volume_launcher_icon.cpp 2013-10-07 22:37:56 +0000
166@@ -42,10 +42,6 @@
167 {
168 SetupVolumeDefaultBehavior();
169 SetupSettingsDefaultBehavior();
170- }
171-
172- void CreateIcon()
173- {
174 icon_ = new NiceMock<VolumeLauncherIcon>(volume_, settings_, notifications_, file_manager_);
175 }
176
177@@ -88,13 +84,25 @@
178 std::string old_lang_;
179 };
180
181+struct TestVolumeLauncherIconDelayedConstruction : TestVolumeLauncherIcon
182+{
183+ TestVolumeLauncherIconDelayedConstruction()
184+ {
185+ icon_ = nullptr;
186+ }
187+
188+ void CreateIcon()
189+ {
190+ icon_ = new NiceMock<VolumeLauncherIcon>(volume_, settings_, notifications_, file_manager_);
191+ }
192+};
193+
194 TEST_F(TestVolumeLauncherIcon, TestIconType)
195 {
196- CreateIcon();
197 EXPECT_EQ(icon_->GetIconType(), AbstractLauncherIcon::IconType::DEVICE);
198 }
199
200-TEST_F(TestVolumeLauncherIcon, TestRunningOnClosed)
201+TEST_F(TestVolumeLauncherIconDelayedConstruction, TestRunningOnClosed)
202 {
203 ON_CALL(*file_manager_, IsPrefixOpened(volume_->GetUri())).WillByDefault(Return(false));
204 CreateIcon();
205@@ -102,7 +110,7 @@
206 EXPECT_FALSE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::RUNNING));
207 }
208
209-TEST_F(TestVolumeLauncherIcon, TestRunningOnOpened)
210+TEST_F(TestVolumeLauncherIconDelayedConstruction, TestRunningOnOpened)
211 {
212 ON_CALL(*file_manager_, IsPrefixOpened(volume_->GetUri())).WillByDefault(Return(true));
213 CreateIcon();
214@@ -112,7 +120,6 @@
215
216 TEST_F(TestVolumeLauncherIcon, FilemanagerSignalDisconnection)
217 {
218- CreateIcon();
219 ASSERT_FALSE(file_manager_->locations_changed.empty());
220 icon_ = nullptr;
221 EXPECT_TRUE(file_manager_->locations_changed.empty());
222@@ -120,8 +127,6 @@
223
224 TEST_F(TestVolumeLauncherIcon, TestRunningStateOnLocationChangedClosed)
225 {
226- CreateIcon();
227-
228 ON_CALL(*file_manager_, IsPrefixOpened(volume_->GetUri())).WillByDefault(Return(false));
229 file_manager_->locations_changed.emit();
230 EXPECT_FALSE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::RUNNING));
231@@ -129,8 +134,6 @@
232
233 TEST_F(TestVolumeLauncherIcon, TestRunningStateOnLocationChangedOpened)
234 {
235- CreateIcon();
236-
237 ON_CALL(*file_manager_, IsPrefixOpened(volume_->GetUri())).WillByDefault(Return(true));
238 file_manager_->locations_changed.emit();
239 EXPECT_TRUE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::RUNNING));
240@@ -138,39 +141,30 @@
241
242 TEST_F(TestVolumeLauncherIcon, TestPosition)
243 {
244- CreateIcon();
245-
246 EXPECT_EQ(icon_->position(), AbstractLauncherIcon::Position::FLOATING);
247 }
248
249 TEST_F(TestVolumeLauncherIcon, TestTooltipText)
250 {
251- CreateIcon();
252-
253- ASSERT_EQ(icon_->tooltip_text, "Test Name");
254+ EXPECT_EQ(volume_->GetName(), icon_->tooltip_text());
255 }
256
257 TEST_F(TestVolumeLauncherIcon, TestIconName)
258 {
259- CreateIcon();
260-
261- ASSERT_EQ(icon_->icon_name, "Test Icon Name");
262+ EXPECT_EQ(volume_->GetIconName(), icon_->icon_name());
263 }
264
265 TEST_F(TestVolumeLauncherIcon, TestVisibility_InitiallyMountedVolume)
266 {
267- CreateIcon();
268-
269 EXPECT_TRUE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE));
270 }
271
272 TEST_F(TestVolumeLauncherIcon, RemoteUri)
273 {
274- CreateIcon();
275 EXPECT_EQ(icon_->GetRemoteUri(), FavoriteStore::URI_PREFIX_DEVICE + volume_->GetIdentifier());
276 }
277
278-TEST_F(TestVolumeLauncherIcon, TestVisibility_InitiallyMountedBlacklistedVolume)
279+TEST_F(TestVolumeLauncherIconDelayedConstruction, TestVisibility_InitiallyMountedBlacklistedVolume)
280 {
281 EXPECT_CALL(*settings_, IsABlacklistedDevice(_))
282 .WillRepeatedly(Return(true));
283@@ -181,7 +175,7 @@
284 }
285
286
287-TEST_F(TestVolumeLauncherIcon, TestVisibility_InitiallyUnmountedVolume)
288+TEST_F(TestVolumeLauncherIconDelayedConstruction, TestVisibility_InitiallyUnmountedVolume)
289 {
290 EXPECT_CALL(*volume_, IsMounted())
291 .WillRepeatedly(Return(false));
292@@ -192,7 +186,7 @@
293 }
294
295
296-TEST_F(TestVolumeLauncherIcon, TestVisibility_InitiallyUnmountedBlacklistedVolume)
297+TEST_F(TestVolumeLauncherIconDelayedConstruction, TestVisibility_InitiallyUnmountedBlacklistedVolume)
298 {
299 EXPECT_CALL(*volume_, IsMounted())
300 .WillRepeatedly(Return(false));
301@@ -207,8 +201,6 @@
302
303 TEST_F(TestVolumeLauncherIcon, TestSettingsChangedSignal)
304 {
305- CreateIcon();
306-
307 EXPECT_CALL(*settings_, IsABlacklistedDevice(_))
308 .WillRepeatedly(Return(true));
309 settings_->changed.emit();
310@@ -218,8 +210,6 @@
311
312 TEST_F(TestVolumeLauncherIcon, TestVisibilityAfterUnmount)
313 {
314- CreateIcon();
315-
316 EXPECT_CALL(*volume_, IsMounted())
317 .WillRepeatedly(Return(false));
318
319@@ -231,7 +221,7 @@
320 EXPECT_TRUE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE));
321 }
322
323-TEST_F(TestVolumeLauncherIcon, TestVisibilityAfterUnmount_BlacklistedVolume)
324+TEST_F(TestVolumeLauncherIconDelayedConstruction, TestVisibilityAfterUnmount_BlacklistedVolume)
325 {
326 EXPECT_CALL(*settings_, IsABlacklistedDevice(_))
327 .WillRepeatedly(Return(true));
328@@ -254,16 +244,12 @@
329 EXPECT_CALL(*volume_, GetIdentifier())
330 .WillRepeatedly(Return(""));
331
332- CreateIcon();
333-
334 for (auto menuitem : icon_->GetMenus())
335 ASSERT_STRNE(dbusmenu_menuitem_property_get(menuitem, DBUSMENU_MENUITEM_PROP_LABEL), "Unlock from Launcher");
336 }
337
338 TEST_F(TestVolumeLauncherIcon, TestUnlockFromLauncherMenuItem_Success)
339 {
340- CreateIcon();
341-
342 auto menuitem = GetMenuItemAtIndex(4);
343
344 ASSERT_STREQ(dbusmenu_menuitem_property_get(menuitem, DBUSMENU_MENUITEM_PROP_LABEL), "Unlock from Launcher");
345@@ -284,8 +270,6 @@
346
347 TEST_F(TestVolumeLauncherIcon, TestUnlockFromLauncherMenuItem_Failure)
348 {
349- CreateIcon();
350-
351 auto menuitem = GetMenuItemAtIndex(4);
352
353 ASSERT_STREQ(dbusmenu_menuitem_property_get(menuitem, DBUSMENU_MENUITEM_PROP_LABEL), "Unlock from Launcher");
354@@ -302,8 +286,6 @@
355
356 TEST_F(TestVolumeLauncherIcon, TestOpenMenuItem)
357 {
358- CreateIcon();
359-
360 auto menuitem = GetMenuItemAtIndex(0);
361
362 ASSERT_STREQ(dbusmenu_menuitem_property_get(menuitem, DBUSMENU_MENUITEM_PROP_LABEL), "Open");
363@@ -312,6 +294,8 @@
364
365 ON_CALL(*volume_, IsMounted()).WillByDefault(Return(false));
366 uint64_t time = g_random_int();
367+
368+ InSequence seq;
369 EXPECT_CALL(*volume_, Mount());
370 EXPECT_CALL(*file_manager_, OpenActiveChild(volume_->GetUri(), time));
371
372@@ -320,8 +304,6 @@
373
374 TEST_F(TestVolumeLauncherIcon, TestNameMenuItem)
375 {
376- CreateIcon();
377-
378 auto menuitem = GetMenuItemAtIndex(2);
379
380 EXPECT_EQ(dbusmenu_menuitem_property_get(menuitem, DBUSMENU_MENUITEM_PROP_LABEL), "<b>" + volume_->GetName() + "</b>");
381@@ -331,6 +313,8 @@
382
383 uint64_t time = g_random_int();
384 ON_CALL(*volume_, IsMounted()).WillByDefault(Return(false));
385+
386+ InSequence seq;
387 EXPECT_CALL(*volume_, Mount());
388 EXPECT_CALL(*file_manager_, OpenActiveChild(volume_->GetUri(), time));
389
390@@ -339,8 +323,6 @@
391
392 TEST_F(TestVolumeLauncherIcon, TestEjectMenuItem_NotEjectableVolume)
393 {
394- CreateIcon();
395-
396 for (auto menuitem : icon_->GetMenus())
397 ASSERT_STRNE(dbusmenu_menuitem_property_get(menuitem, DBUSMENU_MENUITEM_PROP_LABEL), "Eject");
398 }
399@@ -350,8 +332,6 @@
400 EXPECT_CALL(*volume_, CanBeEjected())
401 .WillRepeatedly(Return(true));
402
403- CreateIcon();
404-
405 auto menuitem = GetMenuItemAtIndex(5);
406
407 EXPECT_CALL(*volume_, Eject());
408@@ -366,8 +346,6 @@
409
410 TEST_F(TestVolumeLauncherIcon, TestEjectMenuItem_NotStoppableVolume)
411 {
412- CreateIcon();
413-
414 for (auto menuitem : icon_->GetMenus())
415 ASSERT_STRNE(dbusmenu_menuitem_property_get(menuitem, DBUSMENU_MENUITEM_PROP_LABEL), "Safely remove");
416 }
417@@ -377,8 +355,6 @@
418 EXPECT_CALL(*volume_, CanBeStopped())
419 .WillRepeatedly(Return(true));
420
421- CreateIcon();
422-
423 auto menuitem = GetMenuItemAtIndex(5);
424
425 EXPECT_CALL(*volume_, StopDrive())
426@@ -396,8 +372,6 @@
427 EXPECT_CALL(*volume_, IsMounted())
428 .WillRepeatedly(Return(false));
429
430- CreateIcon();
431-
432 for (auto menuitem : icon_->GetMenus())
433 ASSERT_STRNE(dbusmenu_menuitem_property_get(menuitem, DBUSMENU_MENUITEM_PROP_LABEL), "Unmount");
434 }
435@@ -408,11 +382,6 @@
436 EXPECT_CALL(*volume_, CanBeEjected())
437 .WillRepeatedly(Return(true));
438
439- EXPECT_CALL(*volume_, IsMounted())
440- .WillRepeatedly(Return(true));
441-
442- CreateIcon();
443-
444 for (auto menuitem : icon_->GetMenus())
445 ASSERT_STRNE(dbusmenu_menuitem_property_get(menuitem, DBUSMENU_MENUITEM_PROP_LABEL), "Unmount");
446 }
447@@ -422,22 +391,12 @@
448 EXPECT_CALL(*volume_, CanBeStopped())
449 .WillRepeatedly(Return(true));
450
451- EXPECT_CALL(*volume_, IsMounted())
452- .WillRepeatedly(Return(true));
453-
454- CreateIcon();
455-
456 for (auto menuitem : icon_->GetMenus())
457 ASSERT_STRNE(dbusmenu_menuitem_property_get(menuitem, DBUSMENU_MENUITEM_PROP_LABEL), "Unmount");
458 }
459
460 TEST_F(TestVolumeLauncherIcon, TestUnmountMenuItem)
461 {
462- EXPECT_CALL(*volume_, IsMounted())
463- .WillRepeatedly(Return(true));
464-
465- CreateIcon();
466-
467 auto menuitem = GetMenuItemAtIndex(5);
468
469 EXPECT_CALL(*volume_, Unmount())
470@@ -452,8 +411,6 @@
471
472 TEST_F(TestVolumeLauncherIcon, TestCanBeEject)
473 {
474- CreateIcon();
475-
476 EXPECT_CALL(*volume_, CanBeEjected())
477 .WillRepeatedly(Return(true));
478 ASSERT_TRUE(icon_->CanEject());
479@@ -469,8 +426,6 @@
480 EXPECT_CALL(*volume_, CanBeEjected())
481 .WillRepeatedly(Return(true));
482
483- CreateIcon();
484-
485 EXPECT_CALL(*volume_, Eject());
486 EXPECT_CALL(*notifications_, Display(volume_->GetIconName(), volume_->GetName()));
487 icon_->EjectAndShowNotification();
488@@ -478,8 +433,6 @@
489
490 TEST_F(TestVolumeLauncherIcon, OnRemoved)
491 {
492- CreateIcon();
493-
494 EXPECT_CALL(*settings_, TryToBlacklist(_))
495 .Times(0);
496 EXPECT_CALL(*settings_, TryToUnblacklist(_))
497@@ -492,7 +445,6 @@
498 {
499 EXPECT_CALL(*volume_, CanBeRemoved())
500 .WillRepeatedly(Return(true));
501- CreateIcon();
502
503 EXPECT_CALL(*settings_, TryToBlacklist(_))
504 .Times(0);
505@@ -508,7 +460,6 @@
506 .WillRepeatedly(Return(true));
507 EXPECT_CALL(*settings_, IsABlacklistedDevice(_))
508 .WillRepeatedly(Return(true));
509- CreateIcon();
510
511 EXPECT_CALL(*settings_, TryToBlacklist(_))
512 .Times(0);
513@@ -520,8 +471,6 @@
514
515 TEST_F(TestVolumeLauncherIcon, Stick)
516 {
517- CreateIcon();
518-
519 bool saved = false;
520 icon_->position_saved.connect([&saved] {saved = true;});
521
522@@ -534,8 +483,6 @@
523
524 TEST_F(TestVolumeLauncherIcon, StickAndSave)
525 {
526- CreateIcon();
527-
528 bool saved = false;
529 icon_->position_saved.connect([&saved] {saved = true;});
530
531@@ -548,8 +495,6 @@
532
533 TEST_F(TestVolumeLauncherIcon, Unstick)
534 {
535- CreateIcon();
536-
537 bool forgot = false;
538 icon_->position_forgot.connect([&forgot] {forgot = true;});
539
540@@ -566,9 +511,8 @@
541
542 TEST_F(TestVolumeLauncherIcon, ActivateMounted)
543 {
544- CreateIcon();
545-
546 uint64_t time = g_random_int();
547+ InSequence seq;
548 EXPECT_CALL(*volume_, Mount()).Times(0);
549 EXPECT_CALL(*file_manager_, OpenActiveChild(volume_->GetUri(), time));
550 icon_->Activate(ActionArg(ActionArg::Source::LAUNCHER, 0, time));
551@@ -576,13 +520,67 @@
552
553 TEST_F(TestVolumeLauncherIcon, ActivateUnmounted)
554 {
555- CreateIcon();
556-
557 uint64_t time = g_random_int();
558 ON_CALL(*volume_, IsMounted()).WillByDefault(Return(false));
559+ InSequence seq;
560 EXPECT_CALL(*volume_, Mount());
561 EXPECT_CALL(*file_manager_, OpenActiveChild(volume_->GetUri(), time));
562 icon_->Activate(ActionArg(ActionArg::Source::LAUNCHER, 0, time));
563 }
564
565+TEST_F(TestVolumeLauncherIcon, ShouldHighlightOnDragFilesValid)
566+{
567+ DndData data;
568+ std::string data_string = "file://file1\ndir2/file2\nfile://file3\nfile://dirN/fileN";
569+ data.Fill(data_string.c_str());
570+ EXPECT_TRUE(icon_->ShouldHighlightOnDrag(data));
571+}
572+
573+TEST_F(TestVolumeLauncherIcon, ShouldHighlightOnDragFilesInvalid)
574+{
575+ DndData data;
576+ std::string data_string = "file1\ndir2/file2\napplication://file3\nunity://lens";
577+ data.Fill(data_string.c_str());
578+ EXPECT_FALSE(icon_->ShouldHighlightOnDrag(data));
579+}
580+
581+TEST_F(TestVolumeLauncherIcon, QueryAcceptDrop)
582+{
583+ DndData data;
584+ EXPECT_EQ(nux::DNDACTION_NONE, icon_->QueryAcceptDrop(data));
585+
586+ std::string data_string = "file://foo/file";
587+ data.Fill(data_string.c_str());
588+ EXPECT_EQ(nux::DNDACTION_COPY, icon_->QueryAcceptDrop(data));
589+}
590+
591+TEST_F(TestVolumeLauncherIcon, AcceptDropUnmounted)
592+{
593+ DndData data;
594+ std::string data_string = "file://file1\ndir2/file2\nfile://file3\nfile://dirN/fileN";
595+ data.Fill(data_string.c_str());
596+ auto time = g_random_int();
597+ nux::GetGraphicsDisplay()->GetCurrentEvent().x11_timestamp = time;
598+
599+ InSequence seq;
600+ ON_CALL(*volume_, IsMounted()).WillByDefault(Return(false));
601+ EXPECT_CALL(*volume_, Mount());
602+ EXPECT_CALL(*file_manager_, CopyFiles(data.Uris(), volume_->GetUri(), time));
603+ icon_->AcceptDrop(data);
604+}
605+
606+TEST_F(TestVolumeLauncherIcon, AcceptDropMounted)
607+{
608+ DndData data;
609+ std::string data_string = "file://file1\ndir2/file2\nfile://file3\nfile://dirN/fileN";
610+ data.Fill(data_string.c_str());
611+ auto time = g_random_int();
612+ nux::GetGraphicsDisplay()->GetCurrentEvent().x11_timestamp = time;
613+
614+ InSequence seq;
615+ EXPECT_CALL(*volume_, Mount()).Times(0);
616+ EXPECT_CALL(*file_manager_, CopyFiles(data.Uris(), volume_->GetUri(), time));
617+ icon_->AcceptDrop(data);
618+}
619+
620 }
621
622=== modified file 'unity-shared/FileManager.h'
623--- unity-shared/FileManager.h 2013-09-05 16:22:56 +0000
624+++ unity-shared/FileManager.h 2013-10-07 22:37:56 +0000
625@@ -25,6 +25,7 @@
626 #include <vector>
627 #include <string>
628 #include <vector>
629+#include <set>
630 #include <sigc++/sigc++.h>
631
632 namespace unity
633@@ -45,6 +46,7 @@
634 virtual bool IsPrefixOpened(std::string const& uri) const = 0;
635 virtual bool IsTrashOpened() const = 0;
636 virtual bool IsDeviceOpened() const = 0;
637+ virtual void CopyFiles(std::set<std::string> const& uris, std::string const& dest, uint64_t timestamp = 0) = 0;
638 virtual bool TrashFile(std::string const& uri) = 0;
639 virtual void EmptyTrash(uint64_t timestamp = 0) = 0;
640
641
642=== modified file 'unity-shared/GnomeFileManager.cpp'
643--- unity-shared/GnomeFileManager.cpp 2013-10-07 13:29:56 +0000
644+++ unity-shared/GnomeFileManager.cpp 2013-10-07 22:37:56 +0000
645@@ -220,6 +220,38 @@
646 proxy->CallBegin("EmptyTrash", nullptr, [proxy] (GVariant*, glib::Error const&) {});
647 }
648
649+void GnomeFileManager::CopyFiles(std::set<std::string> const& uris, std::string const& dest, uint64_t timestamp)
650+{
651+ if (uris.empty() || dest.empty())
652+ return;
653+
654+ bool found_valid = false;
655+ GVariantBuilder b;
656+ g_variant_builder_init(&b, G_VARIANT_TYPE("(ass)"));
657+ g_variant_builder_open(&b, G_VARIANT_TYPE("as"));
658+
659+ for (auto const& uri : uris)
660+ {
661+ if (uri.find(FILE_SCHEMA) == 0)
662+ {
663+ found_valid = true;
664+ g_variant_builder_add(&b, "s", uri.c_str());
665+ }
666+ }
667+
668+ g_variant_builder_close(&b);
669+ g_variant_builder_add(&b, "s", dest.c_str());
670+ glib::Variant parameters(g_variant_builder_end(&b));
671+
672+ if (found_valid)
673+ {
674+ // Passing the proxy to the lambda we ensure that it will be destroyed when needed
675+ auto const& proxy = impl_->NautilusOperationsProxy();
676+ proxy->CallBegin("CopyURIs", parameters, [proxy] (GVariant*, glib::Error const&) {});
677+ Activate(timestamp);
678+ }
679+}
680+
681 std::vector<std::string> GnomeFileManager::OpenedLocations() const
682 {
683 return impl_->opened_locations_;
684
685=== modified file 'unity-shared/GnomeFileManager.h'
686--- unity-shared/GnomeFileManager.h 2013-09-05 16:22:56 +0000
687+++ unity-shared/GnomeFileManager.h 2013-10-07 22:37:56 +0000
688@@ -35,6 +35,8 @@
689 void Open(std::string const& uri, uint64_t timestamp);
690 void OpenActiveChild(std::string const& uri, uint64_t timestamp);
691 void OpenTrash(uint64_t timestamp);
692+
693+ void CopyFiles(std::set<std::string> const& uris, std::string const& dest, uint64_t timestamp);
694 bool TrashFile(std::string const& uri);
695 void EmptyTrash(uint64_t timestamp);
696