Merge lp:~elementary-apps/pantheon-files/find-function-part2 into lp:~elementary-apps/pantheon-files/trunk
- find-function-part2
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Danielle Foré |
Approved revision: | 2426 |
Merged at revision: | 2582 |
Proposed branch: | lp:~elementary-apps/pantheon-files/find-function-part2 |
Merge into: | lp:~elementary-apps/pantheon-files/trunk |
Diff against target: |
516 lines (+203/-98) 1 file modified
libwidgets/View/SearchResults.vala (+203/-98) |
To merge this branch: | bzr merge lp:~elementary-apps/pantheon-files/find-function-part2 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Adam Bieńkowski (community) | code | Approve | |
Danielle Foré | Abstain | ||
Review via email: mp+323718@code.launchpad.net |
Commit message
* Add multi-level sorting to the search results
* Categories to distinguish files in the current folder, below the current folder, in the bookmarks and in zeitgeist
* Zeitgeist searching fixed
* Each category is counted separately so a large number of hits in one category does not hide all the results in another category
Description of the change
This branch adds multi-level sorting to the search results as well as introducing categories to distinguish files in the current folder, below the current folder, in the bookmarks and in zeitgeist. Within categories the results are sorted with those starting with the search term coming first and then sorting by name.
Zeitgeist searching is fixed so more matches are found.
Each category is counted separately so a large number of hits in one category does not hide all the results in another category.
Danielle Foré (danrabbit) wrote : | # |
there's a spot that refers to the headers but it looks like it assigns CURRENT_HEADER for everything. Seems unintentional?
Danielle Foré (danrabbit) : | # |
Jeremy Wootten (jeremywootten) wrote : | # |
For me "LOCAL" sounds like the opposite to "REMOTE", i.e. referring to results on the local, native file system - this is in line with usage in Gtk filechooser, recent, sidebar etc. "CURRENT" in this context means the folder currently being displayed. It could be changed to "PWD_HEADER" etc if you think that is clearer?
Jeremy Wootten (jeremywootten) wrote : | # |
Regarding the header sort keys - yes you are right - good spot! These should be changed to the appropriate Category.
Jeremy Wootten (jeremywootten) wrote : | # |
See inline responses
Danielle Foré (danrabbit) wrote : | # |
If there is already a convention for Files that CURRENT means present working directory, that's probably fine :)
Jeremy Wootten (jeremywootten) wrote : | # |
Made correction to headers.
Danielle Foré (danrabbit) wrote : | # |
While this branch renames things like "local_results" to "current_results", it doesn't rename things like "local_search" so you end up with some inconsistency there. I think I would like to see variable name changes in their own branch
Jeremy Wootten (jeremywootten) wrote : | # |
Removed last variable name change revision
- 2423. By Jeremy Wootten
-
Revert other variable name changes
Jeremy Wootten (jeremywootten) wrote : | # |
Reverted "current_results" to "local_results" and "zeitgeist_results" to "global_results", as requested.
Adam Bieńkowski (donadigo) wrote : | # |
I've tested this branch, and so far it looks good, although I'm not sure about this "sort_string". Could you explain how this works in detail? I left some comments in the diff.
- 2424. By Jeremy Wootten
-
Improve code style
Danielle Foré (danrabbit) wrote : | # |
I'm changing my review to abstain on this branch so that it doesn't appear that it still needs fixing on my account :)
Jeremy Wootten (jeremywootten) wrote : | # |
From Slack discussion re sort_string:
I need to have alphabetic sort of the filenames (within categories) so the whole key has to be alphabetic (I think?) How else to combine an integer order and an alphabetic order?
The only slight concern I have is whether it works in other locales.
Adam Bieńkowski (donadigo) wrote : | # |
Couple of additional diff comments. I'm still not sure about the hole Category.to_string (). Those comments are the last thing I want to see addressed in this branch. I won't be pointing anything out afterwards since this is the last branch to migrate Files to GitHub.
Jeremy Wootten (jeremywootten) wrote : | # |
Addressed code style issues identified and merged latest trunk.
- 2425. By Jeremy Wootten
-
Code style improvements
- 2426. By Jeremy Wootten
-
Merge trunk to r2581
Adam Bieńkowski (donadigo) wrote : | # |
Thanks, looks good to me now.
Preview Diff
1 | === modified file 'libwidgets/View/SearchResults.vala' | |||
2 | --- libwidgets/View/SearchResults.vala 2017-05-07 16:16:18 +0000 | |||
3 | +++ libwidgets/View/SearchResults.vala 2017-06-17 09:54:37 +0000 | |||
4 | @@ -20,6 +20,38 @@ | |||
5 | 20 | { | 20 | { |
6 | 21 | public class SearchResults : Gtk.Window, Searchable | 21 | public class SearchResults : Gtk.Window, Searchable |
7 | 22 | { | 22 | { |
8 | 23 | /* The order of these categories governs the order in which matches appear in the search view. | ||
9 | 24 | * The category represents a first level sort. Within a category the matches sort alphabetically on name */ | ||
10 | 25 | private enum Category { | ||
11 | 26 | CURRENT_HEADER, | ||
12 | 27 | CURRENT_BEGINS, | ||
13 | 28 | CURRENT_CONTAINS, | ||
14 | 29 | CURRENT_ELLIPSIS, | ||
15 | 30 | DEEP_HEADER, | ||
16 | 31 | DEEP_BEGINS, | ||
17 | 32 | DEEP_CONTAINS, | ||
18 | 33 | DEEP_ELLIPSIS, | ||
19 | 34 | ZEITGEIST_HEADER, | ||
20 | 35 | ZEITGEIST_BEGINS, | ||
21 | 36 | ZEITGEIST_CONTAINS, | ||
22 | 37 | ZEITGEIST_ELLIPSIS, | ||
23 | 38 | BOOKMARK_HEADER, | ||
24 | 39 | BOOKMARK_BEGINS, | ||
25 | 40 | BOOKMARK_CONTAINS, | ||
26 | 41 | BOOKMARK_ELLIPSIS; | ||
27 | 42 | |||
28 | 43 | /* This function converts a Category enum to a letter which can be prefixed to the match | ||
29 | 44 | * name to form a sort key. This ensures that the categories appear in the list in the | ||
30 | 45 | * desired order - that is within each class of results (current folder, deep search, | ||
31 | 46 | * zeitgeist search and bookmark search), after the header, the matches appear with the | ||
32 | 47 | * "begins with" ones first, then the "contains" and finally an "ellipsis" pseudo-match | ||
33 | 48 | * appears if MAX_RESULTS is exceeded for that category. | ||
34 | 49 | */ | ||
35 | 50 | public string to_string () { | ||
36 | 51 | return CharacterSet.A_2_Z.get_char ((uint)this).to_string (); | ||
37 | 52 | } | ||
38 | 53 | } | ||
39 | 54 | |||
40 | 23 | class Match : Object | 55 | class Match : Object |
41 | 24 | { | 56 | { |
42 | 25 | public string name { get; construct; } | 57 | public string name { get; construct; } |
43 | @@ -27,29 +59,34 @@ | |||
44 | 27 | public string path_string { get; construct; } | 59 | public string path_string { get; construct; } |
45 | 28 | public Icon icon { get; construct; } | 60 | public Icon icon { get; construct; } |
46 | 29 | public File? file { get; construct; } | 61 | public File? file { get; construct; } |
47 | 62 | public string sortkey { get; construct; } | ||
48 | 30 | 63 | ||
51 | 31 | public Match (FileInfo info, string path_string, File parent) { | 64 | public Match (FileInfo info, string path_string, File parent, SearchResults.Category category) { |
52 | 32 | Object (name: info.get_display_name (), | 65 | var _name = info.get_display_name (); |
53 | 66 | Object (name: Markup.escape_text (_name), | ||
54 | 33 | mime: info.get_content_type (), | 67 | mime: info.get_content_type (), |
55 | 34 | icon: info.get_icon (), | 68 | icon: info.get_icon (), |
56 | 35 | path_string: path_string, | 69 | path_string: path_string, |
58 | 36 | file: parent.resolve_relative_path (info.get_name ())); | 70 | file: parent.resolve_relative_path (info.get_name ()), |
59 | 71 | sortkey: category.to_string () + _name); | ||
60 | 37 | } | 72 | } |
61 | 38 | 73 | ||
64 | 39 | public Match.from_bookmark (Bookmark bookmark) { | 74 | public Match.from_bookmark (Bookmark bookmark, SearchResults.Category category) { |
65 | 40 | Object (name: bookmark.label, | 75 | Object (name: Markup.escape_text (bookmark.label), |
66 | 41 | mime: "inode/directory", | 76 | mime: "inode/directory", |
67 | 42 | icon: bookmark.get_icon (), | 77 | icon: bookmark.get_icon (), |
68 | 43 | path_string: "", | 78 | path_string: "", |
70 | 44 | file: bookmark.get_location ()); | 79 | file: bookmark.get_location (), |
71 | 80 | sortkey: category.to_string () + bookmark.label); | ||
72 | 45 | } | 81 | } |
73 | 46 | 82 | ||
75 | 47 | public Match.ellipsis () { | 83 | public Match.ellipsis (SearchResults.Category category) { |
76 | 48 | Object (name: "...", | 84 | Object (name: "...", |
77 | 49 | mime: "", | 85 | mime: "", |
78 | 50 | icon: null, | 86 | icon: null, |
79 | 51 | path_string: "", | 87 | path_string: "", |
81 | 52 | file: null); | 88 | file: null, |
82 | 89 | sortkey: category.to_string ()); | ||
83 | 53 | } | 90 | } |
84 | 54 | } | 91 | } |
85 | 55 | 92 | ||
86 | @@ -76,7 +113,8 @@ | |||
87 | 76 | Zeitgeist.Index zg_index; | 113 | Zeitgeist.Index zg_index; |
88 | 77 | GenericArray<Zeitgeist.Event> templates; | 114 | GenericArray<Zeitgeist.Event> templates; |
89 | 78 | 115 | ||
91 | 79 | int display_count; | 116 | int current_count; |
92 | 117 | int deep_count; | ||
93 | 80 | 118 | ||
94 | 81 | bool local_search_finished = false; | 119 | bool local_search_finished = false; |
95 | 82 | bool global_search_finished = false; | 120 | bool global_search_finished = false; |
96 | @@ -84,10 +122,11 @@ | |||
97 | 84 | bool is_grabbing = false; | 122 | bool is_grabbing = false; |
98 | 85 | Gdk.Device? device = null; | 123 | Gdk.Device? device = null; |
99 | 86 | 124 | ||
104 | 87 | Gtk.TreeIter local_results; | 125 | Gtk.TreeIter? local_results = null; |
105 | 88 | Gtk.TreeIter global_results; | 126 | Gtk.TreeIter? deep_results = null; |
106 | 89 | Gtk.TreeIter bookmark_results; | 127 | Gtk.TreeIter? zeitgeist_results = null; |
107 | 90 | Gtk.TreeIter no_results_label; | 128 | Gtk.TreeIter? bookmark_results = null; |
108 | 129 | |||
109 | 91 | Gtk.TreeView view; | 130 | Gtk.TreeView view; |
110 | 92 | Gtk.TreeStore list; | 131 | Gtk.TreeStore list; |
111 | 93 | Gtk.TreeModelFilter filter; | 132 | Gtk.TreeModelFilter filter; |
112 | @@ -154,19 +193,17 @@ | |||
113 | 154 | 193 | ||
114 | 155 | view.append_column (column); | 194 | view.append_column (column); |
115 | 156 | 195 | ||
117 | 157 | list = new Gtk.TreeStore (5, | 196 | list = new Gtk.TreeStore (6, |
118 | 158 | typeof (string), /*0 file basename or category name */ | 197 | typeof (string), /*0 file basename or category name */ |
119 | 159 | typeof (GLib.Icon), /*1 file icon */ | 198 | typeof (GLib.Icon), /*1 file icon */ |
123 | 160 | typeof (string), /*2 file location */ | 199 | typeof (string?), /*2 file location */ |
124 | 161 | typeof (File), /*3 file object */ | 200 | typeof (File?), /*3 file object */ |
125 | 162 | typeof (bool)); /*4 icon is visible */ | 201 | typeof (bool), /*4 icon is visible */ |
126 | 202 | typeof (string)); /*5 Sort key */ | ||
127 | 163 | 203 | ||
128 | 164 | filter = new Gtk.TreeModelFilter (list, null); | 204 | filter = new Gtk.TreeModelFilter (list, null); |
129 | 165 | 205 | ||
130 | 166 | filter.set_visible_func ((model, iter) => { | 206 | filter.set_visible_func ((model, iter) => { |
131 | 167 | if (iter == no_results_label) | ||
132 | 168 | return n_results < 1; | ||
133 | 169 | |||
134 | 170 | /* hide empty category headers */ | 207 | /* hide empty category headers */ |
135 | 171 | return list.iter_depth (iter) != 0 || list.iter_has_child (iter); | 208 | return list.iter_depth (iter) != 0 || list.iter_has_child (iter); |
136 | 172 | }); | 209 | }); |
137 | @@ -182,17 +219,27 @@ | |||
138 | 182 | } | 219 | } |
139 | 183 | }); | 220 | }); |
140 | 184 | 221 | ||
141 | 222 | list.set_sort_column_id (5, Gtk.SortType.ASCENDING); | ||
142 | 223 | |||
143 | 185 | list.append (out local_results, null); | 224 | list.append (out local_results, null); |
144 | 186 | list.@set (local_results, | 225 | list.@set (local_results, |
146 | 187 | 0, get_category_header (_("In This Folder"))); | 226 | 0, get_category_header (_("In This Folder")), |
147 | 227 | 5, Category.CURRENT_HEADER.to_string ()); | ||
148 | 228 | |||
149 | 229 | list.append (out deep_results, null); | ||
150 | 230 | list.@set (deep_results, | ||
151 | 231 | 0, get_category_header (_("Below This Folder")), | ||
152 | 232 | 5, Category.CURRENT_HEADER.to_string ()); | ||
153 | 188 | 233 | ||
154 | 189 | list.append (out bookmark_results, null); | 234 | list.append (out bookmark_results, null); |
155 | 190 | list.@set (bookmark_results, | 235 | list.@set (bookmark_results, |
157 | 191 | 0, get_category_header (_("Bookmarks"))); | 236 | 0, get_category_header (_("Bookmarks")), |
158 | 237 | 5, Category.CURRENT_HEADER.to_string ()); | ||
159 | 192 | 238 | ||
163 | 193 | list.append (out global_results, null); | 239 | list.append (out zeitgeist_results, null); |
164 | 194 | list.@set (global_results, | 240 | list.@set (zeitgeist_results, |
165 | 195 | 0, get_category_header (_("Everywhere Else"))); | 241 | 0, get_category_header (_("Recently used")), |
166 | 242 | 5, Category.CURRENT_HEADER.to_string ()); | ||
167 | 196 | 243 | ||
168 | 197 | scroll.add (view); | 244 | scroll.add (view); |
169 | 198 | frame.add (scroll); | 245 | frame.add (scroll); |
170 | @@ -255,8 +302,8 @@ | |||
171 | 255 | } | 302 | } |
172 | 256 | 303 | ||
173 | 257 | var include_hidden = GOF.Preferences.get_default ().show_hidden_files; | 304 | var include_hidden = GOF.Preferences.get_default ().show_hidden_files; |
176 | 258 | 305 | current_count = 0; | |
177 | 259 | display_count = 0; | 306 | deep_count = 0; |
178 | 260 | directory_queue = new Gee.LinkedList<File> (); | 307 | directory_queue = new Gee.LinkedList<File> (); |
179 | 261 | waiting_results = new Gee.HashMap<Gtk.TreeIter?,Gee.List> (); | 308 | waiting_results = new Gee.HashMap<Gtk.TreeIter?,Gee.List> (); |
180 | 262 | current_root = folder; | 309 | current_root = folder; |
181 | @@ -290,7 +337,7 @@ | |||
182 | 290 | new Thread<void*> (null, () => { | 337 | new Thread<void*> (null, () => { |
183 | 291 | local_search_finished = false; | 338 | local_search_finished = false; |
184 | 292 | while (!file_search_operation.is_cancelled () && directory_queue.size > 0) { | 339 | while (!file_search_operation.is_cancelled () && directory_queue.size > 0) { |
186 | 293 | visit (search_term, include_hidden, file_search_operation); | 340 | visit (search_term, include_hidden, file_search_operation, folder); |
187 | 294 | } | 341 | } |
188 | 295 | 342 | ||
189 | 296 | local_search_finished = true; | 343 | local_search_finished = true; |
190 | @@ -302,10 +349,10 @@ | |||
191 | 302 | get_zg_results.begin (search_term); | 349 | get_zg_results.begin (search_term); |
192 | 303 | 350 | ||
193 | 304 | var bookmarks_matched = new Gee.LinkedList<Match> (); | 351 | var bookmarks_matched = new Gee.LinkedList<Match> (); |
195 | 305 | 352 | var begins_with = false; | |
196 | 306 | foreach (var bookmark in BookmarkList.get_instance ().list) { | 353 | foreach (var bookmark in BookmarkList.get_instance ().list) { |
199 | 307 | if (term_matches (search_term, bookmark.label)) { | 354 | if (term_matches (search_term, bookmark.label, out begins_with)) { |
200 | 308 | bookmarks_matched.add (new Match.from_bookmark (bookmark)); | 355 | bookmarks_matched.add (new Match.from_bookmark (bookmark, begins_with ? Category.BOOKMARK_BEGINS : Category.BOOKMARK_CONTAINS)); |
201 | 309 | } | 356 | } |
202 | 310 | } | 357 | } |
203 | 311 | 358 | ||
204 | @@ -662,10 +709,10 @@ | |||
205 | 662 | } | 709 | } |
206 | 663 | 710 | ||
207 | 664 | foreach (var match in new_results) { | 711 | foreach (var match in new_results) { |
209 | 665 | Gtk.TreeIter iter; | 712 | Gtk.TreeIter? iter = null; |
210 | 666 | File file; | 713 | File file; |
213 | 667 | /* prevent results from showing in both global and local results */ | 714 | /* do not add global result if already in local results */ |
214 | 668 | if (parent == global_results) { | 715 | if (parent == zeitgeist_results) { |
215 | 669 | var already_added = false; | 716 | var already_added = false; |
216 | 670 | 717 | ||
217 | 671 | for (var valid = list.iter_nth_child (out iter, local_results, 0); valid; | 718 | for (var valid = list.iter_nth_child (out iter, local_results, 0); valid; |
218 | @@ -679,11 +726,25 @@ | |||
219 | 679 | } | 726 | } |
220 | 680 | } | 727 | } |
221 | 681 | 728 | ||
222 | 729 | if (!already_added) { | ||
223 | 730 | for (var valid = list.iter_nth_child (out iter, deep_results, 0); valid; | ||
224 | 731 | valid = list.iter_next (ref iter)) { | ||
225 | 732 | |||
226 | 733 | list.@get (iter, 3, out file); | ||
227 | 734 | |||
228 | 735 | if (file != null && match.file != null && file.equal (match.file)) { | ||
229 | 736 | already_added = true; | ||
230 | 737 | break; | ||
231 | 738 | } | ||
232 | 739 | } | ||
233 | 740 | } | ||
234 | 741 | |||
235 | 682 | if (already_added) { | 742 | if (already_added) { |
236 | 683 | continue; | 743 | continue; |
237 | 684 | } | 744 | } |
238 | 685 | } else if (parent == local_results) { | 745 | } else if (parent == local_results) { |
240 | 686 | for (var valid = list.iter_nth_child (out iter, global_results, 0); valid; | 746 | /* remove current search result from global if in global results */ |
241 | 747 | for (var valid = list.iter_nth_child (out iter, zeitgeist_results, 0); valid; | ||
242 | 687 | valid = list.iter_next (ref iter)) { | 748 | valid = list.iter_next (ref iter)) { |
243 | 688 | 749 | ||
244 | 689 | list.@get (iter, 3, out file); | 750 | list.@get (iter, 3, out file); |
245 | @@ -693,13 +754,25 @@ | |||
246 | 693 | break; | 754 | break; |
247 | 694 | } | 755 | } |
248 | 695 | } | 756 | } |
250 | 696 | } | 757 | } else if (parent == deep_results) { |
251 | 758 | /* remove deep search result from from global if in global results */ | ||
252 | 759 | for (var valid = list.iter_nth_child (out iter, zeitgeist_results, 0); valid; | ||
253 | 760 | valid = list.iter_next (ref iter)) { | ||
254 | 761 | |||
255 | 762 | list.@get (iter, 3, out file); | ||
256 | 763 | |||
257 | 764 | if (file != null && match.file != null && file.equal (match.file)) { | ||
258 | 765 | list.remove (ref iter); | ||
259 | 766 | break; | ||
260 | 767 | } | ||
261 | 768 | } | ||
262 | 769 | } | ||
263 | 697 | 770 | ||
264 | 698 | var location = "<span %s>%s</span>".printf (get_pango_grey_color_string (), | 771 | var location = "<span %s>%s</span>".printf (get_pango_grey_color_string (), |
265 | 699 | Markup.escape_text (match.path_string)); | 772 | Markup.escape_text (match.path_string)); |
266 | 700 | 773 | ||
267 | 701 | list.append (out iter, parent); | 774 | list.append (out iter, parent); |
269 | 702 | list.@set (iter, 0, Markup.escape_text (match.name), 1, match.icon, 2, location, 3, match.file, 4, true); | 775 | list.@set (iter, 0, match.name, 1, match.icon, 2, location, 3, match.file, 4, true, 5, match.sortkey); |
270 | 703 | n_results++; | 776 | n_results++; |
271 | 704 | 777 | ||
272 | 705 | view.expand_all (); | 778 | view.expand_all (); |
273 | @@ -823,21 +896,22 @@ | |||
274 | 823 | FileAttribute.STANDARD_TYPE + "," + | 896 | FileAttribute.STANDARD_TYPE + "," + |
275 | 824 | FileAttribute.STANDARD_ICON; | 897 | FileAttribute.STANDARD_ICON; |
276 | 825 | 898 | ||
280 | 826 | void visit (string term, bool include_hidden, Cancellable cancel) { | 899 | void visit (string term, bool include_hidden, Cancellable cancel, File root_folder) { |
278 | 827 | |||
279 | 828 | FileEnumerator enumerator; | ||
281 | 829 | var folder = directory_queue.poll (); | 900 | var folder = directory_queue.poll (); |
282 | 830 | 901 | ||
283 | 831 | if (folder == null) { | 902 | if (folder == null) { |
284 | 832 | return; | 903 | return; |
285 | 833 | } | 904 | } |
286 | 834 | 905 | ||
287 | 906 | bool in_root = folder.equal (root_folder); | ||
288 | 907 | var category_count = in_root ? current_count : deep_count; | ||
289 | 908 | |||
290 | 835 | var depth = 0; | 909 | var depth = 0; |
291 | 836 | 910 | ||
292 | 837 | File f = folder; | 911 | File f = folder; |
293 | 838 | var path_string = ""; | 912 | var path_string = ""; |
294 | 839 | 913 | ||
296 | 840 | while (!f.equal (current_root)) { | 914 | while (f != null && !f.equal (current_root)) { |
297 | 841 | path_string = f.get_basename () + (path_string == "" ? "" : Path.DIR_SEPARATOR_S + path_string); | 915 | path_string = f.get_basename () + (path_string == "" ? "" : Path.DIR_SEPARATOR_S + path_string); |
298 | 842 | f = f.get_parent (); | 916 | f = f.get_parent (); |
299 | 843 | depth++; | 917 | depth++; |
300 | @@ -847,6 +921,7 @@ | |||
301 | 847 | return; | 921 | return; |
302 | 848 | } | 922 | } |
303 | 849 | 923 | ||
304 | 924 | FileEnumerator enumerator; | ||
305 | 850 | try { | 925 | try { |
306 | 851 | enumerator = folder.enumerate_children (ATTRIBUTES, 0, cancel); | 926 | enumerator = folder.enumerate_children (ATTRIBUTES, 0, cancel); |
307 | 852 | } catch (Error e) { | 927 | } catch (Error e) { |
308 | @@ -856,10 +931,12 @@ | |||
309 | 856 | var new_results = new Gee.LinkedList<Match> (); | 931 | var new_results = new Gee.LinkedList<Match> (); |
310 | 857 | 932 | ||
311 | 858 | FileInfo info = null; | 933 | FileInfo info = null; |
312 | 934 | Category cat; | ||
313 | 859 | 935 | ||
314 | 860 | try { | 936 | try { |
315 | 861 | while (!cancel.is_cancelled () && | 937 | while (!cancel.is_cancelled () && |
317 | 862 | (info = enumerator.next_file (null)) != null) { | 938 | (info = enumerator.next_file (null)) != null && |
318 | 939 | category_count < MAX_RESULTS) { | ||
319 | 863 | 940 | ||
320 | 864 | if (info.get_is_hidden () && !include_hidden) { | 941 | if (info.get_is_hidden () && !include_hidden) { |
321 | 865 | continue; | 942 | continue; |
322 | @@ -869,43 +946,45 @@ | |||
323 | 869 | directory_queue.add (folder.resolve_relative_path (info.get_name ())); | 946 | directory_queue.add (folder.resolve_relative_path (info.get_name ())); |
324 | 870 | } | 947 | } |
325 | 871 | 948 | ||
329 | 872 | if (term_matches (term, info.get_display_name ())) { | 949 | bool begins_with; |
330 | 873 | new_results.add (new Match (info, path_string, folder)); | 950 | if (term_matches (term, info.get_display_name (), out begins_with)) { |
331 | 874 | } | 951 | if (in_root) { |
332 | 952 | cat = begins_with ? Category.CURRENT_BEGINS : Category.CURRENT_CONTAINS; | ||
333 | 953 | } else { | ||
334 | 954 | cat = begins_with ? Category.DEEP_BEGINS : Category.DEEP_CONTAINS; | ||
335 | 955 | } | ||
336 | 956 | new_results.add (new Match (info, path_string, folder, cat)); | ||
337 | 957 | category_count++; | ||
338 | 958 | } | ||
339 | 875 | } | 959 | } |
340 | 876 | } catch (Error e) {warning ("Error enumerating in visit");} | 960 | } catch (Error e) {warning ("Error enumerating in visit");} |
341 | 877 | 961 | ||
342 | 878 | if (new_results.size < 1) { | 962 | if (new_results.size < 1) { |
343 | 963 | cat = in_root ? Category.CURRENT_ELLIPSIS : Category.DEEP_ELLIPSIS; | ||
344 | 964 | new_results.add (new Match.ellipsis (cat)); | ||
345 | 879 | return; | 965 | return; |
369 | 880 | } | 966 | } else if (!cancel.is_cancelled ()) { |
370 | 881 | 967 | if (in_root) { | |
371 | 882 | if (!cancel.is_cancelled ()) { | 968 | current_count = category_count; |
372 | 883 | var new_count = display_count + new_results.size; | 969 | } else { |
373 | 884 | if (new_count > MAX_RESULTS) { | 970 | deep_count = category_count; |
374 | 885 | cancel.cancel (); | 971 | } |
352 | 886 | |||
353 | 887 | var num_ok = MAX_RESULTS - display_count; | ||
354 | 888 | if (num_ok < new_results.size) { | ||
355 | 889 | var count = 0; | ||
356 | 890 | var it = new_results.iterator (); | ||
357 | 891 | while (it.next ()) { | ||
358 | 892 | count++; | ||
359 | 893 | if (count > num_ok) | ||
360 | 894 | it.remove (); | ||
361 | 895 | } | ||
362 | 896 | } | ||
363 | 897 | |||
364 | 898 | new_results.add (new Match.ellipsis ()); | ||
365 | 899 | |||
366 | 900 | display_count = MAX_RESULTS; | ||
367 | 901 | } else | ||
368 | 902 | display_count = new_count; | ||
375 | 903 | 972 | ||
376 | 904 | /* use a closure here to get vala to pass the userdata that we actually want */ | 973 | /* use a closure here to get vala to pass the userdata that we actually want */ |
377 | 905 | Idle.add (() => { | 974 | Idle.add (() => { |
379 | 906 | add_results (new_results, local_results); | 975 | add_results (new_results, in_root ? local_results : deep_results); |
380 | 907 | return false; | 976 | return false; |
381 | 908 | }); | 977 | }); |
382 | 978 | |||
383 | 979 | if (category_count >= MAX_RESULTS) { | ||
384 | 980 | cat = in_root ? Category.CURRENT_ELLIPSIS : Category.DEEP_ELLIPSIS; | ||
385 | 981 | new_results.add (new Match.ellipsis (cat)); | ||
386 | 982 | return; | ||
387 | 983 | } | ||
388 | 984 | |||
389 | 985 | if (current_count >= MAX_RESULTS && deep_count >= MAX_RESULTS) { | ||
390 | 986 | cancel.cancel (); | ||
391 | 987 | } | ||
392 | 909 | } | 988 | } |
393 | 910 | } | 989 | } |
394 | 911 | 990 | ||
395 | @@ -915,11 +994,11 @@ | |||
396 | 915 | 994 | ||
397 | 916 | Zeitgeist.ResultSet results; | 995 | Zeitgeist.ResultSet results; |
398 | 917 | try { | 996 | try { |
400 | 918 | results = yield zg_index.search (term, | 997 | results = yield zg_index.search ("name:" + term + "*", |
401 | 919 | new Zeitgeist.TimeRange.anytime (), | 998 | new Zeitgeist.TimeRange.anytime (), |
402 | 920 | templates, | 999 | templates, |
403 | 921 | 0, /* offset */ | 1000 | 0, /* offset */ |
405 | 922 | MAX_RESULTS + 1, | 1001 | MAX_RESULTS * 3, |
406 | 923 | Zeitgeist.ResultType.MOST_POPULAR_SUBJECTS, | 1002 | Zeitgeist.ResultType.MOST_POPULAR_SUBJECTS, |
407 | 924 | current_operation); | 1003 | current_operation); |
408 | 925 | } catch (IOError.CANCELLED e) { | 1004 | } catch (IOError.CANCELLED e) { |
409 | @@ -935,54 +1014,80 @@ | |||
410 | 935 | 1014 | ||
411 | 936 | var matches = new Gee.LinkedList<Match> (); | 1015 | var matches = new Gee.LinkedList<Match> (); |
412 | 937 | var home = File.new_for_path (Environment.get_home_dir ()); | 1016 | var home = File.new_for_path (Environment.get_home_dir ()); |
413 | 1017 | Category cat; | ||
414 | 938 | var i = 0; | 1018 | var i = 0; |
416 | 939 | while (results.has_next () && !current_operation.is_cancelled ()) { | 1019 | |
417 | 1020 | while (results.has_next () && !current_operation.is_cancelled () && !global_search_finished) { | ||
418 | 940 | var result = results.next_value (); | 1021 | var result = results.next_value (); |
419 | 941 | foreach (var subject in result.subjects.data) { | 1022 | foreach (var subject in result.subjects.data) { |
420 | 942 | if (i == MAX_RESULTS) { | 1023 | if (i == MAX_RESULTS) { |
422 | 943 | matches.add (new Match.ellipsis ()); | 1024 | matches.add (new Match.ellipsis (Category.ZEITGEIST_ELLIPSIS)); |
423 | 1025 | global_search_finished = true; | ||
424 | 944 | break; | 1026 | break; |
425 | 945 | } | 1027 | } |
426 | 946 | 1028 | ||
427 | 947 | try { | 1029 | try { |
428 | 948 | var file = File.new_for_uri (subject.uri); | 1030 | var file = File.new_for_uri (subject.uri); |
450 | 949 | var path_string = ""; | 1031 | /* Zeitgeist search finds search term anywhere in path. We are only interested |
451 | 950 | var parent = file; | 1032 | * when the search term is in the basename */ |
452 | 951 | while ((parent = parent.get_parent ()) != null) { | 1033 | while (file != null && !file.get_basename ().contains (term)) { |
453 | 952 | if (parent.equal (current_root)) | 1034 | file = file.get_parent (); |
454 | 953 | break; | 1035 | } |
455 | 954 | 1036 | ||
456 | 955 | if (parent.equal (home)) { | 1037 | if (file != null) { |
457 | 956 | path_string = "~/" + path_string; | 1038 | var path_string = ""; |
458 | 957 | break; | 1039 | var parent = file; |
459 | 958 | } | 1040 | while ((parent = parent.get_parent ()) != null) { |
460 | 959 | 1041 | if (parent.equal (current_root)) { | |
461 | 960 | if (path_string == "") | 1042 | break; |
462 | 961 | path_string = parent.get_basename (); | 1043 | } |
463 | 962 | else | 1044 | |
464 | 963 | path_string = parent.get_basename () + Path.DIR_SEPARATOR_S + path_string; | 1045 | if (parent.equal (home)) { |
465 | 964 | } | 1046 | path_string = "~/" + path_string; |
466 | 965 | 1047 | break; | |
467 | 966 | var info = yield file.query_info_async (ATTRIBUTES, 0, Priority.DEFAULT, current_operation); | 1048 | } |
468 | 967 | matches.add (new Match (info, path_string, file.get_parent ())); | 1049 | |
469 | 968 | 1050 | if (path_string == "") { | |
470 | 969 | i++; | 1051 | path_string = parent.get_basename (); |
471 | 1052 | } else { | ||
472 | 1053 | path_string = Path.build_path (Path.DIR_SEPARATOR_S, parent.get_basename (), path_string); | ||
473 | 1054 | } | ||
474 | 1055 | } | ||
475 | 1056 | |||
476 | 1057 | /* Eliminate duplicate matches */ | ||
477 | 1058 | bool found = false; | ||
478 | 1059 | foreach (Match m in matches) { | ||
479 | 1060 | if (m.path_string == path_string) { | ||
480 | 1061 | found = true; | ||
481 | 1062 | break; | ||
482 | 1063 | } | ||
483 | 1064 | } | ||
484 | 1065 | |||
485 | 1066 | if (!found) { | ||
486 | 1067 | var info = yield file.query_info_async (ATTRIBUTES, 0, Priority.DEFAULT, current_operation); | ||
487 | 1068 | var name = info.get_display_name (); | ||
488 | 1069 | cat = name.has_prefix (term) ? Category.ZEITGEIST_BEGINS : Category.ZEITGEIST_CONTAINS; | ||
489 | 1070 | matches.add (new Match (info, path_string, file.get_parent (), cat)); | ||
490 | 1071 | i++; | ||
491 | 1072 | } | ||
492 | 1073 | } | ||
493 | 970 | } catch (Error e) {} | 1074 | } catch (Error e) {} |
494 | 971 | } | 1075 | } |
495 | 972 | } | 1076 | } |
496 | 973 | 1077 | ||
497 | 974 | if (!current_operation.is_cancelled ()) { | 1078 | if (!current_operation.is_cancelled ()) { |
499 | 975 | add_results (matches, global_results); | 1079 | add_results (matches, zeitgeist_results); |
500 | 976 | } | 1080 | } |
501 | 977 | 1081 | ||
502 | 978 | global_search_finished = true; | 1082 | global_search_finished = true; |
503 | 979 | Idle.add (send_search_finished); | 1083 | Idle.add (send_search_finished); |
504 | 980 | } | 1084 | } |
505 | 981 | 1085 | ||
508 | 982 | bool term_matches (string term, string name) { | 1086 | bool term_matches (string term, string name, out bool begins_with ) { |
507 | 983 | /**TODO** improve */ | ||
509 | 984 | /* term is assumed to be down */ | 1087 | /* term is assumed to be down */ |
511 | 985 | return name.normalize ().casefold ().contains (term); | 1088 | var n = name.normalize ().casefold (); |
512 | 1089 | begins_with = n.has_prefix (term); | ||
513 | 1090 | return n.contains (term); | ||
514 | 986 | } | 1091 | } |
515 | 987 | 1092 | ||
516 | 988 | string get_category_header (string title) | 1093 | string get_category_header (string title) |
I don't think I like the use of "current" here instead of "local". To me "current" implies time as in Gdk.CURRENT_TIME but what we really mean here isn't results we have currently but results for the pwd.