Merge lp:~jeremywootten/pantheon-files/transfer-popover-with-pie-progress into lp:~elementary-apps/pantheon-files/trunk

Proposed by Jeremy Wootten
Status: Superseded
Proposed branch: lp:~jeremywootten/pantheon-files/transfer-popover-with-pie-progress
Merge into: lp:~elementary-apps/pantheon-files/trunk
Diff against target: 2642 lines (+1397/-452)
28 files modified
CMakeLists.txt (+3/-2)
data/CMakeLists.txt (+5/-2)
data/pantheon-files.db.service.cmake (+4/-0)
data/pantheon-files.ql.service.cmake (+4/-0)
data/pantheon-files.service.cmake (+1/-0)
libcore/Enums.vala (+9/-0)
libcore/gof-directory-async.vala (+1/-0)
libcore/marlin-file-operations.c (+89/-34)
libcore/marlin-progress-info.c (+190/-2)
libcore/marlin-progress-info.h (+18/-0)
libcore/pantheon-files-core-C.vapi (+7/-0)
libwidgets/CMakeLists.txt (+3/-0)
pantheon-files-daemon/CMakeLists.txt (+48/-17)
pantheon-files-daemon/ProgressHandlerQuicklistInterface.vala (+31/-0)
pantheon-files-daemon/QuicklistHandler.vala (+217/-43)
pantheon-files-daemon/main.vala (+68/-0)
pantheon-files-daemon/marlind-tagging.vala (+0/-58)
pantheon-files-daemon/pantheon-files-daemon.vapi (+28/-0)
src/Application.vala (+57/-9)
src/CMakeLists.txt (+10/-13)
src/ProgressUIHandler.vala (+107/-169)
src/View/Slot.vala (+1/-1)
src/View/Widgets/ProgressIndicator.vala (+105/-0)
src/View/Widgets/ProgressIndicatorWithPopover.vala (+121/-0)
src/View/Widgets/ProgressInfoListRow.vala (+238/-0)
src/View/Widgets/ProgressInfoWidget.vala (+0/-102)
src/View/Widgets/TopMenu.vala (+20/-0)
src/View/Window.vala (+12/-0)
To merge this branch: bzr merge lp:~jeremywootten/pantheon-files/transfer-popover-with-pie-progress
Reviewer Review Type Date Requested Status
Zisu Andrei Pending
Danielle Foré Pending
Review via email: mp+311349@code.launchpad.net

This proposal supersedes a proposal from 2016-03-10.

Description of the change

This branch transfers handling of unity launcher quicklist and progress indicator to pantheon-files daemon.
It also refactors the progress UI handling, replacing the dialog window with a pie (doughnut) shaped progress indicator in the headerbar associated with a popover to show details.

To post a comment you must log in.
Revision history for this message
Danielle Foré (danrabbit) wrote : Posted in a previous version of this proposal

I don't think we need to send a notification when cancelling a transfer. That's something that I just did. There's no delay between me doing it and it happening and it's obvious that it happened because the file is removed from the popover. So it seems unnecessary to send a notification here.

Revision history for this message
Danielle Foré (danrabbit) wrote : Posted in a previous version of this proposal

I don't really like that it warns you about closing the window now. That seems like a regression. Users should be able to close a window without worry and the process just continue to run in the background.

I also get notifications when moving things to the trash from an external disk. That seems unnecessary.

I get a notification on starting a transfer. Again, it seems unnecessary to notify me of things that I'm doing.

Revision history for this message
Jeremy Wootten (jeremywootten) wrote : Posted in a previous version of this proposal

OK, thanks for your comments. I'll work on that.

Revision history for this message
Jeremy Wootten (jeremywootten) wrote : Posted in a previous version of this proposal

The stop process button is now more clearly associated with the progress bar (not a close popover button) and is not symbolic - the red color warning that it has an irreversible effect (is confirmation required?). This follows comments on Google+.

Revision history for this message
Danielle Foré (danrabbit) wrote : Posted in a previous version of this proposal

No, I don't think confirmation is required. It is quite reversible. Just start another transfer :p

Notifications don't seen to have an icon. Probably should use the Files icon so it's clear which app this is coming from.

"File operation completed" is pretty vague. Can we get a little more specific? "5 files copied to Foo" for example?

I seem to always get the HTML mime. I'm guessing this is hardcoded?

Can the gradient on the progressbar go from top to bottom instead of around the doughnut? Looks kinda weird and doesn't really make sense. I'm not sure how the light would have to be to produce that effect :p

I'm thinking maybe we shouldn't group files into a single row. I have two rows that say "Copying foo files to bar". It's hard to remember what exactly each one is.

I'm not sure why you're creating constants for icon names, especially if you're only using it once. Seems like it makes the code less legible.

Any reason you're subclassing gtk.box here instead of using ListBox and ListBoxRow?

Margins on the popover seem oddly huge. Margins on dialogs and popovers should be 12px.

I think it would be helpful to name "ProgressInfoWidget.vala" something more indicative of what it actually is. It seems like that could mean just about anything. Something like "TransferRow" might be more clear about what it is used for and what shape it takes. "Widget" could be anything.

You should use gtk.grid instead of nesting boxes

review: Needs Fixing
Revision history for this message
Jeremy Wootten (jeremywootten) wrote : Posted in a previous version of this proposal

The completion notification is "All file operations have been completed" and only appears when the last of the file operations finishes - there are no notifications for individual notifications for file operations. So your suggestion would only work if there was only one file operation (which, admittedly there usually is). Otherwise you would have to list each operation? And whether or not they were successful or failed? Or notify the ending of each operation individually?

Revision history for this message
Zisu Andrei (matzipan) wrote : Posted in a previous version of this proposal

Tested it. Works nicely, although the pie needs a bit of visual improvements to better match the mockup (https://github.com/elementary/mockups/blob/master/files/transfer.svg). I'll try and do those improvements myself if I have some time.

Diff comments attached to this message.

review: Needs Fixing
2044. By Jeremy Wootten

Merge trunk to r2444 and resolve conflicts

2045. By Jeremy Wootten

Move Progress widgets to src/View/Widgets

2046. By Jeremy Wootten

Define MarlinFileOperationType in marlin-progress-info.h not Enums.vala to avoid conflicts with testing branches

2047. By Jeremy Wootten

Merge trunk to r2470

Unmerged revisions

2047. By Jeremy Wootten

Merge trunk to r2470

2046. By Jeremy Wootten

Define MarlinFileOperationType in marlin-progress-info.h not Enums.vala to avoid conflicts with testing branches

2045. By Jeremy Wootten

Move Progress widgets to src/View/Widgets

2044. By Jeremy Wootten

Merge trunk to r2444 and resolve conflicts

2043. By Jeremy Wootten

Merge trunk to r2382 and resolve conflicts

2042. By Jeremy Wootten

Merge trunk to r2229 and resolve conflicts. Make prefix for APP_ID, .desktop files, dbus service files, dbus service addresses, settings schemas consistently org.pantheon.files

2041. By Jeremy Wootten

Change total_files to total_file_count

2040. By Jeremy Wootten

Change remaining_files to remaining_file_count

2039. By Jeremy Wootten

Rename info->no_files

2038. By Jeremy Wootten

Initialise info->current_filename; return empty string if null

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2016-12-16 10:09:11 +0000
3+++ CMakeLists.txt 2017-01-14 12:39:17 +0000
4@@ -2,6 +2,7 @@
5
6 cmake_minimum_required (VERSION 2.8)
7 cmake_policy (VERSION 2.8)
8+cmake_policy (SET CMP0015 OLD)
9 project (pantheon-files C)
10 enable_testing ()
11
12@@ -35,9 +36,9 @@
13 ELSE ()
14 add_subdirectory (src)
15 add_subdirectory (data)
16+ add_subdirectory (libcore)
17+ add_subdirectory (libwidgets)
18 add_subdirectory (pantheon-files-daemon)
19- add_subdirectory (libcore)
20- add_subdirectory (libwidgets)
21 add_subdirectory (plugins)
22 add_subdirectory (filechooser-module)
23 add_subdirectory (po)
24
25=== modified file 'data/CMakeLists.txt'
26--- data/CMakeLists.txt 2016-08-01 23:47:17 +0000
27+++ data/CMakeLists.txt 2017-01-14 12:39:17 +0000
28@@ -1,6 +1,8 @@
29 include (Translations)
30
31-configure_file(pantheon-files.service.cmake ${CMAKE_CURRENT_BINARY_DIR}/pantheon-files.service)
32+configure_file(pantheon-files.desktop.in ${CMAKE_CURRENT_BINARY_DIR}/org.pantheon.files.desktop)
33+configure_file(pantheon-files.db.service.cmake ${CMAKE_CURRENT_BINARY_DIR}/org.pantheon.files.db.service)
34+configure_file(pantheon-files.ql.service.cmake ${CMAKE_CURRENT_BINARY_DIR}/org.pantheon.files.ql.service)
35 configure_file(pantheon-files-pkexec.cmake ${CMAKE_CURRENT_BINARY_DIR}/pantheon-files-pkexec)
36 configure_file(net.launchpad.pantheon-files.policy.cmake ${CMAKE_CURRENT_BINARY_DIR}/net.launchpad.pantheon-files.policy)
37
38@@ -9,7 +11,8 @@
39
40 install (FILES ${CMAKE_CURRENT_BINARY_DIR}/org.pantheon.files.appdata.xml DESTINATION share/appdata)
41 install (FILES ${CMAKE_CURRENT_BINARY_DIR}/org.pantheon.files.desktop DESTINATION share/applications)
42-install (FILES ${CMAKE_CURRENT_BINARY_DIR}/pantheon-files.service DESTINATION share/dbus-1/services/)
43+install (FILES ${CMAKE_CURRENT_BINARY_DIR}/org.pantheon.files.db.service DESTINATION share/dbus-1/services/)
44+install (FILES ${CMAKE_CURRENT_BINARY_DIR}/org.pantheon.files.ql.service DESTINATION share/dbus-1/services/)
45 install (PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/pantheon-files-pkexec DESTINATION bin)
46 install (FILES ${CMAKE_CURRENT_BINARY_DIR}/net.launchpad.pantheon-files.policy DESTINATION share/polkit-1/actions/)
47
48
49=== added file 'data/pantheon-files.db.service.cmake'
50--- data/pantheon-files.db.service.cmake 1970-01-01 00:00:00 +0000
51+++ data/pantheon-files.db.service.cmake 2017-01-14 12:39:17 +0000
52@@ -0,0 +1,4 @@
53+[D-BUS Service]
54+Name=org.pantheon.files.db
55+Exec=@CMAKE_INSTALL_PREFIX@/bin/pantheon-files-daemon
56+
57
58=== added file 'data/pantheon-files.ql.service.cmake'
59--- data/pantheon-files.ql.service.cmake 1970-01-01 00:00:00 +0000
60+++ data/pantheon-files.ql.service.cmake 2017-01-14 12:39:17 +0000
61@@ -0,0 +1,4 @@
62+[D-BUS Service]
63+Name=org.pantheon.files.ql
64+Exec=@CMAKE_INSTALL_PREFIX@/bin/pantheon-files-daemon
65+
66
67=== modified file 'data/pantheon-files.service.cmake'
68--- data/pantheon-files.service.cmake 2016-06-18 11:25:11 +0000
69+++ data/pantheon-files.service.cmake 2017-01-14 12:39:17 +0000
70@@ -1,3 +1,4 @@
71 [D-BUS Service]
72 Name=org.pantheon.files.db
73 Exec=@CMAKE_INSTALL_PREFIX@/bin/pantheon-files-daemon
74+
75
76=== modified file 'libcore/Enums.vala'
77--- libcore/Enums.vala 2016-12-31 19:42:09 +0000
78+++ libcore/Enums.vala 2017-01-14 12:39:17 +0000
79@@ -149,4 +149,13 @@
80 XDND_DIRECT_SAVE0,
81 NETSCAPE_URL
82 }
83+
84+ public enum FileOperationType {
85+ COPY,
86+ MOVE,
87+ TRASH,
88+ DELETE,
89+ LINK,
90+ INVALID
91+ }
92 }
93
94=== modified file 'libcore/gof-directory-async.vala'
95--- libcore/gof-directory-async.vala 2017-01-04 13:40:56 +0000
96+++ libcore/gof-directory-async.vala 2017-01-14 12:39:17 +0000
97@@ -555,6 +555,7 @@
98 }
99 state = State.LOADED;
100 } catch (Error err) {
101+ cancel_timeout (ref load_timeout_id);
102 warning ("Listing directory error: %s %s", err.message, file.uri);
103 can_load = false;
104 if (err is IOError.NOT_FOUND || err is IOError.NOT_DIRECTORY) {
105
106=== modified file 'libcore/marlin-file-operations.c'
107--- libcore/marlin-file-operations.c 2016-10-16 18:35:01 +0000
108+++ libcore/marlin-file-operations.c 2017-01-14 12:39:17 +0000
109@@ -2066,6 +2066,11 @@
110 #ifdef ENABLE_TASKVIEW
111 taskview_generic_set_state (TASKVIEW_GENERIC (job->common.tv_io), TASKVIEW_RUNNING);
112 #else
113+ if (job->try_trash) {
114+ marlin_progress_info_set_optype (common->progress, MARLIN_FILE_OPERATION_TYPE_TRASH);
115+ } else {
116+ marlin_progress_info_set_optype (common->progress, MARLIN_FILE_OPERATION_TYPE_DELETE);
117+ }
118 marlin_progress_info_start (job->common.progress);
119 #endif
120
121@@ -2649,8 +2654,10 @@
122 static void
123 count_file (GFileInfo *info,
124 CommonJob *job,
125- SourceInfo *source_info)
126+ SourceInfo *source_info,
127+ const char *lastctype)
128 {
129+ const char *ctype;
130 source_info->num_files += 1;
131 source_info->num_bytes += g_file_info_get_size (info);
132
133@@ -2658,6 +2665,16 @@
134 report_count_progress (job, source_info);
135 source_info->num_files_since_progress = 0;
136 }
137+
138+ if (info != NULL) {
139+ ctype = g_file_info_get_content_type (info);
140+
141+ if (source_info->num_files == 1) {
142+ marlin_progress_info_set_ctype (job->progress, ctype);
143+ } else if (strcmp (ctype, lastctype) != 0) {
144+ marlin_progress_info_set_ctype (job->progress, "");
145+ }
146+ }
147 }
148
149 static char *
150@@ -2697,6 +2714,7 @@
151 enumerator = g_file_enumerate_children (dir,
152 G_FILE_ATTRIBUTE_STANDARD_NAME","
153 G_FILE_ATTRIBUTE_STANDARD_TYPE","
154+ G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE","
155 G_FILE_ATTRIBUTE_STANDARD_SIZE,
156 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
157 job->cancellable,
158@@ -2704,7 +2722,7 @@
159 if (enumerator) {
160 error = NULL;
161 while ((info = g_file_enumerator_next_file (enumerator, job->cancellable, &error)) != NULL) {
162- count_file (info, job, source_info);
163+ count_file (info, job, source_info, "");
164
165 if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) {
166 subdir = g_file_get_child (dir,
167@@ -2713,8 +2731,6 @@
168 /* Push to head, since we want depth-first */
169 g_queue_push_head (dirs, subdir);
170 }
171-
172- g_object_unref (info);
173 }
174 g_file_enumerator_close (enumerator, job->cancellable, NULL);
175 g_object_unref (enumerator);
176@@ -2799,10 +2815,11 @@
177 }
178 }
179
180-static void
181+static const char*
182 scan_file (GFile *file,
183 SourceInfo *source_info,
184- CommonJob *job)
185+ CommonJob *job,
186+ const char* lastctype)
187 {
188 GFileInfo *info;
189 GError *error;
190@@ -2817,20 +2834,20 @@
191 retry:
192 error = NULL;
193 info = g_file_query_info (file,
194+ G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME","
195 G_FILE_ATTRIBUTE_STANDARD_TYPE","
196+ G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE","
197 G_FILE_ATTRIBUTE_STANDARD_SIZE,
198 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
199 job->cancellable,
200 &error);
201
202 if (info) {
203- count_file (info, job, source_info);
204+ count_file (info, job, source_info,lastctype);
205
206 if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) {
207 g_queue_push_head (dirs, g_object_ref (file));
208 }
209-
210- g_object_unref (info);
211 } else if (job->skip_all_error) {
212 g_error_free (error);
213 skip_file (job, file);
214@@ -2883,6 +2900,17 @@
215 /* Free all from queue if we exited early */
216 g_queue_foreach (dirs, (GFunc)g_object_unref, NULL);
217 g_queue_free (dirs);
218+
219+
220+ const char* res;
221+ if (info) {
222+ res = g_strdup (g_file_info_get_content_type (info));
223+ g_object_unref (info);
224+ } else {
225+ res = "";
226+ }
227+
228+ return res;
229 }
230
231 static void
232@@ -2893,6 +2921,8 @@
233 {
234 GList *l;
235 GFile *file;
236+ const char *lastctype;
237+
238
239 memset (source_info, 0, sizeof (SourceInfo));
240 source_info->op = kind;
241@@ -2902,11 +2932,12 @@
242 for (l = files; l != NULL && !job_aborted (job); l = l->next) {
243 file = l->data;
244
245- scan_file (file,
246- source_info,
247- job);
248+ lastctype = scan_file (file,
249+ source_info,
250+ job,
251+ lastctype);
252 }
253-
254+ g_free (lastctype);
255 /* Make sure we report the final count */
256 report_count_progress (job, source_info);
257 }
258@@ -3076,6 +3107,7 @@
259 CommonJob *job;
260 gboolean is_move;
261 int files_left;
262+ int nth_file;
263 #ifndef ENABLE_TASKVIEW
264 goffset total_size;
265 double elapsed, transfer_rate;
266@@ -3104,15 +3136,15 @@
267 files_left = 1;
268 }
269
270+ nth_file = source_info->num_files - files_left + 1; /*nominal number of file currently being transferred */
271+
272 if (files_left != transfer_info->last_reported_files_left ||
273 transfer_info->last_reported_files_left == 0) {
274 /* Avoid changing this unless files_left changed since last time */
275 transfer_info->last_reported_files_left = files_left;
276-
277 if (source_info->num_files == 1) {
278 if (copy_job->destination != NULL) {
279- s = f (is_move ? _("Moving \"%B\" to \"%B\"") :
280- _("Copying \"%B\" to \"%B\""),
281+ s = f (is_move ? _("Moving \"%B\" to \"%B\"") : _("Copying \"%B\" to \"%B\""),
282 (GFile *)copy_job->files->data,
283 copy_job->destination);
284 } else {
285@@ -3120,20 +3152,22 @@
286 }
287 } else if (copy_job->files != NULL && copy_job->files->next == NULL) {
288 if (copy_job->destination != NULL) {
289- s = f (is_move ? ngettext ("Moving %'d file (in \"%B\") to \"%B\"",
290- "Moving %'d files (in \"%B\") to \"%B\"",
291- files_left) :
292- ngettext ("Copying %'d file (in \"%B\") to \"%B\"",
293- "Copying %'d files (in \"%B\") to \"%B\"",
294- files_left),
295- files_left,
296+ /* TRANSLATORS: The first %'d represents the (nominal) number of the file currently being transferred and should not be translated.
297+ * The second %'d represents the (nominal) total number of files in the transfer operation and should not be translated.
298+ * The \"%B\" 's represent the paths of the source and destination folders respectively and should not be translated. */
299+ s = f (is_move ? _("Moving file %'d of %'d (in \"%B\") to \"%B\". ")
300+ : _("Copying file %'d of %'d (in \"%B\") to \"%B\". "),
301+ nth_file,
302+ source_info->num_files,
303 (GFile *)copy_job->files->data,
304 copy_job->destination);
305 } else {
306- s = f (ngettext ("Duplicating %'d file (in \"%B\")",
307- "Duplicating %'d files (in \"%B\")",
308- files_left),
309- files_left,
310+ /* TRANSLATORS: The first %'d represents the (nominal) number of the file currently being transferred and should not be translated.
311+ * The second %'d represents the (nominal) total number of files in the transfer operation and should not be translated.
312+ * The \"%B\" represents the path of the source and should not be translated. */
313+ s = f ("Duplicating file %'d of %'d (in \"%B\")",
314+ nth_file,
315+ source_info->num_files,
316 (GFile *)copy_job->files->data);
317 }
318 } else {
319@@ -4344,6 +4378,17 @@
320 pdata.source_info = source_info;
321 pdata.transfer_info = transfer_info;
322
323+ GFileInfo *info;
324+ info = g_file_query_info (src, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
325+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
326+ job->cancellable,
327+ NULL);
328+
329+ marlin_progress_info_set_current_filename (job->progress, g_file_info_get_display_name (info));
330+ marlin_progress_info_decrement_remaining_file_count (job->progress);
331+
332+ g_object_unref (info);
333+
334 if (copy_job->is_move) {
335 res = g_file_move (src, dest,
336 flags,
337@@ -4352,6 +4397,7 @@
338 &pdata,
339 &error);
340 } else {
341+
342 res = g_file_copy (src, dest,
343 flags,
344 job->cancellable,
345@@ -4747,6 +4793,8 @@
346 #ifdef ENABLE_TASKVIEW
347 g_object_set (job->common.tv_io, "state", TASKVIEW_RUNNING, NULL);
348 #else
349+ marlin_progress_info_set_optype (common->progress, MARLIN_FILE_OPERATION_TYPE_COPY);
350+ marlin_progress_info_set_destination_uri (common->progress, g_file_get_uri (job->destination));
351 marlin_progress_info_start (job->common.progress);
352 #endif
353
354@@ -4754,10 +4802,13 @@
355 &source_info,
356 common,
357 OP_KIND_COPY);
358+
359 if (job_aborted (common)) {
360 goto aborted;
361 }
362
363+ marlin_progress_info_set_remaining_file_count (common->progress, source_info.num_files);
364+
365 if (job->destination) {
366 dest = g_object_ref (job->destination);
367 } else {
368@@ -4855,7 +4906,6 @@
369 NULL);
370 g_free (s);
371 #else
372- marlin_progress_info_take_status (job->progress, s);
373 marlin_progress_info_take_details (job->progress,
374 f (ngettext ("Preparing to move %'d file",
375 "Preparing to move %'d files",
376@@ -5284,6 +5334,8 @@
377 #ifdef ENABLE_TASKVIEW
378 g_object_set (job->common.tv_io, "state", TASKVIEW_RUNNING, NULL);
379 #else
380+ marlin_progress_info_set_optype (common->progress, MARLIN_FILE_OPERATION_TYPE_MOVE);
381+ marlin_progress_info_set_destination_uri (common->progress, g_file_get_uri (job->destination));
382 marlin_progress_info_start (job->common.progress);
383 #endif
384
385@@ -5297,6 +5349,7 @@
386
387 /* This moves all files that we can do without copy + delete */
388 move_files_prepare (job, dest_fs_id, &dest_fs_type, &fallbacks);
389+
390 if (job_aborted (common)) {
391 goto aborted;
392 }
393@@ -5310,6 +5363,8 @@
394 common,
395 OP_KIND_MOVE);
396
397+ marlin_progress_info_set_remaining_file_count (common->progress, source_info.num_files);
398+
399 g_list_free (fallback_files);
400
401 if (job_aborted (common)) {
402@@ -5320,6 +5375,7 @@
403 job->destination,
404 NULL,
405 source_info.num_bytes);
406+
407 if (job_aborted (common)) {
408 goto aborted;
409 }
410@@ -5616,8 +5672,6 @@
411 {
412 CopyMoveJob *job;
413 CommonJob *common;
414- GList *copy_files;
415- GArray *copy_positions;
416 GFile *src;
417 GdkPoint *point;
418 char *dest_fs_type;
419@@ -5629,14 +5683,15 @@
420 common = &job->common;
421 common->io_job = io_job;
422
423- copy_files = NULL;
424- copy_positions = NULL;
425-
426 dest_fs_type = NULL;
427+ total = left = g_list_length (job->files);
428
429 #ifdef ENABLE_TASKVIEW
430 g_object_set (job->common.tv_io, "state", TASKVIEW_RUNNING, NULL);
431 #else
432+ marlin_progress_info_set_optype (common->progress, MARLIN_FILE_OPERATION_TYPE_LINK);
433+ marlin_progress_info_set_destination_uri (common->progress, g_file_get_uri (job->destination));
434+ marlin_progress_info_set_remaining_file_count (common->progress, total);
435 marlin_progress_info_start (job->common.progress);
436 #endif
437
438@@ -5648,7 +5703,7 @@
439 goto aborted;
440 }
441
442- total = left = g_list_length (job->files);
443+
444
445 report_link_progress (job, total, left);
446
447
448=== modified file 'libcore/marlin-progress-info.c'
449--- libcore/marlin-progress-info.c 2016-06-24 18:59:29 +0000
450+++ libcore/marlin-progress-info.c 2017-01-14 12:39:17 +0000
451@@ -49,9 +49,15 @@
452 char *title;
453 char *status;
454 char *details;
455+ char *ctype;
456+ char *destination;
457+ MarlinFileOperationType optype;
458+ char *current_filename;
459 double progress;
460- double current;
461- double total;
462+ double current; /* Current number of bytes that have been copied/moved/deleted */
463+ double total; /* Total number of bytes to be copied/moved/deleted */
464+ int file_count; /* Current number of files that have been copied/moved/deleted */
465+ int total_file_count; /* Total number of files to be copied/moved/deleted */
466 gboolean activity_mode;
467 gboolean started;
468 gboolean finished;
469@@ -85,6 +91,9 @@
470 g_free (info->title);
471 g_free (info->status);
472 g_free (info->details);
473+ g_free (info->ctype);
474+ g_free (info->destination);
475+ g_free (info->current_filename);
476 g_object_unref (info->cancellable);
477
478 if (G_OBJECT_CLASS (marlin_progress_info_parent_class)->finalize) {
479@@ -160,6 +169,13 @@
480 MarlinProgressInfoManager *manager;
481
482 info->cancellable = g_cancellable_new ();
483+ info->title = NULL;
484+ info->status = NULL;
485+ info->ctype = NULL;
486+ info->destination = NULL;
487+ info->current_filename = NULL;
488+ info->total_file_count = -1;
489+ info->file_count = -1;
490
491 info->title = NULL;
492
493@@ -296,6 +312,10 @@
494 g_cancellable_cancel (info->cancellable);
495
496 G_UNLOCK (progress_info);
497+ /* Call finish now for responsiveness of interface, as it may take some tiime for the
498+ * marlin file operation to actually finish.
499+ */
500+ marlin_progress_info_finish (info);
501 }
502
503 GCancellable *
504@@ -509,12 +529,177 @@
505 G_UNLOCK (progress_info);
506 }
507
508+char *
509+marlin_progress_info_get_destination_uri (MarlinProgressInfo *info)
510+{
511+ char *uri;
512+ G_LOCK (progress_info);
513+
514+ uri = g_strdup (info->destination);
515+
516+ G_UNLOCK (progress_info);
517+
518+ return uri;
519+}
520+
521+char *
522+marlin_progress_info_get_ctype (MarlinProgressInfo *info)
523+{
524+ char *ct;
525+ G_LOCK (progress_info);
526+
527+ ct = g_strdup (info->ctype);
528+
529+ G_UNLOCK (progress_info);
530+
531+ return ct;
532+}
533+
534+MarlinFileOperationType
535+marlin_progress_info_get_optype (MarlinProgressInfo *info)
536+{
537+ MarlinFileOperationType ot;
538+ G_LOCK (progress_info);
539+
540+ ot = info->optype;
541+
542+ G_UNLOCK (progress_info);
543+
544+ return ot;
545+}
546+
547+char *
548+marlin_progress_info_get_current_filename (MarlinProgressInfo *info)
549+{
550+ char *cfn;
551+ G_LOCK (progress_info);
552+
553+ if (info->current_filename) {
554+ cfn = g_strdup (info->current_filename);
555+ } else {
556+ cfn = "";
557+ }
558+
559+ G_UNLOCK (progress_info);
560+
561+ return cfn;
562+}
563+
564+int
565+marlin_progress_info_get_remaining_file_count (MarlinProgressInfo *info)
566+{
567+ uint rf;
568+
569+ G_LOCK (progress_info);
570+
571+ rf = info->file_count;
572+
573+ G_UNLOCK (progress_info);
574+
575+ return rf;
576+}
577+
578+int
579+marlin_progress_info_get_total_file_count (MarlinProgressInfo *info)
580+{
581+ uint tf;
582+
583+ G_LOCK (progress_info);
584+
585+ tf = info->total_file_count;
586+
587+ G_UNLOCK (progress_info);
588+
589+ return tf;
590+}
591+
592+void
593+marlin_progress_info_set_destination_uri (MarlinProgressInfo *info,
594+ const char *uri)
595+{
596+ G_LOCK (progress_info);
597+
598+ if (info->destination) {
599+ g_free (info->destination);
600+ }
601+ info->destination = g_strdup (uri);
602+
603+ G_UNLOCK (progress_info);
604+}
605+
606+void
607+marlin_progress_info_set_ctype (MarlinProgressInfo *info,
608+ const char *ctype)
609+{
610+ G_LOCK (progress_info);
611+
612+ if (info->ctype) {
613+ g_free (info->ctype);
614+ }
615+ info->ctype = g_strdup (ctype);
616+
617+ G_UNLOCK (progress_info);
618+}
619+
620+void
621+marlin_progress_info_set_current_filename (MarlinProgressInfo *info,
622+ char *current_filename)
623+{
624+ G_LOCK (progress_info);
625+
626+ if (info->current_filename) {
627+ g_free (info->current_filename);
628+ }
629+ info->current_filename = g_strdup (current_filename);
630+
631+ G_UNLOCK (progress_info);
632+}
633+
634+void
635+marlin_progress_info_set_optype (MarlinProgressInfo *info,
636+ MarlinFileOperationType optype)
637+{
638+ G_LOCK (progress_info);
639+ /* Should only be set once */
640+
641+ info->optype = optype;
642+
643+ G_UNLOCK (progress_info);
644+}
645+
646+void
647+marlin_progress_info_set_remaining_file_count (MarlinProgressInfo *info,
648+ int file_count)
649+{
650+ G_LOCK (progress_info);
651+
652+ info->file_count = file_count;
653+ if (info->total_file_count < 0) { /* Initialized as -1 */
654+ info->total_file_count = file_count;
655+ }
656+
657+ G_UNLOCK (progress_info);
658+}
659+
660+void
661+marlin_progress_info_decrement_remaining_file_count (MarlinProgressInfo *info)
662+{
663+ G_LOCK (progress_info);
664+
665+ info->file_count = info->file_count - 1;
666+
667+ G_UNLOCK (progress_info);
668+}
669+
670 void
671 marlin_progress_info_take_status (MarlinProgressInfo *info,
672 char *status)
673 {
674 G_LOCK (progress_info);
675
676+ if (info->title == NULL) {
677+ info->title = g_strdup (status);
678+ }
679 if (eel_strcmp (info->status, status) != 0) {
680 g_free (info->status);
681 info->status = status;
682@@ -534,6 +719,9 @@
683 {
684 G_LOCK (progress_info);
685
686+ if (info->title == NULL) {
687+ info->title = g_strdup (status);
688+ }
689 if (eel_strcmp (info->status, status) != 0) {
690 g_free (info->status);
691 info->status = g_strdup (status);
692
693=== modified file 'libcore/marlin-progress-info.h'
694--- libcore/marlin-progress-info.h 2016-06-24 18:59:29 +0000
695+++ libcore/marlin-progress-info.h 2017-01-14 12:39:17 +0000
696@@ -26,6 +26,7 @@
697
698 #include <glib-object.h>
699 #include <gio/gio.h>
700+#include "pantheon-files-core.h"
701
702 /* Match .vapi class Marlin.Progress.Info */
703 #define MARLIN_PROGRESS_TYPE_INFO (marlin_progress_info_get_type ())
704@@ -81,6 +82,23 @@
705 void marlin_progress_info_set_progress (MarlinProgressInfo *info,
706 double current,
707 double total);
708+void marlin_progress_info_set_ctype (MarlinProgressInfo *info,
709+ const char *ctype);
710+char* marlin_progress_info_get_ctype (MarlinProgressInfo *info);
711+void marlin_progress_info_set_destination_uri (MarlinProgressInfo *info,
712+ const char *ctype);
713+char* marlin_progress_info_get_destination_uri (MarlinProgressInfo *info);
714+void marlin_progress_info_set_optype (MarlinProgressInfo *info,
715+ MarlinFileOperationType optype);
716+MarlinFileOperationType marlin_progress_info_get_optype (MarlinProgressInfo *info);
717+void marlin_progress_info_set_current_filename (MarlinProgressInfo *info,
718+ char *filename);
719+char* marlin_progress_info_get_current_filename (MarlinProgressInfo *info);
720+void marlin_progress_info_set_remaining_file_count (MarlinProgressInfo *info,
721+ int no_files);
722+void marlin_progress_info_decrement_remaining_file_count (MarlinProgressInfo *info);
723+int marlin_progress_info_get_remaining_file_count (MarlinProgressInfo *info);
724+int marlin_progress_info_get_total_files (MarlinProgressInfo *info);
725 void marlin_progress_info_pulse_progress (MarlinProgressInfo *info);
726
727
728
729=== modified file 'libcore/pantheon-files-core-C.vapi'
730--- libcore/pantheon-files-core-C.vapi 2016-12-23 14:29:30 +0000
731+++ libcore/pantheon-files-core-C.vapi 2017-01-14 12:39:17 +0000
732@@ -208,6 +208,13 @@
733 public bool get_is_finished ();
734 public bool get_is_paused ();
735 public GLib.Cancellable get_cancellable ();
736+// public string get_title ();
737+ public string get_ctype ();
738+ public string get_destination_uri ();
739+ public FileOperationType get_optype ();
740+ public string get_current_filename ();
741+ public int get_remaining_file_count ();
742+ public int get_total_file_count ();
743 }
744
745 [CCode (cheader_filename = "marlin-progress-info-manager.h")]
746
747=== modified file 'libwidgets/CMakeLists.txt'
748--- libwidgets/CMakeLists.txt 2016-12-27 18:19:20 +0000
749+++ libwidgets/CMakeLists.txt 2017-01-14 12:39:17 +0000
750@@ -37,6 +37,9 @@
751 Chrome/BreadcrumbIconList.vala
752 Chrome/ButtonWithMenu.vala
753 Chrome/ImgEventBox.vala
754+ #~ Chrome/ProgressIndicator.vala
755+ #~ Chrome/ProgressIndicatorWithPopover.vala
756+ #~ Chrome/ProgressInfoListRow.vala
757 View/SearchResults.vala
758 Interfaces/SearchableInterface.vala
759 Chrome/ViewSwitcher.vala
760
761=== modified file 'pantheon-files-daemon/CMakeLists.txt'
762--- pantheon-files-daemon/CMakeLists.txt 2014-08-05 18:18:49 +0000
763+++ pantheon-files-daemon/CMakeLists.txt 2017-01-14 12:39:17 +0000
764@@ -1,11 +1,25 @@
765 # Vala stuff
766
767-include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)
768-include_directories(${CMAKE_CURRENT_SOURCE_DIR})
769-include_directories(${CMAKE_CURRENT_BINARY_DIR})
770-include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../libcore/)
771-include_directories(${CMAKE_CURRENT_BINARY_DIR}/../libwidgets/)
772+set (PKGNAME pantheon-files-daemon)
773+
774+include_directories (${CMAKE_SOURCE_DIR}/libcore/)
775+include_directories (${CMAKE_BINARY_DIR}/libcore/)
776+include_directories (${CMAKE_SOURCE_DIR}/libwidgets/)
777+include_directories (${CMAKE_BINARY_DIR}/libwidgets/)
778+
779 find_package(PkgConfig)
780+OPTION (WITH_UNITY "Add Unity launcher support" ON)
781+pkg_check_modules (UNITY unity>=4.0.0)
782+IF (WITH_UNITY AND UNITY_FOUND)
783+ set (UNITY_OPTIONS --define=HAVE_UNITY)
784+ set (UNITY_PKG unity)
785+ include_directories (${UNITY_INCLUDE_DIRS})
786+ link_libraries (${UNITY_LIBRARIES})
787+ link_directories (${UNITY_LIBRARY_DIRS})
788+ELSE (WITH_UNITY AND UNITY_FOUND)
789+ set (UNITY_PKG "")
790+ENDIF (WITH_UNITY AND UNITY_FOUND)
791+
792 pkg_check_modules(DEPS REQUIRED
793 glib-2.0>=2.29.0
794 gthread-2.0
795@@ -16,26 +30,43 @@
796 gee-0.8
797 sqlite3
798 dbus-glib-1
799- libnotify>=0.7.2)
800-set(CFLAGS
801- ${DEPS_CFLAGS} ${DEPS_CFLAGS_OTHER}
802-)
803-set(LIB_PATHS
804- ${DEPS_LIBRARY_DIRS}
805-)
806+ libnotify>=0.7.2
807+ ${UNITY_PKG})
808+
809+set(CFLAGS ${DEPS_CFLAGS} ${DEPS_CFLAGS_OTHER})
810+set(LIB_PATHS {DEPS_LIBRARY_DIRS} ${UNITY_LIBRARIES})
811 link_directories(${LIB_PATHS})
812 add_definitions(${CFLAGS})
813
814-vala_precompile(VALA_C pantheon-files-daemon
815+vala_precompile(VALA_C ${PKGNAME}
816+ main.vala
817 marlind-tagging.vala
818+ QuicklistHandler.vala
819+ ProgressHandlerQuicklistInterface.vala
820 PACKAGES
821 gtk+-3.0
822 gio-2.0
823 gee-0.8
824 sqlite3
825+ dbus-glib-1
826+ libnotify
827+ pantheon-files-core
828+ pantheon-files-widgets
829+ pantheon-files-core-C
830+ ${UNITY_PKG}
831 OPTIONS
832+ --vapidir=${CMAKE_SOURCE_DIR}/libcore
833+ --vapidir=${CMAKE_BINARY_DIR}/libcore
834 --thread)
835-add_executable(pantheon-files-daemon
836- ${VALA_C} )
837-target_link_libraries(pantheon-files-daemon ${DEPS_LIBRARIES})
838-install(TARGETS pantheon-files-daemon RUNTIME DESTINATION bin)
839+
840+add_executable(${PKGNAME} ${VALA_C} )
841+
842+add_dependencies (${PKGNAME} pantheon-files-core pantheon-files-widgets)
843+target_link_libraries (pantheon-files-daemon pantheon-files-core)
844+
845+IF (WITH_UNITY AND UNITY_FOUND)
846+ target_link_libraries (pantheon-files-daemon pantheon-files-core ${UNITY_LIBRARIES})
847+ add_definitions ("-DHAVE_UNITY=1")
848+ENDIF (WITH_UNITY AND UNITY_FOUND)
849+
850+install(TARGETS ${PKGNAME} RUNTIME DESTINATION bin)
851
852=== added file 'pantheon-files-daemon/ProgressHandlerQuicklistInterface.vala'
853--- pantheon-files-daemon/ProgressHandlerQuicklistInterface.vala 1970-01-01 00:00:00 +0000
854+++ pantheon-files-daemon/ProgressHandlerQuicklistInterface.vala 2017-01-14 12:39:17 +0000
855@@ -0,0 +1,31 @@
856+/***
857+ Copyright (C) 2015 elementary Developers
858+
859+ This program is free software: you can redistribute it and/or modify it
860+ under the terms of the GNU Lesser General Public License version 3, as published
861+ by the Free Software Foundation.
862+
863+ This program is distributed in the hope that it will be useful, but
864+ WITHOUT ANY WARRANTY; without even the implied warranties of
865+ MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
866+ PURPOSE. See the GNU General Public License for more details.
867+
868+ You should have received a copy of the GNU General Public License along
869+ with this program. If not, see <http://www.gnu.org/licenses/>.
870+
871+ Authors : Jeremy Wootten <jeremy@elementaryos.org>
872+***/
873+
874+namespace Marlin {
875+ [DBus (name = "org.pantheon.files.ql")]
876+ public interface ProgressHandlerQuicklistInterface : Object {
877+ public signal void cancel_all_infos ();
878+ public signal void show_dialog ();
879+ public signal void show_error_message (string msg); /* For debugging use */
880+
881+ public abstract bool build_unity_quicklist () throws IOError;
882+ public abstract void update_unity_launcher_entries (bool infos_active, bool info_cancelled) throws IOError;
883+ public abstract void progress_changed (double progress) throws IOError;
884+ public abstract void initialize () throws IOError;
885+ }
886+}
887
888=== renamed file 'src/QuicklistHandler.vala' => 'pantheon-files-daemon/QuicklistHandler.vala'
889--- src/QuicklistHandler.vala 2016-06-18 11:25:11 +0000
890+++ pantheon-files-daemon/QuicklistHandler.vala 2017-01-14 12:39:17 +0000
891@@ -13,6 +13,9 @@
892
893 You should have received a copy of the GNU General Public License along
894 with this program. If not, see <http://www.gnu.org/licenses/>.
895+
896+ Authors: Julián Unrrein <junrrein@gmail.com>
897+ Jeremy Wootten <jeremy@elementaryos.org>
898 ***/
899
900 namespace Marlin {
901@@ -23,27 +26,24 @@
902 public List<Dbusmenu.Menuitem> progress_quicklists = null;
903 }
904
905- private QuicklistHandler quicklisthandler_singleton = null;
906-
907- public class QuicklistHandler : Object {
908-
909- public List<Marlin.LauncherEntry> launcher_entries = null;
910-
911- private QuicklistHandler () {
912- this.entry_add (Marlin.APP_DESKTOP);
913-
914- if (this.launcher_entries.length () == 0) {
915- error ("Couldn't find a valid Unity launcher entry.");
916- } else {
917- var bookmarks = Marlin.BookmarkList.get_instance ();
918-
919- bookmarks.contents_changed.connect (() => {
920- debug ("Refreshing Unity dynamic bookmarks.");
921- this.remove_bookmark_quicklists ();
922- this.load_bookmarks (bookmarks);
923- });
924- }
925+ [DBus (name = "org.pantheon.files.ql")]
926+ public class QuicklistHandler : Object, ProgressHandlerQuicklistInterface {
927+
928+ private enum State {
929+ UNINITIALIZED,
930+ LOADING,
931+ LOADED
932 }
933+ /* NOTE This runs as a daemon and is started, if necessary, by DBus. Any warning messages
934+ * produced by it therefore would not normally appear in the terminal. Use "show_error_message"
935+ * signal to produce notifications */
936+
937+ private static QuicklistHandler quicklisthandler_singleton = null;
938+ private GLib.List<string> bookmark_list = null;
939+ private State state = State.UNINITIALIZED;
940+ public List<Marlin.LauncherEntry> launcher_entries;
941+ private GLib.File? bookmarks_file = null;
942+ private GLib.FileMonitor? bookmarks_monitor = null;
943
944 public static unowned QuicklistHandler get_singleton () {
945 if (quicklisthandler_singleton == null)
946@@ -52,18 +52,80 @@
947 return quicklisthandler_singleton;
948 }
949
950+ public void initialize () {
951+ if (state != State.UNINITIALIZED) {
952+ return;
953+ }
954+ state = State.LOADING;
955+ foreach (var marlin_lentry in launcher_entries) {
956+ marlin_lentry.entry.quicklist = null;
957+ marlin_lentry.bookmark_quicklists = null;
958+ marlin_lentry.progress_quicklists = null;
959+ }
960+ launcher_entries = null;
961+
962+ this.entry_add (Marlin.APP_DESKTOP);
963+
964+ load_bookmarks_file_async ();
965+ }
966+
967+ private QuicklistHandler () {
968+ string filename = GLib.Path.build_filename (GLib.Environment.get_user_config_dir (),
969+ "gtk-3.0",
970+ "bookmarks",
971+ null);
972+
973+ var file = GLib.File.new_for_path (filename);
974+ if (file.query_exists (null)) {
975+ bookmarks_file = file;
976+ try {
977+ bookmarks_monitor = bookmarks_file.monitor (GLib.FileMonitorFlags.SEND_MOVED, null);
978+ bookmarks_monitor.set_rate_limit (1000);
979+ bookmarks_monitor.changed.connect (on_bookmarks_file_changed);
980+ }
981+ catch (GLib.Error error) {}
982+ }
983+ }
984+
985+ private void load_bookmarks_file_async () {
986+ if (bookmarks_file == null) {
987+ state = State.LOADED;
988+ return;
989+ }
990+ state = State.LOADED;
991+ bookmarks_file.load_contents_async.begin (null, (obj, res) => {
992+ try {
993+ uint8[] contents;
994+ bookmarks_file.load_contents_async.end (res, out contents, null);
995+ if (contents != null) {
996+ bookmark_list_from_string ((string)contents);
997+ load_bookmarks ();
998+ }
999+ }
1000+ catch (GLib.Error error) {}
1001+ });
1002+ }
1003+
1004+ private void bookmark_list_from_string (string contents) {
1005+ string [] lines = contents.split ("\n");
1006+ foreach (string line in lines) {
1007+ if (line[0] == '\0' || line[0] == ' ') {
1008+ continue; /* ignore blank lines */
1009+ }
1010+ /* Keep both the full uri and the custom label (if any) */
1011+ bookmark_list.append (line);
1012+ }
1013+ }
1014+
1015 private void entry_add (string entry_id) {
1016 var unity_lentry = Unity.LauncherEntry.get_for_desktop_id (entry_id);
1017-
1018 if (unity_lentry != null) {
1019 var marlin_lentry = new Marlin.LauncherEntry ();
1020 marlin_lentry.entry = unity_lentry;
1021
1022 this.launcher_entries.prepend (marlin_lentry);
1023-
1024 /* Ensure dynamic quicklist exists */
1025 Dbusmenu.Menuitem ql = unity_lentry.quicklist;
1026-
1027 if (ql == null) {
1028 ql = new Dbusmenu.Menuitem ();
1029 unity_lentry.quicklist = ql;
1030@@ -76,42 +138,154 @@
1031 var unity_lentry = marlin_lentry.entry;
1032 Dbusmenu.Menuitem ql = unity_lentry.quicklist;
1033
1034- if (ql == null)
1035- break;
1036-
1037- foreach (var menuitem in marlin_lentry.bookmark_quicklists) {
1038- ql.child_delete (menuitem);
1039+ if (ql != null) {
1040+ foreach (var menuitem in marlin_lentry.bookmark_quicklists) {
1041+ ql.child_delete (menuitem);
1042+ }
1043 }
1044
1045 marlin_lentry.bookmark_quicklists = null;
1046+ bookmark_list = null;
1047 }
1048 }
1049
1050- private void load_bookmarks (Marlin.BookmarkList bookmarks) {
1051- var bookmark_count = bookmarks.length ();
1052-
1053- for (int index = 0; index < bookmark_count; index++) {
1054- var bookmark = bookmarks.item_at (index);
1055-
1056- if (bookmark.uri_known_not_to_exist ())
1057- continue;
1058-
1059+ private void load_bookmarks () {
1060+ int index = -1;
1061+ foreach (string line in bookmark_list) {
1062+ string [] parts = line.split (" ", 2);
1063+ /* First part is full uri and second part is the custom label */
1064+ GLib.List<GLib.File> files = null;
1065+ GLib.File file = GLib.File.new_for_uri (parts[0]);
1066+ files.append (file);
1067+ string label = parts.length > 1 ? parts[1] : file.get_basename ();
1068+ index++;
1069 foreach (var marlin_lentry in this.launcher_entries) {
1070 var unity_lentry = marlin_lentry.entry;
1071 Dbusmenu.Menuitem ql = unity_lentry.quicklist;
1072 var menuitem = new Dbusmenu.Menuitem ();
1073-
1074- menuitem.property_set ("label", bookmark.label);
1075+ menuitem.property_set (Dbusmenu.MENUITEM_PROP_TYPE,
1076+ Dbusmenu.CLIENT_TYPES_DEFAULT);
1077+ menuitem.property_set (Dbusmenu.MENUITEM_PROP_LABEL,
1078+ label);
1079 menuitem.item_activated.connect (() => {
1080- var location = bookmark.get_location ();
1081- Marlin.Application.get ().create_window (location);
1082+ try {
1083+ var launcher = GLib.AppInfo.create_from_commandline ("pantheon-files",
1084+ null,
1085+ GLib.AppInfoCreateFlags.SUPPORTS_URIS);
1086+ launcher.launch (files, null);
1087+ } catch (GLib.Error e) {}
1088 });
1089
1090 ql.child_add_position (menuitem, index);
1091 marlin_lentry.bookmark_quicklists.prepend (menuitem);
1092 }
1093 }
1094- }
1095-
1096+ state = State.LOADED;
1097+ }
1098+
1099+ private void on_bookmarks_file_changed (GLib.File file, GLib.File? other_file, GLib.FileMonitorEvent event_type) {
1100+ if (event_type != FileMonitorEvent.CREATED || state == State.LOADING) {
1101+ return;
1102+ }
1103+
1104+ state = State.LOADING;
1105+ remove_bookmark_quicklists ();
1106+
1107+ load_bookmarks_file_async ();
1108+ }
1109+
1110+ public bool build_unity_quicklist () {
1111+ /* Create menu items for the quicklist */
1112+ foreach (var marlin_lentry in this.launcher_entries) {
1113+ if (marlin_lentry.progress_quicklists != null) {
1114+ continue;
1115+ }
1116+ /* Separator between bookmarks and progress items */
1117+ var separator = new Dbusmenu.Menuitem ();
1118+
1119+ separator.property_set (Dbusmenu.MENUITEM_PROP_TYPE,
1120+ Dbusmenu.CLIENT_TYPES_SEPARATOR);
1121+ separator.property_set (Dbusmenu.MENUITEM_PROP_LABEL,
1122+ "Progress items separator");
1123+ marlin_lentry.progress_quicklists.append (separator);
1124+
1125+#if 0 /* Now use popover but keep possibility of showing a dialog when Files window is closed */
1126+ /* "Show progress window" menu item */
1127+ var show_menuitem = new Dbusmenu.Menuitem ();
1128+
1129+ show_menuitem.property_set (Dbusmenu.MENUITEM_PROP_TYPE,
1130+ Dbusmenu.CLIENT_TYPES_DEFAULT);
1131+ show_menuitem.property_set (Dbusmenu.MENUITEM_PROP_LABEL,
1132+ _("Show Copy Dialog"));
1133+
1134+ show_menuitem.item_activated.connect (() => {
1135+ show_dialog ();
1136+ });
1137+
1138+ marlin_lentry.progress_quicklists.append (show_menuitem);
1139+
1140+ /* "Cancel in-progress operations" menu item */
1141+#endif
1142+ var cancel_menuitem = new Dbusmenu.Menuitem ();
1143+
1144+ cancel_menuitem.property_set (Dbusmenu.MENUITEM_PROP_TYPE,
1145+ Dbusmenu.CLIENT_TYPES_DEFAULT);
1146+ cancel_menuitem.property_set (Dbusmenu.MENUITEM_PROP_LABEL,
1147+ _("Cancel All In-progress Actions"));
1148+
1149+ cancel_menuitem.item_activated.connect (() => {
1150+ cancel_all_infos ();
1151+ });
1152+
1153+ marlin_lentry.progress_quicklists.append (cancel_menuitem);
1154+ }
1155+
1156+ return this.launcher_entries.length () > 0;
1157+ }
1158+
1159+ public void update_unity_launcher_entries (bool infos_active, bool info_cancelled) {
1160+ foreach (var marlin_lentry in this.launcher_entries) {
1161+ Unity.LauncherEntry unity_lentry = marlin_lentry.entry;
1162+
1163+ if (infos_active) {
1164+ unity_lentry.progress_visible = true;
1165+ show_progress_quicklist (marlin_lentry, true);
1166+ } else {
1167+ unity_lentry.progress_visible = false;
1168+ unity_lentry.progress = 0.0;
1169+ show_progress_quicklist (marlin_lentry, false);
1170+ if (info_cancelled) {
1171+ unity_lentry.urgent = true;
1172+
1173+ Timeout.add_seconds (2, () => {
1174+ unity_lentry.urgent = false;
1175+ return false;
1176+ });
1177+ }
1178+ }
1179+ }
1180+ }
1181+
1182+ private void show_progress_quicklist (Marlin.LauncherEntry marlin_lentry, bool show) {
1183+ Unity.LauncherEntry unity_lentry = marlin_lentry.entry;
1184+ Dbusmenu.Menuitem quicklist = unity_lentry.quicklist;
1185+
1186+ foreach (Dbusmenu.Menuitem menuitem in marlin_lentry.progress_quicklists) {
1187+ if (show) {
1188+ if (menuitem.get_parent () == null) {
1189+ quicklist.child_add_position (menuitem, -1);
1190+ }
1191+ } else {
1192+ quicklist.child_delete (menuitem);
1193+ }
1194+ }
1195+ }
1196+
1197+ public void progress_changed (double progress) {
1198+ foreach (Marlin.LauncherEntry marlin_lentry in this.launcher_entries) {
1199+ Unity.LauncherEntry unity_lentry = marlin_lentry.entry;
1200+ unity_lentry.progress = progress;
1201+ }
1202+ }
1203 }
1204 }
1205
1206=== added file 'pantheon-files-daemon/main.vala'
1207--- pantheon-files-daemon/main.vala 1970-01-01 00:00:00 +0000
1208+++ pantheon-files-daemon/main.vala 2017-01-14 12:39:17 +0000
1209@@ -0,0 +1,68 @@
1210+/* -*- Mode: C; indent-tabs-mode: s; c-basic-offset: 4; tab-width: 4 -*- */
1211+/*
1212+ * Copyright (C) 2010 Jordi Puigdellívol <jordi@gloobus.net>
1213+ * Copyright (C) 2015 Elementary Developers
1214+ *
1215+ * This library is free software; you can redistribute it and/or modify
1216+ * it under the terms of the GNU Lesser General Public License
1217+ * version 3.0 as published by the Free Software Foundation.
1218+ *
1219+ * This library is distributed in the hope that it will be useful,
1220+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1221+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1222+ * GNU Lesser General Public License version 3.0 for more details.
1223+ *
1224+ * You should have received a copy of the GNU Lesser General Public
1225+ * License along with this library. If not, see
1226+ * <http://www.gnu.org/licenses/>.
1227+ *
1228+ * Authors: Jordi Puigdellívol <jordi@gloobus.net>
1229+ * ammonkey <am.monkeyd@gmail.com>
1230+ * Jeremy Wootten <jeremy@elementaryos.org>
1231+*/
1232+
1233+
1234+ void on_bus_aquired (DBusConnection conn, string n) {
1235+ try {
1236+ string name = "/org/pantheon/files/db";
1237+ var object = new MarlinTags ();
1238+ conn.register_object (name, object);
1239+ message ("MarlinTags object registered with dbus connection name %s", name);
1240+ } catch (IOError e) {
1241+ error ("Could not register service");
1242+ }
1243+ }
1244+ void on_ql_bus_aquired (DBusConnection conn) {
1245+ uint conn_id = 0;
1246+ try {
1247+ string name = "/org/pantheon/files/ql";
1248+ Marlin.ProgressHandlerQuicklistInterface object = Marlin.QuicklistHandler.get_singleton ();
1249+ conn_id = conn.register_object (name, object);
1250+ message ("QuicklistHandler object registered with dbus connection name %s", name);
1251+ object.initialize ();
1252+ } catch (IOError e) {
1253+ error ("Could not register service");
1254+ }
1255+ }
1256+
1257+ // Exit C function to quit the loop
1258+ extern void exit (int exit_code);
1259+
1260+ void on_name_lost (DBusConnection connection, string name) {
1261+ critical ("Name %s was not acquired", name);
1262+ exit (-1);
1263+ }
1264+
1265+ void main () {
1266+ Bus.own_name (BusType.SESSION, "org.pantheon.files.db", BusNameOwnerFlags.NONE,
1267+ on_bus_aquired,
1268+ () => {},
1269+ on_name_lost);
1270+
1271+ Bus.own_name (BusType.SESSION, "org.pantheon.files.ql", BusNameOwnerFlags.NONE,
1272+ on_ql_bus_aquired,
1273+ () => {},
1274+ on_name_lost);
1275+
1276+ new MainLoop ().run ();
1277+ }
1278
1279=== modified file 'pantheon-files-daemon/marlind-tagging.vala'
1280--- pantheon-files-daemon/marlind-tagging.vala 2016-06-18 11:25:11 +0000
1281+++ pantheon-files-daemon/marlind-tagging.vala 2017-01-14 12:39:17 +0000
1282@@ -113,14 +113,12 @@
1283 var uri = escape(raw_uri);
1284
1285 var sql = "INSERT OR REPLACE INTO tags (uri,content_type,modified_time) VALUES ('"+uri+"','"+content_type+"',"+modified_time.to_string()+");\n";
1286- //var c = "INSERT OR IGNORE INTO tags (uri,content_type,modified_time) VALUES ('"+uri+"','"+content_type+"',"+modified_time.to_string()+");\n";
1287
1288 int rc = db.exec (sql, null, null);
1289 if (rc != Sqlite.OK) {
1290 stderr.printf ("[record_uri: SQL error] %d, %s\n", rc, db.errmsg ());
1291 return false;
1292 }
1293- //stdout.printf("[Consult]: %s\n",c);
1294
1295 return true;
1296 }
1297@@ -143,7 +141,6 @@
1298 stderr.printf ("[record_uri: SQL error] %d, %s\n", rc, db.errmsg ());
1299 return false;
1300 }
1301- //stdout.printf("[Consult]: %s\n",sql);
1302
1303 return true;
1304 }
1305@@ -163,7 +160,6 @@
1306 -1, out stmt);
1307 assert (rc == Sqlite.OK);
1308 rc = stmt.step();
1309-
1310 vb.open (new VariantType ("as"));
1311
1312 switch (rc) {
1313@@ -209,7 +205,6 @@
1314 case Sqlite.ROW:
1315 for (col = 0; col < cols; col++) {
1316 txt = stmt.column_text(col);
1317- //print ("%s = %s\n", stmt.column_name (col), txt);
1318 }
1319 break;
1320 default:
1321@@ -217,7 +212,6 @@
1322 break;
1323 }
1324 } while (rc == Sqlite.ROW);
1325- //stdout.printf("[getColor]: %s\n", txt);
1326
1327 int ret = int.parse(txt);
1328 /* It appears that a db error return -1, we got to check the value just in case */
1329@@ -309,9 +303,6 @@
1330 }
1331
1332 private void upgrade_database () {
1333-
1334- // version 2
1335-
1336 if (!has_column("tags", "content_type")) {
1337 message("upgrade_database: adding content_type column to tags");
1338 if (!add_column("tags", "content_type", "TEXT"))
1339@@ -329,52 +320,3 @@
1340 }
1341 }
1342 }
1343-
1344-/* =============== Main ==================== */
1345-/*void main (string[] args) {
1346-
1347- MarlinTags t = new MarlinTags();
1348-
1349- t.openMarlinDB();
1350-
1351- t.setColor("file:///home/jordi" ,MARLIN_RED);
1352- t.setColor("file:///home/dev" ,MARLIN_YELLOW);
1353-
1354-//t.deleteEntry(File.new_for_path ("/home/dev")); //When deleting files
1355-//t.deleteEntry("/home/documents");
1356-
1357-//t.clearDB();
1358-t.showTable("tags");
1359-
1360-
1361-// DBUS Things
1362-print("\n\nColor for file is %i\n",
1363-t.getColor("file:///home/jordi"));
1364-}*/
1365-
1366-
1367-void on_bus_aquired (DBusConnection conn) {
1368- try {
1369- conn.register_object ("/org/pantheon/files/db", new MarlinTags ());
1370- } catch (IOError e) {
1371- error ("Could not register service");
1372- }
1373-}
1374-
1375-// Exit C function to quit the loop
1376-extern void exit (int exit_code);
1377-
1378-void on_bus_lost (DBusConnection connection, string name) {
1379- critical ("Could not aquire name.");
1380- exit (-1);
1381-}
1382-
1383-void main () {
1384- Bus.own_name (BusType.SESSION, "org.pantheon.files.db", BusNameOwnerFlags.NONE,
1385- on_bus_aquired,
1386- () => {},
1387- on_bus_lost);
1388-
1389- new MainLoop ().run ();
1390-}
1391-
1392
1393=== added file 'pantheon-files-daemon/pantheon-files-daemon.vapi'
1394--- pantheon-files-daemon/pantheon-files-daemon.vapi 1970-01-01 00:00:00 +0000
1395+++ pantheon-files-daemon/pantheon-files-daemon.vapi 2017-01-14 12:39:17 +0000
1396@@ -0,0 +1,28 @@
1397+/***
1398+ Copyright (C) 2015 elementary Developers
1399+
1400+ This program is free software: you can redistribute it and/or modify it
1401+ under the terms of the GNU Lesser General Public License version 3, as published
1402+ by the Free Software Foundation.
1403+
1404+ This program is distributed in the hope that it will be useful, but
1405+ WITHOUT ANY WARRANTY; without even the implied warranties of
1406+ MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1407+ PURPOSE. See the GNU General Public License for more details.
1408+
1409+ You should have received a copy of the GNU General Public License along
1410+ with this program. If not, see <http://www.gnu.org/licenses/>.
1411+
1412+ Authors : Jeremy Wootten <jeremy@elementaryos.org>
1413+***/
1414+
1415+[DBus (name = "org.pantheon.files.ql")]
1416+interface ProgressHandlerQuicklistInterface : Object {
1417+ public signal void cancel_all_infos ();
1418+ public signal void show_dialog ();
1419+
1420+ public abstract async bool build_unity_quicklist () throws IOError;
1421+ public abstract async void update_unity_launcher_entries (bool infos_active, bool info_cancelled) throws IOError;
1422+ public abstract async void progress_changed (double progress) throws IOError;
1423+
1424+}
1425
1426=== modified file 'src/Application.vala'
1427--- src/Application.vala 2016-12-31 19:42:09 +0000
1428+++ src/Application.vala 2017-01-14 12:39:17 +0000
1429@@ -26,10 +26,11 @@
1430 public class Marlin.Application : Granite.Application {
1431
1432 private VolumeMonitor volume_monitor;
1433- private Marlin.Progress.UIHandler progress_handler;
1434+ public Marlin.Progress.UIHandler progress_handler {get; private set;}
1435 private Marlin.ClipboardManager clipboard;
1436 private Marlin.Thumbnailer thumbnailer;
1437 private Gtk.RecentManager recent;
1438+ private double current_progress = 0.0;
1439
1440 private const int MARLIN_ACCEL_MAP_SAVE_DELAY = 15;
1441 private const uint MAX_WINDOWS = 25;
1442@@ -88,12 +89,15 @@
1443 Marlin.IconInfo.clear_caches ();
1444 });
1445
1446- progress_handler = new Marlin.Progress.UIHandler (this);
1447-
1448 this.clipboard = Marlin.ClipboardManager.get_for_display ();
1449 this.thumbnailer = Marlin.Thumbnailer.get ();
1450 this.recent = new Gtk.RecentManager ();
1451
1452+ progress_handler = new Marlin.Progress.UIHandler (this);
1453+ progress_handler.new_progress_info.connect (on_uihandler_new_progress_info);
1454+ progress_handler.show_progress.connect (on_uihandler_show_progress);
1455+ progress_handler.overall_progress_changed.connect (on_uihandler_overall_progress_changed);
1456+ progress_handler.completed.connect (on_uihandler_completed);
1457 plugins = new Marlin.PluginManager (Config.PLUGIN_DIR);
1458
1459 /**TODO** move the volume manager here? */
1460@@ -102,12 +106,27 @@
1461 this.volume_monitor = VolumeMonitor.get ();
1462 this.volume_monitor.mount_removed.connect (mount_removed_callback);
1463
1464-#if HAVE_UNITY
1465- QuicklistHandler.get_singleton ();
1466-#endif
1467-
1468 window_count = 0;
1469- this.window_added.connect_after (() => {window_count++;});
1470+ this.window_added.connect ((w) => {
1471+ window_count++;
1472+ if (progress_handler.get_active_info_count () > 0) {
1473+ /* Files window may previously have been closed, or a new window added with
1474+ * file operations ongoing. In this case, we need to show the progress indicator
1475+ * in the header bar.
1476+ */
1477+ Timeout.add (200, () => {
1478+ /* Allow time for the top menu to be realized */
1479+ var win = (Marlin.View.Window)w;
1480+ foreach (Marlin.Progress.Info info in progress_handler.get_active_info_list ()) {
1481+ win.add_progress_info (info);
1482+ }
1483+ win.show_progress (true);
1484+ win.overall_progress_changed (current_progress);
1485+ return false;
1486+ });
1487+ }
1488+ });
1489+
1490 this.window_removed.connect (() => {
1491 window_count--;
1492 });
1493@@ -222,8 +241,9 @@
1494
1495 public new void quit () {
1496 /* Protect against holding Ctrl-Q down */
1497- if (quitting)
1498+ if (quitting) {
1499 return;
1500+ }
1501
1502 quitting = true;
1503 unowned List<Gtk.Window> window_list = this.get_windows ();
1504@@ -248,6 +268,34 @@
1505 }
1506 }
1507
1508+ private void on_uihandler_new_progress_info (Marlin.Progress.Info info) {
1509+ /* Notify each window */
1510+ foreach (var window in this.get_windows ()) {
1511+ ((Marlin.View.Window)window).add_progress_info (info);
1512+ }
1513+ }
1514+
1515+ private void on_uihandler_show_progress (bool show) {
1516+ /* Notify each window */
1517+ foreach (var window in this.get_windows ()) {
1518+ ((Marlin.View.Window)window).show_progress (show);
1519+ }
1520+ }
1521+
1522+ private void on_uihandler_overall_progress_changed (double progress) {
1523+ /* Notify each window */
1524+ foreach (var window in this.get_windows ()) {
1525+ ((Marlin.View.Window)window).overall_progress_changed (progress);
1526+ }
1527+ current_progress = progress;
1528+ }
1529+
1530+ private void on_uihandler_completed () {
1531+ if (get_windows () != null) {
1532+ current_progress = 0.0;
1533+ }
1534+ }
1535+
1536 private void init_schemas () {
1537 /* GSettings parameters */
1538 Preferences.settings = new Settings ("org.pantheon.files.preferences");
1539
1540=== modified file 'src/CMakeLists.txt'
1541--- src/CMakeLists.txt 2016-12-27 18:19:20 +0000
1542+++ src/CMakeLists.txt 2017-01-14 12:39:17 +0000
1543@@ -58,14 +58,8 @@
1544 set (PLANK_OPTIONS --define=HAVE_PLANK_0_11)
1545 endif ()
1546
1547-set (CFLAGS
1548- ${DEPS_CFLAGS} ${DEPS_CFLAGS_OTHER}
1549-)
1550-
1551-set (LIB_PATHS
1552- ${DEPS_LIBRARY_DIRS}
1553-)
1554-
1555+set (CFLAGS ${DEPS_CFLAGS} ${DEPS_CFLAGS_OTHER})
1556+set (LIB_PATHS ${DEPS_LIBRARY_DIRS})
1557 link_directories (${LIB_PATHS})
1558 add_definitions (${CFLAGS} -O2)
1559
1560@@ -77,7 +71,6 @@
1561 marlin-deep-count.vala
1562 ProgressUIHandler.vala
1563 TextRenderer.vala
1564- QuicklistHandler.vala
1565 ClipboardManager.vala
1566 Dialogs/AbstractPropertiesDialog.vala
1567 Dialogs/ChooseAppDialog.vala
1568@@ -98,6 +91,7 @@
1569 View/Sidebar.vala
1570 View/Slot.vala
1571 View/Miller.vala
1572+ ${CMAKE_SOURCE_DIR}/pantheon-files-daemon/ProgressHandlerQuicklistInterface.vala
1573 View/Widgets/AbstractEditableLabel.vala
1574 View/Widgets/BreadcrumbsEntry.vala
1575 View/Widgets/DiskRenderer.vala
1576@@ -106,10 +100,11 @@
1577 View/Widgets/MultiLineEditableLabel.vala
1578 View/Widgets/OverlayBar.vala
1579 View/Widgets/PermissionButton.vala
1580- View/Widgets/ProgressInfoWidget.vala
1581 View/Widgets/SingleLineEditableLabel.vala
1582 View/Widgets/TopMenu.vala
1583-
1584+ View/Widgets/ProgressIndicator.vala
1585+ View/Widgets/ProgressIndicatorWithPopover.vala
1586+ View/Widgets/ProgressInfoListRow.vala
1587 PACKAGES
1588 gtk+-3.0
1589 gio-2.0
1590@@ -162,6 +157,7 @@
1591 View/Sidebar.vala
1592 View/Slot.vala
1593 View/Miller.vala
1594+ ${CMAKE_SOURCE_DIR}/pantheon-files-daemon/ProgressHandlerQuicklistInterface.vala
1595 View/Widgets/AbstractEditableLabel.vala
1596 View/Widgets/BreadcrumbsEntry.vala
1597 View/Widgets/DiskRenderer.vala
1598@@ -170,10 +166,11 @@
1599 View/Widgets/MultiLineEditableLabel.vala
1600 View/Widgets/OverlayBar.vala
1601 View/Widgets/PermissionButton.vala
1602- View/Widgets/ProgressInfoWidget.vala
1603 View/Widgets/SingleLineEditableLabel.vala
1604 View/Widgets/TopMenu.vala
1605-
1606+ View/Widgets/ProgressIndicator.vala
1607+ View/Widgets/ProgressIndicatorWithPopover.vala
1608+ View/Widgets/ProgressInfoListRow.vala
1609 PACKAGES
1610 gtk+-3.0
1611 gio-2.0
1612
1613=== modified file 'src/ProgressUIHandler.vala'
1614--- src/ProgressUIHandler.vala 2016-12-18 22:01:14 +0000
1615+++ src/ProgressUIHandler.vala 2017-01-14 12:39:17 +0000
1616@@ -20,27 +20,47 @@
1617 Jeremy Wootten <jeremy@elementaryos.org>
1618 ***/
1619
1620-/*** One instance of this class is owned by the application and handles UI for file transfers initiated by
1621- * of the app windows. Feedback is provided by a dialog window which appears if a transfer takes longer than
1622- * approximately 1 second. The unity launcher is also updated if present and a notification is sent of the
1623- * completion of the operation unless it was cancelled by the user.
1624-***/
1625+/** This class controls the UI relating to file operations in progress (Unity launchers, notifications and signals
1626+ * to progress indicators) but does not itself display any widgets. Displayed widgets are in libwidgets.
1627+ * **/
1628 public class Marlin.Progress.UIHandler : Object {
1629-
1630 private Marlin.Progress.InfoManager manager = null;
1631-#if HAVE_UNITY
1632- private Marlin.QuicklistHandler quicklist_handler = null;
1633-#endif
1634- private Gtk.Dialog progress_window = null;
1635- private Gtk.Widget window_vbox = null;
1636+ private ProgressHandlerQuicklistInterface qldaemon;
1637+ private Marlin.Application application;
1638+
1639 private uint active_infos = 0;
1640-
1641- private Marlin.Application application;
1642+ private bool updating_overall_progress = false;
1643+
1644+ private const string ACTION_DETAILS = "details";
1645+ private const string TITLE = _("File Manager Operations");
1646+ private const string ICON_NAME = "system-file-manager";
1647+ private const int ICON_SIZE = 64;
1648+ private const string CANCELLED_ICON_NAME = "dialog-warning";
1649+
1650+ public signal void overall_progress_changed (double progress);
1651+ public signal void show_progress (bool show);
1652+ public signal void new_progress_info (Marlin.Progress.Info info);
1653+ public signal void completed ();
1654
1655 public UIHandler (Marlin.Application app) {
1656 this.manager = new Marlin.Progress.InfoManager ();
1657 this.application = app;
1658
1659+ try {
1660+ qldaemon = Bus.get_proxy_sync (BusType.SESSION, "org.pantheon.files.ql",
1661+ "/org/pantheon/files/ql");
1662+ qldaemon.cancel_all_infos.connect (() => {
1663+ cancel_all ();
1664+ });
1665+
1666+ qldaemon.show_error_message.connect (show_daemon_notification);
1667+
1668+ qldaemon.initialize ();
1669+
1670+ } catch (IOError e) {
1671+ stderr.printf ("%s\n", e.message);
1672+ }
1673+
1674 manager.new_progress_info.connect ((info) => {
1675 info.started.connect (progress_info_started_cb);
1676 });
1677@@ -72,27 +92,25 @@
1678 private void progress_info_started_cb (Marlin.Progress.Info info) {
1679 application.hold ();
1680
1681- if (info == null || !(info is Marlin.Progress.Info) ||
1682- info.get_is_finished () || info.get_cancellable ().is_cancelled ()) {
1683-
1684- return;
1685- }
1686-
1687- this.active_infos++;
1688- info.finished.connect (progress_info_finished_cb);
1689-
1690- var operation_running = false;
1691+ info.finished.connect (() => {
1692+ debug ("Release application");
1693+ application.release ();
1694+ });
1695+
1696+ bool operation_running = false;
1697+ /* Do not create progress info widget until operation has been running for at least 1 seconds
1698+ * not including time while paused.
1699+ */
1700 Timeout.add_full (GLib.Priority.LOW, 500, () => {
1701 if (info == null || !(info is Marlin.Progress.Info) ||
1702 info.get_is_finished () || info.get_cancellable ().is_cancelled ()) {
1703
1704 return false;
1705 }
1706-
1707 if (info.get_is_paused ()) {
1708 return true;
1709 } else if (operation_running && !info.get_is_finished ()) {
1710- add_progress_info_to_window (info);
1711+ handle_new_progress_info (info);
1712 return false;
1713 } else {
1714 operation_running = true;
1715@@ -101,13 +119,13 @@
1716 });
1717 }
1718
1719- private void add_progress_info_to_window (Marlin.Progress.Info info) {
1720- if (this.active_infos == 1) {
1721- /* This is the only active operation, present the window */
1722- add_to_window (info);
1723- (this.progress_window as Gtk.Window).present ();
1724- } else if (this.progress_window.visible) {
1725- add_to_window (info);
1726+ private void handle_new_progress_info (Marlin.Progress.Info info) {
1727+ this.active_infos++;
1728+ info.finished.connect (progress_info_finished_cb);
1729+ new_progress_info (info);
1730+
1731+ if (this.active_infos > 0) {
1732+ show_progress (true);
1733 }
1734
1735 #if HAVE_UNITY
1736@@ -115,46 +133,7 @@
1737 #endif
1738 }
1739
1740- private void add_to_window (Marlin.Progress.Info info) {
1741- ensure_window ();
1742-
1743- var progress_widget = new Marlin.Progress.InfoWidget (info);
1744- (this.window_vbox as Gtk.Box).pack_start (progress_widget, false, false, 6);
1745-
1746- progress_widget.show ();
1747- if (this.progress_window.visible) {
1748- (this.progress_window as Gtk.Window).present ();
1749- }
1750- }
1751-
1752- private void ensure_window () {
1753- if (this.progress_window == null) {
1754- /* This provides an undeletable, unminimisable window in which to show the info widgets */
1755- this.progress_window = new Gtk.Dialog ();
1756- this.progress_window.resizable = false;
1757- this.progress_window.deletable = false;
1758- this.progress_window.title = _("File Operations");
1759- this.progress_window.set_wmclass ("file_progress", "Marlin");
1760- this.progress_window.icon_name = "system-file-manager";
1761-
1762- this.window_vbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 5);
1763-
1764- this.progress_window.get_content_area ().set_border_width (10);
1765- this.progress_window.get_content_area ().add (this.window_vbox);
1766- this.window_vbox.show ();
1767-
1768- this.progress_window.delete_event.connect ((widget, event) => {
1769- widget.hide ();
1770- return true;
1771- });
1772- }
1773-
1774- progress_window.set_transient_for (application.get_active_window ());
1775- }
1776-
1777 private void progress_info_finished_cb (Marlin.Progress.Info info) {
1778- application.release ();
1779-
1780 if (active_infos > 0) {
1781 this.active_infos--;
1782 /* Only notify if application is not focussed. Add a delay
1783@@ -172,23 +151,26 @@
1784 } else {
1785 warning ("Attempt to decrement zero active infos");
1786 }
1787- /* For rapid file transfers this can get called before progress window was been created */
1788- if (active_infos < 1 && progress_window != null && progress_window.visible) {
1789- (this.progress_window as Gtk.Window).hide ();
1790+
1791+ if (active_infos < 1) {
1792+ show_progress (false);
1793 }
1794 #if HAVE_UNITY
1795 update_unity_launcher (info, false);
1796 #endif
1797+ show_operation_complete_notification (info, active_infos < 1);
1798 }
1799
1800 private void show_operation_complete_notification (Marlin.Progress.Info info, bool all_finished) {
1801+ /* Some operations may have been cancelled or not successful */
1802+ string result;
1803 if (info.get_cancellable ().is_cancelled ()) {
1804 return; /* No notification of cancellation action required */
1805+ } else {
1806+ /* TRANSLATORS: %s will be replaced by the title of the file operation */
1807+ result = (_("Completed %s")).printf (info.get_title ());
1808 }
1809
1810- /// TRANSLATORS: %s will be replaced by the title of the file operation
1811- var result = (_("Completed %s")).printf (info.get_title ());
1812-
1813 if (all_finished) {
1814 result = result + "\n" + _("All file operations have ended");
1815 }
1816@@ -199,101 +181,48 @@
1817 application.send_notification ("Pantheon Files Operation", complete_notification);
1818 }
1819
1820+ private void show_daemon_notification (string msg) {
1821+ var notification = new GLib.Notification (_("QuicklistHandler message"));
1822+ notification.set_body (msg);
1823+ notification.set_icon (new GLib.ThemedIcon (Marlin.ICON_APP_LOGO));
1824+ application.send_notification ("Pantheon Files Operation", notification);
1825+ }
1826+
1827 #if HAVE_UNITY
1828 private void update_unity_launcher (Marlin.Progress.Info info,
1829 bool added) {
1830
1831- if (this.quicklist_handler == null) {
1832- this.quicklist_handler = QuicklistHandler.get_singleton ();
1833-
1834- if (this.quicklist_handler == null)
1835- return;
1836-
1837- build_unity_quicklist ();
1838- }
1839-
1840- foreach (var marlin_lentry in this.quicklist_handler.launcher_entries)
1841- update_unity_launcher_entry (info, marlin_lentry);
1842-
1843- if (added)
1844- info.progress_changed.connect (unity_progress_changed);
1845- }
1846-
1847- private void build_unity_quicklist () {
1848- /* Create menu items for the quicklist */
1849- foreach (var marlin_lentry in this.quicklist_handler.launcher_entries) {
1850- /* Separator between bookmarks and progress items */
1851- var separator = new Dbusmenu.Menuitem ();
1852-
1853- separator.property_set (Dbusmenu.MENUITEM_PROP_TYPE,
1854- Dbusmenu.CLIENT_TYPES_SEPARATOR);
1855- separator.property_set (Dbusmenu.MENUITEM_PROP_LABEL,
1856- "Progress items separator");
1857- marlin_lentry.progress_quicklists.append (separator);
1858-
1859- /* "Show progress window" menu item */
1860- var show_menuitem = new Dbusmenu.Menuitem ();
1861-
1862- show_menuitem.property_set (Dbusmenu.MENUITEM_PROP_LABEL,
1863- _("Show Copy Dialog"));
1864-
1865- show_menuitem.item_activated.connect (() => {
1866- (this.progress_window as Gtk.Window).present ();
1867- });
1868-
1869- marlin_lentry.progress_quicklists.append (show_menuitem);
1870-
1871- /* "Cancel in-progress operations" menu item */
1872- var cancel_menuitem = new Dbusmenu.Menuitem ();
1873-
1874- cancel_menuitem.property_set (Dbusmenu.MENUITEM_PROP_LABEL,
1875- _("Cancel All In-progress Actions"));
1876-
1877- cancel_menuitem.item_activated.connect (() => {
1878- unowned List<Marlin.Progress.Info> infos = this.manager.get_all_infos ();
1879-
1880- foreach (var info in infos)
1881- info.cancel ();
1882- });
1883-
1884- marlin_lentry.progress_quicklists.append (cancel_menuitem);
1885- }
1886- }
1887-
1888- private void update_unity_launcher_entry (Marlin.Progress.Info info,
1889- Marlin.LauncherEntry marlin_lentry) {
1890- Unity.LauncherEntry unity_lentry = marlin_lentry.entry;
1891-
1892- if (this.active_infos > 0) {
1893- unity_lentry.progress_visible = true;
1894- unity_progress_changed ();
1895- show_unity_quicklist (marlin_lentry, true);
1896- } else {
1897- unity_lentry.progress_visible = false;
1898- unity_lentry.progress = 0.0;
1899- show_unity_quicklist (marlin_lentry, false);
1900- }
1901- }
1902-
1903- private void show_unity_quicklist (Marlin.LauncherEntry marlin_lentry,
1904- bool show) {
1905-
1906- Unity.LauncherEntry unity_lentry = marlin_lentry.entry;
1907- Dbusmenu.Menuitem quicklist = unity_lentry.quicklist;
1908-
1909- foreach (Dbusmenu.Menuitem menuitem in marlin_lentry.progress_quicklists) {
1910- var parent = menuitem.get_parent ();
1911- if (show) {
1912- if (parent == null) {
1913- quicklist.child_add_position (menuitem, -1);
1914+ if (this.qldaemon == null) {
1915+ return;
1916+ }
1917+
1918+ /* Active_infos has already been decremented when info is finished or cancelled,
1919+ * or incremented when info has been added */
1920+ try {
1921+ if (qldaemon.build_unity_quicklist ()) {
1922+ qldaemon.update_unity_launcher_entries (this.active_infos > 0,
1923+ info.get_cancellable ().is_cancelled ());
1924+ if (active_infos > 0) {
1925+ update_overall_progress ();
1926 }
1927- } else if (parent != null && parent == quicklist) {
1928- quicklist.child_delete (menuitem);
1929 }
1930+ } catch (IOError e) {
1931+ warning ("ProgressUIHandler unable to create quicklist - %s", e.message);
1932+ }
1933+
1934+ if (added) {
1935+ info.progress_changed.connect (update_overall_progress);
1936 }
1937 }
1938-
1939- private void unity_progress_changed () {
1940+#endif
1941+
1942+ private void update_overall_progress () {
1943+ /* Prevent re-entry */
1944+ if (updating_overall_progress) {
1945+ return;
1946+ }
1947+ updating_overall_progress = true;
1948+
1949 double progress = 0;
1950 double current = 0;
1951 double total = 0;
1952@@ -319,11 +248,20 @@
1953 if (progress > 1.0)
1954 progress = 1.0;
1955
1956- foreach (Marlin.LauncherEntry marlin_lentry in this.quicklist_handler.launcher_entries) {
1957- Unity.LauncherEntry unity_lentry = marlin_lentry.entry;
1958- unity_lentry.progress = progress;
1959+ overall_progress_changed (progress);
1960+
1961+ /* Unity launcher shows a single progress bar which shows the overall progress of all the
1962+ * active file operations.
1963+ */
1964+
1965+#if HAVE_UNITY
1966+ try {
1967+ qldaemon.progress_changed (progress);
1968+ } catch (GLib.IOError e) {
1969+ warning ("ProgressUIHandler unable to update Unity Launcher progress - %s", e.message);
1970 }
1971+#endif
1972+
1973+ updating_overall_progress = false;
1974 }
1975-#endif
1976-
1977 }
1978
1979=== modified file 'src/View/Slot.vala'
1980--- src/View/Slot.vala 2016-09-21 18:27:17 +0000
1981+++ src/View/Slot.vala 2017-01-14 12:39:17 +0000
1982@@ -426,7 +426,7 @@
1983 }
1984
1985 if (dir_view != null) {
1986- dir_view.close ();
1987+ dir_view.close (); /* stop signal handlers running during destruction */
1988 disconnect_dir_view_signals ();
1989 }
1990 }
1991
1992=== added file 'src/View/Widgets/ProgressIndicator.vala'
1993--- src/View/Widgets/ProgressIndicator.vala 1970-01-01 00:00:00 +0000
1994+++ src/View/Widgets/ProgressIndicator.vala 2017-01-14 12:39:17 +0000
1995@@ -0,0 +1,105 @@
1996+/***
1997+ Copyright (C) 2015 ELementary Developers
1998+
1999+ This program is free software: you can redistribute it and/or modify it
2000+ under the terms of the GNU Lesser General Public License version 3, as published
2001+ by the Free Software Foundation.
2002+
2003+ This program is distributed in the hope that it will be useful, but
2004+ WITHOUT ANY WARRANTY; without even the implied warranties of
2005+ MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2006+ PURPOSE. See the GNU General Public License for more details.
2007+
2008+ You should have received a copy of the GNU General Public License along
2009+ with this program. If not, see <http://www.gnu.org/licenses/>.
2010+
2011+ Authors : Jeremy Wootten <jeremy@elementaryos.org>
2012+***/
2013+
2014+
2015+namespace Marlin.View.Chrome {
2016+ public class ProgressIndicator : Gtk.DrawingArea {
2017+ double fraction = 0.0;
2018+ double hole_fraction = 0.0;
2019+
2020+ public ProgressIndicator (int height) {
2021+ set_size_request (height ,height);
2022+
2023+ draw.connect (on_draw);
2024+ show ();
2025+ }
2026+
2027+ public void set_fraction (double f) {
2028+ fraction = f.clamp (0.0, 1.0);
2029+ queue_draw ();
2030+ }
2031+
2032+ public void set_hole_fraction (double f) {
2033+ hole_fraction = f.clamp (0.0, 1.0);
2034+ }
2035+
2036+ public bool on_draw (Cairo.Context context) {
2037+ int height = get_allocated_height ();
2038+ int width = get_allocated_width ();
2039+
2040+ double xc = width / 2.0;
2041+ double yc = height / 2.0;
2042+
2043+ double outer_radius = yc - 1.0;
2044+
2045+ draw_doughnut (context, xc, yc, outer_radius);
2046+
2047+ return true;
2048+ }
2049+
2050+ private void draw_doughnut (Cairo.Context cr, double center_x, double center_y, double outer_radius) {
2051+
2052+ double hole_radius = outer_radius * hole_fraction;
2053+ double thickness = (outer_radius - hole_radius);
2054+ double middle_radius = hole_radius + thickness / 2.0; // middle radius
2055+
2056+ double angle1 = - Math.PI / 2.0; // 12 o'clock
2057+ double angle2 = angle1 + 2*Math.PI; // full circle
2058+ double angle3 = angle1 + 2 * Math.PI * fraction; // progress angle
2059+
2060+ Gdk.RGBA color = {};
2061+
2062+ cr.save ();
2063+
2064+ Cairo.Pattern pat;
2065+ /* Draw trough */
2066+ color = {0.96,0.96,0.96,1.0}; /* TODO Get color, gradient etc from gtk.css */
2067+ var shade1 = 0.9;
2068+ var shade2 = 1.0;
2069+ pat = new Cairo.Pattern.linear (0.0, 0.0, 0.0, 2 * center_y);
2070+ pat.add_color_stop_rgb (0.0, color.red * shade1, color.green * shade1, color.blue * shade1);
2071+ pat.add_color_stop_rgb (1.0, color.red * shade2, color.green * shade2, color.blue * shade2);
2072+ cr.set_source (pat);
2073+ cr.arc (center_x, center_y, middle_radius, angle1, angle2);
2074+ cr.set_line_width (thickness - 2.0);
2075+ cr.stroke ();
2076+
2077+
2078+ /* Draw progress */
2079+ color.parse ("#3d9bda"); /* TODO Get color, gradient etc from gtk.css */
2080+ shade1 = 1.3;
2081+ shade2 = 1.08;
2082+
2083+ pat = new Cairo.Pattern.linear (0.0, 0.0, 0.0, 2 * center_y);
2084+ pat.add_color_stop_rgb (0.0, 1.0, 1.0, 1.0);
2085+ pat.add_color_stop_rgb (0.1, color.red * shade1, color.green * shade1, color.blue * shade1);
2086+ pat.add_color_stop_rgb (1.0, color.red * shade2, color.green * shade2, color.blue * shade2);
2087+
2088+ cr.set_source (pat);
2089+ cr.arc (center_x, center_y, middle_radius, angle1, angle3);
2090+ cr.set_line_width (thickness);
2091+ cr.stroke ();
2092+
2093+ cr.restore ();
2094+
2095+ }
2096+ }
2097+
2098+
2099+
2100+}
2101
2102=== added file 'src/View/Widgets/ProgressIndicatorWithPopover.vala'
2103--- src/View/Widgets/ProgressIndicatorWithPopover.vala 1970-01-01 00:00:00 +0000
2104+++ src/View/Widgets/ProgressIndicatorWithPopover.vala 2017-01-14 12:39:17 +0000
2105@@ -0,0 +1,121 @@
2106+/***
2107+ Copyright (C) 2015 ELementary Developers
2108+
2109+ This program is free software: you can redistribute it and/or modify it
2110+ under the terms of the GNU Lesser General Public License version 3, as published
2111+ by the Free Software Foundation.
2112+
2113+ This program is distributed in the hope that it will be useful, but
2114+ WITHOUT ANY WARRANTY; without even the implied warranties of
2115+ MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2116+ PURPOSE. See the GNU General Public License for more details.
2117+
2118+ You should have received a copy of the GNU General Public License along
2119+ with this program. If not, see <http://www.gnu.org/licenses/>.
2120+
2121+ Authors : Jeremy Wootten <jeremy@elementaryos.org>
2122+***/
2123+
2124+
2125+namespace Marlin.View.Chrome {
2126+
2127+ public class ProgressIndicatorWithPopover : Gtk.EventBox {
2128+ ProgressIndicator progress_indicator;
2129+ Gtk.Popover popover;
2130+ Gtk.ListBox content_box;
2131+ GLib.Mutex info_count_mutex;
2132+
2133+ private uint _info_count = 0;
2134+ public uint info_count {
2135+ get {
2136+ return _info_count;
2137+ }
2138+ private set {
2139+ _info_count = value;
2140+ if (_info_count > 0) {
2141+ string tip_format = ngettext (_("%u file operation in progress"), _("%u file operations in progress"), _info_count);
2142+ set_tooltip_text (tip_format.printf (_info_count));
2143+ } else {
2144+ set_tooltip_text (_("No operations in progress")); /* Should not be needed as widget hidden */
2145+ }
2146+ }
2147+ }
2148+
2149+ public ProgressIndicatorWithPopover () {
2150+ info_count_mutex = GLib.Mutex ();
2151+
2152+ progress_indicator = new ProgressIndicator (28);
2153+ progress_indicator.set_valign (Gtk.Align.CENTER);
2154+ progress_indicator.set_hole_fraction (0.4);
2155+
2156+ add (progress_indicator);
2157+
2158+ popover = new Gtk.Popover (progress_indicator);
2159+ popover.set_position (Gtk.PositionType.BOTTOM);
2160+
2161+ content_box = new Gtk.ListBox ();
2162+ content_box.margin_start = 6;
2163+ content_box.margin_end = 6;
2164+ content_box.margin_top = 6;
2165+ content_box.margin_bottom = 6;
2166+ popover.add (content_box);
2167+
2168+ connect_events ();
2169+
2170+ set_has_tooltip (true);
2171+ info_count = 0;
2172+ }
2173+
2174+ private void connect_events () {
2175+ set_events (Gdk.EventMask.BUTTON_PRESS_MASK);
2176+ button_press_event.connect (on_button_press_event);
2177+ }
2178+
2179+ private bool on_button_press_event () {
2180+ if (info_count > 0) {
2181+ popover.show_all ();
2182+ }
2183+ return true;
2184+ }
2185+
2186+ public void set_overall_progress (double progress) {
2187+ progress_indicator.set_fraction (progress);
2188+ }
2189+
2190+ public void add_progress_info (Marlin.Progress.Info info) {
2191+ var lbr = new Marlin.Progress.InfoListRow (info);
2192+ content_box.insert (lbr, -1);
2193+
2194+ info_count_mutex.lock ();
2195+ info_count++;
2196+ info_count_mutex.unlock ();
2197+
2198+ info.finished.connect (() => {
2199+ content_box.remove (lbr);
2200+ lbr.destroy ();
2201+ info_count_mutex.lock ();
2202+ info_count--;
2203+ info_count_mutex.unlock ();
2204+ });
2205+ }
2206+
2207+ public void close () {
2208+ popover.hide ();
2209+ this.hide ();
2210+ }
2211+
2212+ public void hide_popover () {
2213+ popover.hide ();
2214+ }
2215+
2216+ public void open () {
2217+ assert (info_count > 0);
2218+ popover.hide ();
2219+ this.set_visible (true);
2220+ }
2221+
2222+ public void reset () {
2223+ progress_indicator.set_fraction (0.0);
2224+ }
2225+ }
2226+}
2227
2228=== added file 'src/View/Widgets/ProgressInfoListRow.vala'
2229--- src/View/Widgets/ProgressInfoListRow.vala 1970-01-01 00:00:00 +0000
2230+++ src/View/Widgets/ProgressInfoListRow.vala 2017-01-14 12:39:17 +0000
2231@@ -0,0 +1,238 @@
2232+/***
2233+ Copyright (c) 2007, 2011 Red Hat, Inc.
2234+ Copyright (c) 2013-2017 elementary LLC (http://launchpad.net/elementary)
2235+
2236+ This program is free software: you can redistribute it and/or modify it
2237+ under the terms of the GNU Lesser General Public License version 3, as published
2238+ by the Free Software Foundation.
2239+
2240+ This program is distributed in the hope that it will be useful, but
2241+ WITHOUT ANY WARRANTY; without even the implied warranties of
2242+ MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2243+ PURPOSE. See the GNU General Public License for more details.
2244+
2245+ You should have received a copy of the GNU General Public License along
2246+ with this program. If not, see <http://www.gnu.org/licenses/>.
2247+
2248+ Authors: Alexander Larsson <alexl@redhat.com>
2249+ Cosimo Cecchi <cosimoc@redhat.com>
2250+ Julián Unrrein <junrrein@gmail.com>
2251+***/
2252+public class Marlin.Progress.InfoListRow : Gtk.ListBoxRow {
2253+
2254+ private Marlin.Progress.Info info;
2255+
2256+ Gtk.Grid grid;
2257+ Gtk.Label status;
2258+ Gtk.Label details;
2259+ Gtk.ProgressBar progress_bar;
2260+ Gtk.Image progress_icon = null;
2261+ Gtk.Expander expander;
2262+ Gtk.TextView log;
2263+
2264+ private int previous_remaining_file_count;
2265+ private int remaining_file_count;
2266+ private int total_file_count;
2267+ private Marlin.FileOperationType operation = Marlin.FileOperationType.INVALID;
2268+ private string dest_dir = "";
2269+ private bool only_one_file = true;
2270+
2271+ public InfoListRow (Marlin.Progress.Info info) {
2272+ this.info = info;
2273+ this.set_selectable (false);
2274+ this.set_activatable (false);
2275+
2276+ operation = info.get_optype ();
2277+ dest_dir = PF.FileUtils.sanitize_path (info.get_destination_uri ());
2278+
2279+ total_file_count = info.get_total_file_count ();
2280+ only_one_file = total_file_count == 1;
2281+
2282+ remaining_file_count = total_file_count;
2283+ previous_remaining_file_count = total_file_count; /* Ensure first line of details output */
2284+
2285+ build_and_show_widget ();
2286+
2287+ update_data ();
2288+ update_progress ();
2289+
2290+ info.changed.connect (update_data);
2291+ info.progress_changed.connect (update_progress);
2292+ }
2293+
2294+ private void build_and_show_widget () {
2295+ grid = new Gtk.Grid ();
2296+ grid.set_column_spacing (6);
2297+ grid.set_row_spacing (6);
2298+ grid.set_column_homogeneous (false);
2299+ grid.set_row_homogeneous (false);
2300+ grid.margin_start = 6;
2301+ grid.margin_end = 6;
2302+ grid.margin_top = 6;
2303+ grid.margin_bottom = 6;
2304+
2305+ var content_type = info.get_ctype ();
2306+ progress_icon = new Gtk.Image.from_gicon (GLib.ContentType.get_icon ("text/plain"), Gtk.IconSize.DIALOG);
2307+ if (content_type != null && content_type.length > 0) {
2308+ progress_icon = new Gtk.Image.from_gicon (GLib.ContentType.get_icon (content_type), Gtk.IconSize.DIALOG);
2309+ }
2310+ progress_icon.expand = true;
2311+ grid.attach (progress_icon, 0, 0, 1, 3);
2312+
2313+ status = new Gtk.Label ("status");
2314+ (status as Gtk.Label).set_line_wrap (true);
2315+ (status as Gtk.Label).set_line_wrap_mode (Pango.WrapMode.WORD_CHAR);
2316+ (status as Gtk.Misc).set_alignment ((float) 0.0, (float) 0.5);
2317+ grid.attach (status, 1, 0, 2, 1);
2318+
2319+ progress_bar = new Gtk.ProgressBar ();
2320+ progress_bar.pulse_step = 0.05;
2321+ progress_bar.hexpand = true;
2322+ progress_bar.valign = Gtk.Align.CENTER;
2323+ progress_bar.set_size_request (700, -1);
2324+ grid.attach (progress_bar, 1, 1 , 1, 1);
2325+
2326+ var button = new Gtk.Button.from_icon_name ("process-stop", Gtk.IconSize.BUTTON);
2327+
2328+ /* Stop frame and dark background */
2329+ button.set_relief (Gtk.ReliefStyle.NONE);
2330+ button.set_can_focus (false);
2331+
2332+ button.valign = Gtk.Align.CENTER;
2333+ button.set_tooltip_text (_("Cancel this file operation"));
2334+ grid.attach (button, 3, 1, 1, 1);
2335+
2336+ button.clicked.connect (() => {
2337+ info.cancel ();
2338+ button.sensitive = false;
2339+ });
2340+
2341+ details = new Gtk.Label ("");
2342+ (details as Gtk.Label).set_line_wrap (true);
2343+ (details as Gtk.Misc).set_alignment ((float) 0.0, (float) 0.5);
2344+ grid.attach (details, 1, 2, 1, 1);
2345+
2346+ if (!only_one_file) {
2347+ expander = new Gtk.Expander ("<b>" + _("Details") + "</b>");
2348+ expander.set_use_markup (true);
2349+ expander.margin_top = 6;
2350+ expander.margin_bottom = 6;
2351+ log = new Gtk.TextView ();
2352+ var sw = new Gtk.ScrolledWindow (null, null);
2353+ sw.set_size_request (-1, int.min (96, total_file_count * 16 + 24));
2354+ sw.set_vexpand (false);
2355+ var exp_size = GLib.Value (typeof (int));
2356+ var exp_spacing = GLib.Value (typeof (int));
2357+ expander.style_get_property ("expander-size", ref exp_size);
2358+ expander.style_get_property ("expander-spacing", ref exp_spacing);
2359+ sw.margin_start = exp_size.get_int () + 2 * exp_spacing.get_int ();
2360+ sw.margin_bottom = 12;
2361+ sw.add (log);
2362+ expander.add (sw);
2363+ grid.attach (expander, 1,3, 1, 1);
2364+
2365+ hide.connect (() => {
2366+ expander.set_expanded (false);
2367+ });
2368+ }
2369+ add (grid);
2370+ show_all ();
2371+ }
2372+
2373+ private void update_data () {
2374+ string? txt = info.get_status ();
2375+ (status as Gtk.Label).set_text (txt);
2376+
2377+ txt = info.get_details ();
2378+ string markup = Markup.printf_escaped ("<span size='small'>%s</span>", txt);
2379+ (details as Gtk.Label).set_markup (markup);
2380+
2381+ if (only_one_file) {
2382+ return;
2383+ }
2384+
2385+ remaining_file_count = info.get_remaining_file_count ();
2386+ int diff = previous_remaining_file_count - remaining_file_count;
2387+ if (diff != 0) {
2388+ var buf = log.get_buffer ();
2389+ if (previous_remaining_file_count < total_file_count) { /* There was a previous line - complete it */
2390+ /* TRANSLATORS: This is to indicate the completion of a file operation detailed on the same line */
2391+ buf.insert_at_cursor (_(".... done") + "\n", -1);
2392+ }
2393+
2394+ buf.insert_at_cursor (get_doing_string (operation, dest_dir, diff - 1) + "\n", -1);
2395+
2396+ Gtk.TextMark mark = buf.get_insert ();
2397+ log.scroll_to_mark (mark, 0.0, true, 0.0, 0.0);
2398+ previous_remaining_file_count = remaining_file_count;
2399+ }
2400+ }
2401+
2402+ private void update_progress () {
2403+ double progress = info.get_progress ();
2404+
2405+ if (progress < 0)
2406+ (progress_bar as Gtk.ProgressBar).pulse ();
2407+ else
2408+ (progress_bar as Gtk.ProgressBar).set_fraction (progress);
2409+ }
2410+
2411+ private string get_doing_string (Marlin.FileOperationType optype, string dest, uint others) {
2412+ var filename = info.get_current_filename ();
2413+ if (others > 0) {
2414+ return get_doing_several_string (optype, filename, dest, others);
2415+ }
2416+
2417+ switch (optype) {
2418+ case Marlin.FileOperationType.COPY:
2419+ /* TRANSLATORS: the first %s will be replaced by a filename, the second by destination path */
2420+ return _("Copying %s to %s").printf (filename, dest);
2421+ case Marlin.FileOperationType.MOVE:
2422+ /* TRANSLATORS: the first %s will be replaced by a filename, the second by destination path */
2423+ return _("Moving %s to %s").printf (filename, dest);
2424+ case Marlin.FileOperationType.TRASH:
2425+ /* TRANSLATORS: the first %s will be replaced by a filename, the second by destination path */
2426+ return _("Moving %s to trash bin").printf (filename);
2427+ case Marlin.FileOperationType.DELETE:
2428+ /* TRANSLATORS: the first %s will be replaced by a filename, the second by destination path */
2429+ return _("Permanently deleting %s").printf (filename);
2430+ case Marlin.FileOperationType.LINK:
2431+ /* TRANSLATORS: the first %s will be replaced by a filename, the second by destination path */
2432+ return _("Linking %s to %s").printf (filename, dest);
2433+ default:
2434+ break;
2435+ }
2436+
2437+ /* TRANSLATORS: %s will be replaced by a filename and should not be translated */
2438+ return _("Operating on %s").printf (filename); /* Should not be reached */
2439+ }
2440+ private string get_doing_several_string (Marlin.FileOperationType optype, string filename, string dest, uint others) {
2441+ switch (optype) {
2442+ case Marlin.FileOperationType.COPY:
2443+ /* TRANSLATORS: the first %s will be replaced by a filename, the second by destination path.
2444+ %i will be replaced by a number */
2445+ return _("Copying %s and %i more to %s").printf (filename, others, dest);
2446+ case Marlin.FileOperationType.MOVE:
2447+ /* TRANSLATORS: the first %s will be replaced by a filename, the second by destination path.
2448+ %i will be replaced by a number */
2449+ return _("Moving %s and %i more to %s").printf (filename, others, dest);
2450+ case Marlin.FileOperationType.TRASH:
2451+ /* TRANSLATORS: the first %s will be replaced by a filename, the second by destination path.
2452+ %i will be replaced by a number */
2453+ return _("Moving %s and %i more to trash bin").printf (filename, others);
2454+ case Marlin.FileOperationType.DELETE:
2455+ /* TRANSLATORS: the first %s will be replaced by a filename, the second by destination path.
2456+ %i will be replaced by a number */
2457+ return _("Permanently deleting %s and %i more").printf (filename, others);
2458+ case Marlin.FileOperationType.LINK:
2459+ /* TRANSLATORS: the first %s will be replaced by a filename, the second by destination path.
2460+ %i will be replaced by a number */
2461+ return _("Linking %s and %i more to %s").printf (filename, others, dest);
2462+ default:
2463+ break;
2464+ }
2465+
2466+ /* TRANSLATORS: %s will be replaced by a filename*/
2467+ return _("Operating on %s and % more").printf (filename, others); /* Should not be reached */
2468+ }
2469+}
2470
2471=== removed file 'src/View/Widgets/ProgressInfoWidget.vala'
2472--- src/View/Widgets/ProgressInfoWidget.vala 2016-12-31 19:42:09 +0000
2473+++ src/View/Widgets/ProgressInfoWidget.vala 1970-01-01 00:00:00 +0000
2474@@ -1,102 +0,0 @@
2475-/***
2476- Copyright (c) 2007, 2011 Red Hat, Inc.
2477- Copyright (c) 2013-2017 elementary LLC (http://launchpad.net/elementary)
2478-
2479- This program is free software: you can redistribute it and/or modify it
2480- under the terms of the GNU Lesser General Public License version 3, as published
2481- by the Free Software Foundation.
2482-
2483- This program is distributed in the hope that it will be useful, but
2484- WITHOUT ANY WARRANTY; without even the implied warranties of
2485- MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2486- PURPOSE. See the GNU General Public License for more details.
2487-
2488- You should have received a copy of the GNU General Public License along
2489- with this program. If not, see <http://www.gnu.org/licenses/>.
2490-
2491- Authors: Alexander Larsson <alexl@redhat.com>
2492- Cosimo Cecchi <cosimoc@redhat.com>
2493- Julián Unrrein <junrrein@gmail.com>
2494-***/
2495-
2496-public class Marlin.Progress.InfoWidget : Gtk.Box {
2497-
2498- private Marlin.Progress.Info info;
2499-
2500- Gtk.Widget status; /* Gtk.Label */
2501- Gtk.Widget details; /* Gtk.Label */
2502- Gtk.Widget progress_bar;
2503-
2504- public InfoWidget (Marlin.Progress.Info info) {
2505- this.info = info;
2506-
2507- build_and_show_widget ();
2508-
2509- update_data ();
2510- update_progress ();
2511-
2512- this.info.changed.connect (update_data);
2513- this.info.progress_changed.connect (update_progress);
2514-
2515- this.info.finished.connect (() => {
2516- destroy ();
2517- });
2518- }
2519-
2520- private void build_and_show_widget () {
2521- this.orientation = Gtk.Orientation.VERTICAL;
2522- this.homogeneous = false;
2523- this.spacing = 5;
2524-
2525- this.status = new Gtk.Label ("status");
2526- (this.status as Gtk.Label).set_size_request (500, -1);
2527- (this.status as Gtk.Label).set_line_wrap (true);
2528- (this.status as Gtk.Label).set_line_wrap_mode (Pango.WrapMode.WORD_CHAR);
2529- (this.status as Gtk.Misc).set_alignment ((float) 0.0, (float) 0.5);
2530- pack_start (status, true, false, 0);
2531-
2532- var hbox = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 10);
2533- var box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
2534-
2535- this.progress_bar = new Gtk.ProgressBar ();
2536- (this.progress_bar as Gtk.ProgressBar).pulse_step = 0.05;
2537- box.pack_start (this.progress_bar, true, false, 0);
2538- hbox.pack_start (box, true, true, 0);
2539-
2540- var button = new Gtk.Button.from_icon_name ("process-stop-symbolic", Gtk.IconSize.BUTTON);
2541- button.get_style_context ().add_class ("flat");
2542- button.clicked.connect (() => {
2543- this.info.cancel ();
2544- button.sensitive = false;
2545- });
2546-
2547- hbox.pack_start (button, false, false, 0);
2548-
2549- pack_start (hbox, false, false, 0);
2550-
2551- this.details = new Gtk.Label ("details");
2552- (this.details as Gtk.Label).set_line_wrap (true);
2553- (this.details as Gtk.Misc).set_alignment ((float) 0.0, (float) 0.5);
2554- pack_start (details, true, false, 0);
2555-
2556- show_all ();
2557- }
2558-
2559- private void update_data () {
2560- string status = this.info.get_status ();
2561- (this.status as Gtk.Label).set_text (status);
2562-
2563- string details = this.info.get_details ();
2564- string markup = Markup.printf_escaped ("<span size='small'>%s</span>", details);
2565- (this.details as Gtk.Label).set_markup (markup);
2566- }
2567-
2568- private void update_progress () {
2569- double progress = this.info.get_progress ();
2570-
2571- if (progress < 0)
2572- (this.progress_bar as Gtk.ProgressBar).pulse ();
2573- else
2574- (this.progress_bar as Gtk.ProgressBar).set_fraction (progress);
2575- }
2576-}
2577
2578=== modified file 'src/View/Widgets/TopMenu.vala'
2579--- src/View/Widgets/TopMenu.vala 2017-01-13 13:31:06 +0000
2580+++ src/View/Widgets/TopMenu.vala 2017-01-14 12:39:17 +0000
2581@@ -28,6 +28,7 @@
2582 public LocationBar? location_bar;
2583 public Chrome.ButtonWithMenu button_forward;
2584 public Chrome.ButtonWithMenu button_back;
2585+ private ProgressIndicatorWithPopover progress_indicator;
2586
2587 public bool locked_focus {get; private set; default = false;}
2588
2589@@ -88,6 +89,9 @@
2590 location_bar.show_all ();
2591 pack_start (location_bar);
2592
2593+ progress_indicator = new ProgressIndicatorWithPopover ();
2594+ pack_start (progress_indicator);
2595+
2596 show ();
2597 }
2598
2599@@ -167,5 +171,21 @@
2600 public void cancel () {
2601 location_bar.cancel ();
2602 }
2603+
2604+ public void update_overall_progress (double progress) {
2605+ progress_indicator.set_overall_progress (progress);
2606+ }
2607+
2608+ public void show_progress (bool show) {
2609+ if (show) {
2610+ progress_indicator.open ();
2611+ } else {
2612+ progress_indicator.close ();
2613+ }
2614+ }
2615+
2616+ public void add_progress_info (Marlin.Progress.Info info) {
2617+ progress_indicator.add_progress_info (info);
2618+ }
2619 }
2620 }
2621
2622=== modified file 'src/View/Window.vala'
2623--- src/View/Window.vala 2017-01-13 13:31:06 +0000
2624+++ src/View/Window.vala 2017-01-14 12:39:17 +0000
2625@@ -1094,5 +1094,17 @@
2626 application.set_accels_for_action ("win.info::HELP", {"F1"});
2627 application.set_accels_for_action ("win.info::ABOUT", {"F3"});
2628 }
2629+
2630+ public void overall_progress_changed (double progress) {
2631+ top_menu.update_overall_progress (progress);
2632+ }
2633+
2634+ public void show_progress (bool show) {
2635+ top_menu.show_progress (show);
2636+ }
2637+
2638+ public void add_progress_info (Marlin.Progress.Info info) {
2639+ top_menu.add_progress_info (info);
2640+ }
2641 }
2642 }

Subscribers

People subscribed via source and target branches

to all changes: