227
217
new Subject.full ("file:*",
228
218
"!"+NFO_PRESENTATION,
229
219
"", "", "", "", ""));
230
type_templates.insert ("documents", (event as GLib.Object).ref() as Event);
220
type_templates["documents"] = event;
232
222
/* Section.FOLDERS
233
223
* - we're using special ORIGIN queries here */
234
224
event = new Event.full("", ZG_USER_ACTIVITY, "",
235
225
new Subject.full ("file:*",
236
226
"", "", "", "", "", ""));
237
type_templates.insert ("folders", (event as GLib.Object).ref() as Event);
227
type_templates["folders"] = event;
239
229
/* Section.IMAGES */
240
230
event = new Event.full("", ZG_USER_ACTIVITY, "",
241
231
new Subject.full ("file:*",
242
232
NFO_IMAGE, "", "", "", "", ""));
243
type_templates.insert ("images", (event as GLib.Object).ref() as Event);
233
type_templates["images"] = event;
245
235
/* Section.AUDIO */
246
236
event = new Event.full("", ZG_USER_ACTIVITY, "",
247
237
new Subject.full ("file:*",
248
238
NFO_AUDIO, "", "", "", "", ""));
249
type_templates.insert ("audio", (event as GLib.Object).ref() as Event);
239
type_templates["audio"] = event;
251
241
/* Section.VIDEOS */
252
242
event = new Event.full("", ZG_USER_ACTIVITY, "",
253
243
new Subject.full ("file:*",
254
244
NFO_VIDEO, "", "", "", "", ""));
255
type_templates.insert ("videos", (event as GLib.Object).ref() as Event);
245
type_templates["videos"] = event;
257
247
/* Section.PRESENTATIONS
258
248
* FIXME: Zeitgeist logger needs to user finer granularity
284
274
"!"+NFO_PRESENTATION,
286
276
"", "", "", ""));
287
type_templates.insert ("other", (event as GLib.Object).ref() as Event);
290
private bool search_is_invalid (LensSearch? search)
292
/* This boolean expression is unfolded as we seem to get
293
* some null dereference if we join them in a big || expression */
296
else if (search.search_string == null)
277
type_templates["other"] = event;
280
private const string[] ALL_TYPES =
291
private GenericArray<Event> create_template (OptionsFilter? filter)
293
var templates = new GenericArray<Event> ();
295
if (filter == null || !filter.filtering)
297
/* Section.ALL_FILES */
298
templates.add (type_templates["all"]);
304
foreach (unowned string type_id in ALL_TYPES)
306
var option = filter.get_option (type_id);
307
if (option == null || !option.active) continue;
312
if (types.length == ALL_TYPES.length)
314
/* Section.ALL_FILES */
315
templates.add (type_templates["all"]);
319
foreach (unowned string type_id in types)
321
// we need to handle folders separately
322
if (type_id == "folders") continue;
324
templates.add (type_templates[type_id]);
330
private bool is_search_empty (LensSearch search)
332
if (search.search_string == null) return true;
299
return search.search_string.strip() == "";
334
return search.search_string.strip () == "";
302
private string prepare_search_string (LensSearch? search)
337
private string prepare_search_string (LensSearch search)
304
339
var s = search.search_string;
322
357
private async void update_global_search_async (LensSearch search,
323
358
Cancellable cancellable)
325
if (search_is_invalid (search))
327
yield update_global_without_search_async (search, cancellable);
360
var has_search = !is_search_empty (search);
361
var results_model = search.results_model;
332
364
* For global searches we collate all results under one category heading
333
365
* called Files & Folders
336
var results_model = search.results_model;
338
var search_string = prepare_search_string (search);
340
var templates = new PtrArray.sized(1);
341
templates.add (type_templates.lookup ("all"));
344
369
/* Get results ranked by recency */
345
var timer = new Timer ();
346
var results = yield index.search (search_string,
347
new Zeitgeist.TimeRange.anytime(),
370
var results = yield run_zg_query (search,
371
new Zeitgeist.TimeRange.anytime (),
372
ResultType.MOST_RECENT_SUBJECTS,
351
ResultType.MOST_RECENT_SUBJECTS,
355
debug ("Found %u/%u global results for search '%s' in %fms",
356
results.size (), results.estimated_matches (),
357
search_string, timer.elapsed()*1000);
360
377
results_model.clear ();
362
var checked_url = urls.check_url (search.search_string);
363
if (checked_url != null)
379
/* check if the thing typed isn't a url (like facebook.com) */
382
var checked_url = urls.check_url (search.search_string);
383
if (checked_url != null)
365
385
results_model.append (checked_url, urls.icon,
366
386
Categories.FILES_AND_FOLDERS,
367
387
"text/html", search.search_string,
368
388
checked_url, checked_url);
371
int64 min_size, max_size;
372
get_current_size_limits (out min_size, out max_size);
374
392
Unity.FilesLens.append_events_sorted (results, results_model,
375
min_size, max_size, false,
376
394
Categories.FILES_AND_FOLDERS);
378
396
/* Add downloads catagory if we don't have a search */
379
if (search_is_invalid (search))
397
if (has_search == false)
381
399
yield update_downloads_async (results_model, cancellable,
382
400
search.search_string,
394
412
private async void update_search_async (LensSearch search,
395
413
Cancellable cancellable)
397
if (search_is_invalid (search))
399
yield update_without_search_async (search, cancellable);
403
415
var results_model = search.results_model;
405
var search_string = prepare_search_string (search);
407
string type_id = get_current_type ();
409
bool origin_grouping = type_id == "folders";
410
var result_type = origin_grouping ? ResultType.MOST_RECENT_ORIGIN
411
: ResultType.MOST_RECENT_SUBJECTS;
413
/* Grab the pre-compiled template we're going to use */
414
var templates = new PtrArray.sized(1);
415
templates.add (type_templates.lookup (type_id));
416
var txn = new Dee.Transaction (results_model);
417
var has_search = !is_search_empty (search);
419
var filter = scope.get_filter ("type") as OptionsFilter;
421
var active_filters = get_current_types ();
422
bool only_folders = active_filters != null &&
423
active_filters.length == 1 && active_filters[0] == "folders";
418
428
/* Get results ranked by recency */
419
var timer = new Timer ();
420
var results = yield index.search (search_string,
421
get_current_timerange (),
429
debug ("Found %u/%u results for search '%s' in %fms",
430
results.size (), results.estimated_matches (),
431
search_string, timer.elapsed()*1000);
433
results_model.clear ();
435
var checked_url = urls.check_url (search.search_string);
436
if (checked_url != null)
429
ResultSet? results = null;
432
results = yield run_zg_query (search,
433
get_current_timerange (),
434
ResultType.MOST_RECENT_SUBJECTS,
442
/* check if the thing typed isn't a url (like facebook.com) */
445
var checked_url = urls.check_url (search.search_string);
446
if (checked_url != null)
438
results_model.append (checked_url, urls.icon, Categories.RECENT,
439
"text/html", search.search_string,
440
checked_url, checked_url);
448
txn.append (checked_url, urls.icon, Categories.RECENT,
449
"text/html", search.search_string,
450
checked_url, checked_url);
443
var bookmark_matches = bookmarks.prefix_search (search.search_string);
444
append_bookmarks (bookmark_matches, results_model, Categories.FOLDERS);
454
/* apply filters to results found by zeitgeist */
446
455
int64 min_size, max_size;
447
456
get_current_size_limits (out min_size, out max_size);
449
Unity.FilesLens.append_events_sorted (results, results_model,
460
Unity.FilesLens.append_events_sorted (results, txn,
453
yield update_downloads_async (results_model, cancellable,
465
/* get recently downloaded files */
466
yield update_downloads_async (txn, cancellable,
454
467
search.search_string);
469
/* commit if the origin query is taking too long, if we committed right
470
* away, we'd cause flicker */
471
if (txn.get_n_rows () > 0)
473
/* here be something magical */
474
timer_id = Timeout.add (200, () =>
476
if (!cancellable.is_cancelled () && !txn.is_committed ())
485
/* folders are last category we need, update the model directly */
486
if (filter == null ||
487
!filter.filtering || filter.get_option ("folders").active)
489
results = yield run_zg_query (search, get_current_timerange (),
490
ResultType.MOST_RECENT_ORIGIN,
491
null, 50, cancellable);
493
if (!txn.is_committed ()) txn.commit ();
495
/* add bookmarks first */
496
append_bookmarks (has_search ?
497
bookmarks.prefix_search (search.search_string) : bookmarks.list (),
501
Unity.FilesLens.append_events_sorted (results, results_model,
508
if (!txn.is_committed ()) txn.commit ();
456
511
} catch (IOError.CANCELLED ioe) {
458
513
} catch (GLib.Error e) {
514
/* if we weren't cancelled, commit the transaction */
459
515
warning ("Error performing global search '%s': %s",
460
516
search.search_string, e.message);
517
if (!txn.is_committed ()) txn.commit ();
519
if (timer_id != 0) Source.remove (timer_id);
464
private string get_current_type ()
523
private string[]? get_current_types ()
466
/* Get the current type to filter by */
467
var filter = scope.get_filter ("type") as RadioOptionFilter;
468
Unity.FilterOption? option = filter.get_active_option ();
469
return option == null ? "all" : option.id;
525
/* returns null if the filter is disabled / all options selected */
526
var filter = scope.get_filter ("type") as CheckOptionFilter;
528
if (filter == null || !filter.filtering) return null;
531
foreach (unowned string type_id in ALL_TYPES)
533
var option = filter.get_option (type_id);
534
if (option == null || !option.active) continue;
539
if (types.length == ALL_TYPES.length) return null;
472
544
private TimeRange get_current_timerange ()
559
private async void update_without_search_async (LensSearch search,
560
Cancellable cancellable)
562
var results_model = search.results_model;
564
string type_id = get_current_type ();
566
bool origin_grouping = type_id == "folders";
567
var result_type = origin_grouping ? ResultType.MOST_RECENT_ORIGIN
568
: ResultType.MOST_RECENT_SUBJECTS;
571
/* Grab the pre-compiled template we're going to use */
572
var templates = new PtrArray.sized(1);
573
templates.add (type_templates.lookup (type_id));
576
/* Get results ranked by recency */
577
var timer = new Timer ();
578
var results = yield log.find_events (get_current_timerange (),
580
Zeitgeist.StorageState.ANY,
586
debug ("Found %u/%u no search results in %fms",
587
results.size (), results.estimated_matches (),
588
timer.elapsed()*1000);
590
results_model.clear ();
592
if (type_id == "all" || type_id == "folders")
594
append_bookmarks (bookmarks.list (), results_model, Categories.FOLDERS);
597
int64 min_size, max_size;
598
get_current_size_limits (out min_size, out max_size);
600
Unity.FilesLens.append_events_sorted (results, results_model,
604
yield update_downloads_async (results_model, cancellable);
606
} catch (IOError.CANCELLED ioe) {
608
} catch (GLib.Error e) {
609
warning ("Error performing empty search: %s",
614
private async void update_global_without_search_async (LensSearch search,
615
Cancellable cancellable)
617
var results_model = search.results_model;
619
/* Grab the pre-compiled template we're going to use */
620
var templates = new PtrArray.sized(1);
621
templates.add (type_templates.lookup ("all"));
624
/* Get results ranked by recency */
625
var timer = new Timer ();
626
var results = yield log.find_events (new Zeitgeist.TimeRange.anytime(),
628
Zeitgeist.StorageState.ANY,
630
ResultType.MOST_RECENT_SUBJECTS,
634
debug ("Found %u/%u global results in %fms",
635
results.size (), results.estimated_matches (),
636
timer.elapsed()*1000);
638
results_model.clear ();
640
Unity.FilesLens.append_events_sorted (results, results_model,
641
int64.MIN, int64.MAX,
642
false, Categories.RECENT_FILES);
644
yield update_downloads_async (results_model, cancellable);
646
} catch (IOError.CANCELLED ioe) {
648
} catch (GLib.Error e) {
649
warning ("Error performing empty search: %s",
631
private async ResultSet run_zg_query (LensSearch search,
632
TimeRange time_range,
633
ResultType result_type,
634
OptionsFilter? filters,
636
Cancellable cancellable) throws Error
639
var timer = new Timer ();
640
var templates = create_template (filters);
642
/* Copy the templates to a PtrArray which libzg expects */
643
var ptr_arr = new PtrArray ();
644
for (int i = 0; i < templates.length; i++)
646
ptr_arr.add (templates[i]);
649
/* Get results ranked by recency */
650
if (is_search_empty (search))
652
results = yield log.find_events (time_range,
654
Zeitgeist.StorageState.ANY,
661
var search_string = prepare_search_string (search);
663
results = yield index.search (search_string,
672
debug ("Found %u/%u no search results in %fms",
673
results.size (), results.estimated_matches (),
674
timer.elapsed()*1000);
654
679
private void append_bookmarks (GLib.List<Bookmark> bookmarks,
763
788
int category_override = -1)
765
foreach (var ev in events)
790
foreach (var ev in events)
792
if (ev.num_subjects() > 0)
767
if (ev.num_subjects() > 0)
769
// FIXME: We only use the first subject...
770
Zeitgeist.Subject su = ev.get_subject(0);
778
uri = su.get_origin ();
780
mimetype = "inode/directory";
785
display_name = su.get_text ();
786
mimetype = su.get_mimetype ();
787
mimetype = su.get_mimetype () != null ?
788
su.get_mimetype () : "application/octet-stream";
790
if (uri == null) continue;
791
File file = File.new_for_uri (uri);
793
if (display_name == null || display_name == "")
795
display_name = Path.get_basename (file.get_parse_name ());
798
bool check_size = min_size > 0 || max_size < int64.MAX;
799
/* Don't check existence on non-native files as http:// and
800
* friends are *very* expensive to query */
801
if (file.is_native()) {
802
// hidden files should be ignored
804
FileInfo info = file.query_info (check_size ?
805
ATTR_SIZE_AND_HIDDEN : ATTR_HIDDEN, 0, null);
806
if (info.get_is_hidden())
809
(info.get_size () < min_size || info.get_size () > max_size))
813
} catch (GLib.Error e) {
814
// as error occurred file must be missing therefore ignoring it
818
string icon = Utils.get_icon_for_uri (uri, mimetype);
821
string comment = file.get_parse_name ();
794
// FIXME: We only use the first subject...
795
Zeitgeist.Subject su = ev.get_subject(0);
803
uri = su.get_origin ();
805
mimetype = "inode/directory";
810
display_name = su.get_text ();
811
mimetype = su.get_mimetype ();
812
mimetype = su.get_mimetype () != null ?
813
su.get_mimetype () : "application/octet-stream";
815
if (uri == null) continue;
816
File file = File.new_for_uri (uri);
818
if (display_name == null || display_name == "")
820
display_name = Path.get_basename (file.get_parse_name ());
823
bool check_size = min_size > 0 || max_size < int64.MAX;
824
/* Don't check existence on non-native files as http:// and
825
* friends are *very* expensive to query */
826
if (file.is_native()) {
827
// hidden files should be ignored
829
FileInfo info = file.query_info (check_size ?
830
ATTR_SIZE_AND_HIDDEN : ATTR_HIDDEN, 0, null);
831
if (info.get_is_hidden())
834
(info.get_size () < min_size || info.get_size () > max_size))
838
} catch (GLib.Error e) {
839
// as error occurred file must be missing therefore ignoring it
843
string icon = Utils.get_icon_for_uri (uri, mimetype);
846
string comment = file.get_parse_name ();
848
if (category_override >= 0)
849
category_id = category_override;
851
category_id = file.query_file_type (0, null) == FileType.DIRECTORY ?
852
Categories.FOLDERS : Categories.RECENT;
823
if (category_override >= 0)
824
category_id = category_override;
826
category_id = file.query_file_type (0, null) == FileType.DIRECTORY ?
827
Categories.FOLDERS : Categories.RECENT;
829
results.append (uri, icon, category_id, mimetype,
830
display_name, comment);
854
results.append (uri, icon, category_id, mimetype,
855
display_name, comment);
835
860
} /* namespace */