Merge lp:~jeremywootten/pantheon-files/fix-1176471-folder-properties into lp:~elementary-apps/pantheon-files/trunk

Proposed by Jeremy Wootten
Status: Merged
Approved by: Felipe Escoto
Approved revision: 2055
Merged at revision: 2104
Proposed branch: lp:~jeremywootten/pantheon-files/fix-1176471-folder-properties
Merge into: lp:~elementary-apps/pantheon-files/trunk
Diff against target: 465 lines (+215/-97)
1 file modified
src/View/PropertiesWindow.vala (+215/-97)
To merge this branch: bzr merge lp:~jeremywootten/pantheon-files/fix-1176471-folder-properties
Reviewer Review Type Date Requested Status
Felipe Escoto (community) Approve
Review via email: mp+290833@code.launchpad.net

Commit message

Show additional property information for folder selections (lp: 1176471)

Description of the change

This branch shows another information line when the selection consists only of folders. This line shows the how many subfolders and ordinary files were found on a deep count of the selected folder (s).

To post a comment you must log in.
Revision history for this message
Felipe Escoto (philip.scott) wrote :

Empty folders show

"Contains: "

review: Needs Fixing
2052. By Jeremy Wootten

Merge trunk to r2101

2053. By Jeremy Wootten

Deal better with empty folders

2054. By Jeremy Wootten

Hide 'Contains:' line for empty folders

Revision history for this message
Felipe Escoto (philip.scott) wrote :

I made some comments on code style, but now it's not appearing on my end for some reason :P

Revision history for this message
Jeremy Wootten (jeremywootten) :
2055. By Jeremy Wootten

Fix formatting; ensure contains label shows when appropriate

Revision history for this message
Jeremy Wootten (jeremywootten) wrote :

I have fixed the code format issues identified and also a previously unidentified bug causing the "Contains" line to sometimes be hidden inappropriately.

Revision history for this message
Felipe Escoto (philip.scott) wrote :

Perfect! Thank you :)

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/View/PropertiesWindow.vala'
2--- src/View/PropertiesWindow.vala 2016-02-28 13:08:42 +0000
3+++ src/View/PropertiesWindow.vala 2016-04-09 09:23:32 +0000
4@@ -254,6 +254,8 @@
5 private Gtk.Widget header_title;
6 private Gtk.Label type_label;
7 private Gtk.Label size_label;
8+ private Gtk.Label contains_label;
9+ private Gtk.Label contains_key_label;
10 private Gtk.Widget type_key_label;
11 private string ftype; /* common type */
12 private Gtk.Spinner spinner;
13@@ -265,21 +267,24 @@
14
15 private bool files_contain_a_directory;
16
17- private uint _folder_count;
18- private signal void folder_count_changed ();
19+ private uint _uncounted_folders = 0;
20+ private uint selected_folders = 0;
21+ private uint selected_files = 0;
22+ private signal void uncounted_folders_changed ();
23
24- private uint folder_count {
25+ private uint uncounted_folders {
26 get {
27- return _folder_count;
28+ return _uncounted_folders;
29 }
30
31 set {
32- _folder_count = value;
33- folder_count_changed ();
34+ _uncounted_folders = value;
35+ uncounted_folders_changed ();
36 }
37 }
38
39- private uint file_count;
40+ private uint folder_count = 0; /* Count of folders current NOT including top level (selected) folders (to match OverlayBar)*/
41+ private uint file_count; /* Count of files current including top level (selected) files other than folders */
42
43 public PropertiesWindow (GLib.List<GOF.File> _files, FM.AbstractDirectoryView _view, Gtk.Window parent) {
44 base (_("Properties"), parent);
45@@ -296,6 +301,12 @@
46
47 view = _view;
48
49+ /* Connect signal before creating any DeepCount directories */
50+ this.destroy.connect (() => {
51+ foreach (var dir in deep_count_directories)
52+ dir.cancel ();
53+ });
54+
55 /* The properties window may outlive the passed-in file object
56 lifetimes. The objects must be referenced as a precaution.
57
58@@ -327,9 +338,11 @@
59 critical ("Properties Window constructor called with invalid file data (2)");
60 return;
61 }
62+
63 var ftype = gof.get_ftype ();
64 if (ftype != null)
65 mimes.add (ftype);
66+
67 if (gof.is_directory)
68 files_contain_a_directory = true;
69 }
70@@ -380,25 +393,12 @@
71 add_section (stack, _("Preview"), PanelType.PREVIEW.to_string (), preview_box);
72 }
73
74- if (count == 1 && !view.is_in_recent ()) {
75- int start_offset= 0, end_offset = -1;
76-
77- Marlin.get_rename_region (goffile.info.get_name (), out start_offset, out end_offset, goffile.is_folder ());
78- (header_title as Gtk.Entry).select_region (start_offset, end_offset);
79- }
80-
81- if (folder_count == 0)
82- spinner.hide ();
83-
84- if (size_warning < 1)
85- size_warning_image.hide ();
86-
87- if (file_count > 1) {
88- type_key_label.hide ();
89- type_label.hide ();
90- }
91-
92 show_all ();
93+
94+ /* There is a race condition between reaching update_header_desc () or here first
95+ * so call update_widgets_state in both places.
96+ */
97+ update_widgets_state ();
98 present ();
99 }
100
101@@ -408,12 +408,6 @@
102 string header_desc_str;
103
104 header_desc_str = format_size ((int64) total_size);
105- if (ftype != null)
106- type_label.label = goffile.formated_type;
107- else {
108- type_key_label.hide ();
109- type_label.hide ();
110- }
111
112 if (size_warning > 0) {
113 string file_plural = _("file");
114@@ -424,6 +418,8 @@
115 }
116
117 size_label.label = header_desc_str;
118+ contains_label.label = get_contains_label (folder_count, file_count);
119+ update_widgets_state ();
120 }
121
122 private Mutex mutex;
123@@ -431,37 +427,47 @@
124
125 private void selection_size_update () {
126 total_size = 0;
127- deep_count_directories = null;
128+ uncounted_folders = 0;
129+ selected_folders = 0;
130+ selected_files = 0;
131 folder_count = 0;
132 file_count = 0;
133 size_warning = 0;
134 size_warning_image.hide ();
135
136+ deep_count_directories = null;
137+
138 foreach (GOF.File gof in files) {
139 if (gof.is_root_network_folder ()) {
140 size_label.label = _("unknown");
141 continue;
142 }
143 if (gof.is_directory) {
144- folder_count++;
145- var d = new Marlin.DeepCount (gof.location);
146+ mutex.lock ();
147+ uncounted_folders++; /* this gets decremented by DeepCount*/
148+ mutex.unlock ();
149+
150+ selected_folders++;
151+ var d = new Marlin.DeepCount (gof.location); /* Starts counting on creation */
152 deep_count_directories.prepend (d);
153+
154 d.finished.connect (() => {
155- mutex.lock ();
156- deep_count_directories.remove (d);
157- total_size += d.total_size;
158- size_warning = d.file_not_read;
159- update_header_desc ();
160- if (file_count + folder_count == size_warning)
161- size_label.label = _("unknown");
162-
163- folder_count--;
164- if (!size_label.visible)
165- size_label.show ();
166- mutex.unlock ();
167- });
168+ mutex.lock ();
169+ deep_count_directories.remove (d);
170+
171+ total_size += d.total_size;
172+ size_warning = d.file_not_read;
173+ if (file_count + uncounted_folders == size_warning)
174+ size_label.label = _("unknown");
175+
176+ folder_count += d.dirs_count;
177+ file_count += d.files_count;
178+ uncounted_folders--; /* triggers signal which updates description when reaches zero */
179+ mutex.unlock ();
180+ });
181+
182 } else {
183- file_count++;
184+ selected_files++;
185 }
186
187 mutex.lock ();
188@@ -469,24 +475,18 @@
189 mutex.unlock ();
190 }
191
192- if (file_count > 0)
193- update_header_desc ();
194-
195- if (folder_count > 0) {
196+ if (uncounted_folders > 0) {/* possible race condition - uncounted_folders could have been decremented? */
197 spinner.start ();
198-
199- folder_count_changed.connect (() => {
200- if (folder_count == 0) {
201+ uncounted_folders_changed.connect (() => {
202+ if (uncounted_folders == 0) {
203 spinner.hide ();
204 spinner.stop ();
205+ update_header_desc ();
206 }
207 });
208+ } else {
209+ update_header_desc ();
210 }
211-
212- this.destroy.connect (() => {
213- foreach (var dir in deep_count_directories)
214- dir.cancel ();
215- });
216 }
217
218 private void rename_file (GOF.File file, string new_name) {
219@@ -520,12 +520,36 @@
220 }
221
222 private void build_header_box (Gtk.Box content) {
223+ /* create some widgets first (may be hidden by selection_size_update ()) */
224 var file_pix = goffile.get_icon_pixbuf (48, false, GOF.FileIconFlags.NONE);
225 var file_img = new Gtk.Image.from_pixbuf (overlay_emblems (file_pix, goffile.emblems_list));
226
227+ spinner = new Gtk.Spinner ();
228+ spinner.set_hexpand (false);
229+ spinner.halign = Gtk.Align.START;
230+
231+ size_warning_image = new Gtk.Image.from_icon_name ("help-info-symbolic", Gtk.IconSize.MENU);
232+ size_warning_image.halign = Gtk.Align.START;
233+ size_warning_image.no_show_all = true;
234+ size_label = new Gtk.Label ("");
235+ size_label.set_hexpand (false);
236+
237+ type_label = new Gtk.Label ("");
238+ type_label.set_halign (Gtk.Align.START);
239+ type_key_label = new Gtk.Label (_("Type:"));
240+ type_key_label.halign = Gtk.Align.END;
241+
242+ contains_label = new Gtk.Label ("");
243+ contains_label.set_halign (Gtk.Align.START);
244+ contains_key_label = new Gtk.Label (_("Contains:"));
245+ contains_key_label.set_halign (Gtk.Align.END);
246+
247+ selection_size_update (); /* Start counting first to get number of selected files and folders */
248+
249+ /* Build header box */
250 if (count > 1 || (count == 1 && !goffile.is_writable ())) {
251 var label = new Gtk.Label ("");
252- label.set_markup ("<span>" + _("%u selected items").printf(count) + "</span>");
253+ label.set_markup ("<span>" + get_selected_label (selected_folders, selected_files) + "</span>");
254 label.set_halign (Gtk.Align.START);
255 header_title = label;
256 } else if (count == 1 && goffile.is_writable ()) {
257@@ -545,28 +569,6 @@
258 }
259
260 pack_header_box (file_img, header_title);
261-
262- /* The header box is ready, now let's build some widgets that are going
263- * to be updated by selection_size_update() while the rest of the UI is
264- * being built. */
265- type_label = new Gtk.Label ("");
266- type_label.set_halign (Gtk.Align.START);
267-
268- size_label = new Gtk.Label ("");
269- size_label.set_hexpand (false);
270-
271- type_key_label = new Gtk.Label (_("Type:"));
272- type_key_label.halign = Gtk.Align.END;
273-
274- spinner = new Gtk.Spinner ();
275- spinner.set_hexpand (false);
276- spinner.halign = Gtk.Align.START;
277-
278- size_warning_image = new Gtk.Image.from_icon_name ("help-info-symbolic", Gtk.IconSize.MENU);
279- size_warning_image.halign = Gtk.Align.START;
280- size_warning_image.no_show_all = true;
281-
282- selection_size_update ();
283 }
284
285 private string? get_common_ftype () {
286@@ -652,9 +654,23 @@
287 info.add (new Pair<string, string>(_("Deleted:"), deletion_date));
288 }
289 }
290+
291 ftype = get_common_ftype ();
292 if (ftype != null) {
293 info.add (new Pair<string, string>(_("MimeType:"), ftype));
294+ /* get image size in pixels using an asynchronous method to stop the interface blocking on
295+ * large images. */
296+ if ("image" in ftype) {
297+ string resolution_value = "";
298+ if (file.width > 0) { /* resolution has already been determined */
299+ resolution_value = goffile.width.to_string () +" × " + goffile.height.to_string () + " px";
300+ } else {
301+ resolution_value = _("loading ...");
302+ /* Async function will update info when resolution determined */
303+ get_resolution.begin (file, info);
304+ }
305+ info.add (new Pair<string, string> (resolution_key, resolution_value));
306+ }
307 } else {
308 /* show list of mimetypes only if we got a default application in common */
309 if (view.get_default_app () != null && !goffile.is_directory) {
310@@ -666,19 +682,7 @@
311 }
312 }
313
314- /* get image size in pixels using an asynchronous method to stop the interface blocking on
315- * large images. */
316- if ("image" in ftype) {
317- string resolution_value = "";
318- if (file.width > 0) { /* resolution has already been determined */
319- resolution_value = goffile.width.to_string () +" × " + goffile.height.to_string () + " px";
320- } else {
321- resolution_value = _("loading ...");
322- /* Async function will update info when resolution determined */
323- get_resolution.begin (file, info);
324- }
325- info.add (new Pair<string, string> (resolution_key, resolution_value));
326- }
327+
328
329 if (got_common_location ()) {
330 if (view.is_in_recent ()) {
331@@ -756,12 +760,13 @@
332 size_key_label.halign = Gtk.Align.END;
333
334 var size_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 4);
335+ size_box.pack_start (spinner, false, false);
336 size_box.pack_start (size_label, false, true);
337- size_box.pack_start (spinner, false, false);
338 size_box.pack_start (size_warning_image);
339
340 create_info_line (size_key_label, size_label, information, ref n, size_box);
341 create_info_line (type_key_label, type_label, information, ref n);
342+ create_info_line (contains_key_label, contains_label, information, ref n);
343
344 foreach (var pair in item_info) {
345 var value_label = new Gtk.Label (pair.value);
346@@ -1465,6 +1470,119 @@
347 }
348 return file_size;
349 }
350+
351+ private string get_contains_label (uint folders, uint files) {
352+ string txt = "";
353+ if (folders > 0) {
354+ if (folders > 1) {
355+ if (files > 0) {
356+ if (files > 1) {
357+ txt = _("%u subfolders and %u files").printf (folders, files);
358+ } else {
359+ txt = _("%u subfolders and %u file").printf (folders,files);
360+ }
361+ } else {
362+ txt = _("%u subfolders").printf (folders);
363+ }
364+ } else {
365+ if (files > 0) {
366+ if (files > 1) {
367+ txt = _("%u subfolder and %u files").printf (folders, files);
368+ } else {
369+ txt = _("%u subfolder and %u file").printf (folders, files);
370+ }
371+ } else {
372+ txt = _("%u folder").printf (folders);
373+ }
374+ }
375+ } else {
376+ if (files > 0) {
377+ if (files > 1) {
378+ txt = _("%u files").printf (files);
379+ } else {
380+ txt = _("%u file").printf (files);
381+ }
382+ }
383+ }
384+
385+ return txt;
386+ }
387+
388+ private string get_selected_label (uint folders, uint files) {
389+ string txt = "";
390+ uint total = folders + files;
391+
392+ if (folders > 0) {
393+ if (folders > 1) {
394+ if (files > 0) {
395+ if (files > 1) {
396+ txt = _("%u selected items (%u folders and %u files)").printf (total, folders, files);
397+ } else {
398+ txt = _("%u selected items (%u folders and %u file)").printf (total, folders, files);
399+ }
400+ } else {
401+ txt = _("%u folders").printf (folders);
402+ }
403+ } else {
404+ if (files > 0) {
405+ if (files > 1) {
406+ txt = _("%u selected items (%u folder and %u files)").printf (total, folders,files);
407+ } else {
408+ txt = _("%u selected items (%u folder and %u file)").printf (total, folders,files);
409+ }
410+ } else {
411+ txt = _("%u folder").printf (folders); /* displayed for background folder*/
412+ }
413+ }
414+ } else {
415+ if (files > 0) {
416+ if (files > 1) {
417+ txt = _("%u files").printf (files);
418+ } else {
419+ txt = _("%u file").printf (files); /* should not be displayed - entry instead */
420+ }
421+ }
422+ }
423+
424+ /* The selection should never be empty */
425+ return txt;
426+ }
427+
428+ /** Hide certain widgets under certain conditions **/
429+ private void update_widgets_state () {
430+ if (uncounted_folders == 0)
431+ spinner.hide ();
432+
433+ if (size_warning < 1)
434+ size_warning_image.hide ();
435+
436+ if (ftype != null) {
437+ type_label.label = goffile.formated_type;
438+ }
439+
440+ if (count > 1) {
441+ type_key_label.hide ();
442+ type_label.hide ();
443+ }
444+
445+ if ((header_title is Gtk.Entry) && !view.is_in_recent ()) {
446+ int start_offset= 0, end_offset = -1;
447+
448+ Marlin.get_rename_region (goffile.info.get_name (), out start_offset, out end_offset, goffile.is_folder ());
449+ (header_title as Gtk.Entry).select_region (start_offset, end_offset);
450+ }
451+
452+ /* Only show 'contains' label when only folders selected - otherwise could be ambiguous whether
453+ * the "contained files" counted are only in the subfolders or not.*/
454+ /* Only show 'contains' label when folders selected are not empty */
455+ if (count > selected_folders || contains_label.get_text ().length < 1) {
456+ contains_key_label.hide ();
457+ contains_label.hide ();
458+ } else { /* Make sure it shows otherwise (may have been hidden by previous call)*/
459+ contains_key_label.show ();
460+ contains_label.show ();
461+ }
462+ }
463 }
464
465 public class Marlin.View.VolumePropertiesWindow : Marlin.View.PropertiesWindowBase {

Subscribers

People subscribed via source and target branches

to all changes: