Merge lp:~jeremywootten/pantheon-files/fix-1176471-folder-properties into lp:~elementary-apps/pantheon-files/trunk
- fix-1176471-folder-properties
- Merge into 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 | ||||
Related bugs: |
|
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.
- 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 { |
Empty folders show
"Contains: "