~ps-jenkins/unity-lens-files/latestsnapshot-7.0daily13.05.07ubuntu.unity.experimental.certified-0ubuntu1

« back to all changes in this revision

Viewing changes to src/daemon.vala

  • Committer: Michal Hruby
  • Date: 2013-03-12 13:43:11 UTC
  • Revision ID: michal.mhr@gmail.com-20130312134311-e0cdn2bd241gxsm6
Make sure everything works with latest libunity

Show diffs side-by-side

added added

removed removed

Lines of Context:
37
37
    private Devices devices;
38
38
    private UrlChecker urls;
39
39
 
40
 
    private Unity.Lens lens;
41
 
    private Unity.Scope scope;
 
40
    private Unity.DeprecatedScope scope;
42
41
 
43
42
    /* For each section we have a set of Zeitgeist.Event templates that
44
43
     * we use to query Zeitgeist */
49
48
    private const string USE_LOCATE_KEY = "use-locate";
50
49
    public bool use_locate { get; set; default = true; }
51
50
 
52
 
    private Settings lens_settings;
 
51
    private Settings scope_settings;
 
52
    private Variant empty_asv;
53
53
 
54
54
    construct
55
55
    {
 
56
      empty_asv = new Variant.array (VariantType.VARDICT.element (), {});
56
57
      prepare_type_templates ();
57
58
 
58
59
      if (SCHEMA_NAME in Settings.list_schemas ())
59
60
      {
60
 
        lens_settings = new Settings (SCHEMA_NAME);
61
 
        lens_settings.bind (USE_LOCATE_KEY, this, USE_LOCATE_KEY,
 
61
        scope_settings = new Settings (SCHEMA_NAME);
 
62
        scope_settings.bind (USE_LOCATE_KEY, this, USE_LOCATE_KEY,
62
63
                            SettingsBindFlags.GET);
63
64
      }
64
65
 
65
 
      scope = new Unity.Scope ("/com/canonical/unity/scope/files");
 
66
      scope = new Unity.DeprecatedScope ("/com/canonical/unity/scope/files",
 
67
                                         "files-local.scope");
66
68
      scope.search_in_global = true;
67
 
      scope.provides_personal_content = true;
 
69
      scope.search_hint = _("Search Files & Folders");
68
70
      scope.activate_uri.connect (activate);
69
71
      scope.preview_uri.connect (preview);
70
72
 
71
 
      lens = new Unity.Lens("/com/canonical/unity/lens/files", "files");
72
 
      lens.search_in_global = true;
73
 
      lens.search_hint = _("Search Files & Folders");
74
 
      lens.visible = true;
75
73
      populate_categories ();
76
74
      populate_filters ();
77
 
      lens.add_local_scope (scope);
78
75
 
79
76
      /* Bring up Zeitgeist interfaces */
80
77
      log = new Zeitgeist.Log ();
103
100
        scope.queue_search_changed (SearchType.GLOBAL);
104
101
      });
105
102
 
106
 
      /* Listen for filter changes */
107
 
      scope.filters_changed.connect (() => {
108
 
        scope.queue_search_changed (SearchType.DEFAULT);
109
 
      });
110
 
 
111
 
      scope.generate_search_key.connect ((lens_search) => {
112
 
        return lens_search.search_string.strip ();
113
 
      });
114
 
 
115
 
      /* Listen for changes to the lens entry search */
116
 
      scope.search_changed.connect ((lens_search, search_type, cancellable) =>
 
103
      scope.generate_search_key.connect ((search) => {
 
104
        return search.search_string.strip ();
 
105
      });
 
106
 
 
107
      /* Listen for changes to the search */
 
108
      scope.search_changed.connect ((search, search_type, cancellable) =>
117
109
      {
118
 
        dispatch_search.begin (lens_search, search_type, cancellable);
 
110
        dispatch_search.begin (search, search_type, cancellable);
119
111
      });
120
112
 
121
 
      lens.export ();
 
113
      scope.export ();
122
114
    }
123
115
 
124
 
    private async void dispatch_search (LensSearch lens_search,
 
116
    private async void dispatch_search (DeprecatedScopeSearch scope_search,
125
117
                                        SearchType search_type,
126
 
                                        Cancellable cancellable)
 
118
                                        GLib.Cancellable cancellable)
127
119
    {
128
120
      if (search_type == SearchType.GLOBAL)
129
121
      {
130
 
        yield update_global_search_async (lens_search, cancellable);
 
122
        yield update_global_search_async (scope_search, cancellable);
131
123
      }
132
124
      else
133
125
      {
134
 
        yield update_search_async (lens_search, cancellable);
 
126
        yield update_search_async (scope_search, cancellable);
135
127
      }
136
128
 
137
129
      // make sure we don't forget to emit finished (if we didn't get cancelled)
138
130
      if (!cancellable.is_cancelled ())
139
131
      {
140
 
        if (lens_search.results_model.get_n_rows () == 0)
 
132
        if (scope_search.results_model.get_n_rows () == 0)
141
133
        {
142
 
          lens_search.set_reply_hint ("no-results-hint",
 
134
          scope_search.set_reply_hint ("no-results-hint",
143
135
            _("Sorry, there are no files or folders that match your search."));
144
136
        }
145
137
 
146
 
        lens_search.finished ();
147
138
      }
 
139
      scope_search.finished ();
148
140
    }
149
141
 
150
142
    private void populate_filters ()
151
143
    {
152
 
      var filters = new GLib.List<Unity.Filter> ();
 
144
      var filters = new Unity.FilterSet ();
153
145
 
154
146
      /* Last modified */
155
147
      {
159
151
        filter.add_option ("last-30-days", _("Last 30 days"));
160
152
        filter.add_option ("last-year", _("Last year"));
161
153
 
162
 
        filters.append (filter);
 
154
        filters.add (filter);
163
155
      }
164
156
 
165
157
      /* Type filter */
175
167
        filter.add_option ("presentations", _("Presentations"));
176
168
        filter.add_option ("other", _("Other"));
177
169
 
178
 
        filters.append (filter);
 
170
        filters.add (filter);
179
171
      }
180
172
 
181
173
      /* Size filter */
190
182
        filter.add_option ("1GB", _("1GB"));
191
183
        filter.add_option (">1GB", _(">1GB"));
192
184
 
193
 
        filters.append (filter);
 
185
        filters.add (filter);
194
186
      }
195
187
 
196
 
      lens.filters = filters;
 
188
      scope.filters = filters;
197
189
    }
198
190
 
199
191
    private void populate_categories ()
200
192
    {
201
 
      var categories = new GLib.List<Unity.Category> ();
 
193
      var categories = new Unity.CategorySet ();
202
194
      var icon_dir = File.new_for_path (ICON_PATH);
203
195
 
204
 
      var cat = new Unity.Category (_("Recent"),
205
 
                                    new FileIcon (icon_dir.get_child ("group-recent.svg")));
206
 
      categories.append (cat);
 
196
      var cat = new Unity.Category ("global", _("Files & Folders"),
 
197
                                    new FileIcon (icon_dir.get_child ("group-folders.svg")));
 
198
      categories.add (cat);
207
199
 
208
 
      cat = new Unity.Category (_("Recent Files"),
 
200
      cat = new Unity.Category ("recent", _("Recent"),
209
201
                                new FileIcon (icon_dir.get_child ("group-recent.svg")));
210
 
      categories.append (cat);
 
202
      categories.add (cat);
211
203
 
212
 
      cat =  new Unity.Category (_("Downloads"),
 
204
      cat =  new Unity.Category ("downloads", _("Downloads"),
213
205
                                 new FileIcon (icon_dir.get_child ("group-downloads.svg")));
214
 
      categories.append (cat);
215
 
 
216
 
      cat = new Unity.Category (_("Folders"),
217
 
                                new FileIcon (icon_dir.get_child ("group-folders.svg")));
218
 
      categories.append (cat);
219
 
 
220
 
      cat = new Unity.Category (_("Files & Folders"),
221
 
                                new FileIcon (icon_dir.get_child ("group-folders.svg")));
222
 
      categories.append (cat);
223
 
 
224
 
      lens.categories = categories;
 
206
      categories.add (cat);
 
207
 
 
208
      cat = new Unity.Category ("folders", _("Folders"),
 
209
                                new FileIcon (icon_dir.get_child ("group-folders.svg")));
 
210
      categories.add (cat);
 
211
 
 
212
      scope.categories = categories;
225
213
    }
226
214
 
227
215
    private void init_templates (HashTable<string, Event> templates,
374
362
      return templates;
375
363
    }
376
364
 
377
 
    private bool is_search_empty (LensSearch search)
 
365
    private bool is_search_empty (DeprecatedScopeSearch search)
378
366
    {
379
367
      if (search.search_string == null) return true;
380
368
 
381
369
      return search.search_string.strip () == "";
382
370
    }
383
371
 
384
 
    private string prepare_search_string (LensSearch search)
 
372
    private string prepare_search_string (DeprecatedScopeSearch search)
385
373
    {
386
374
      var s = search.search_string;
387
375
 
401
389
      return s;
402
390
    }
403
391
 
404
 
    private async void update_global_search_async (LensSearch search,
405
 
                                                   Cancellable cancellable)
 
392
    private async void update_global_search_async (DeprecatedScopeSearch search,
 
393
                                                   GLib.Cancellable cancellable)
406
394
    {
407
395
      var has_search = !is_search_empty (search);
408
396
      var results_model = search.results_model;
416
404
        /* Get results ranked by recency */
417
405
        var results = yield run_zg_query (search,
418
406
                                          new Zeitgeist.TimeRange.anytime (),
419
 
                                          ResultType.MOST_RECENT_CURRENT_URI,
 
407
                                          Zeitgeist.ResultType.MOST_RECENT_CURRENT_URI,
420
408
                                          null,
421
409
                                          20,
422
410
                                          cancellable);
431
419
        {
432
420
          results_model.append (checked_url, urls.get_icon_for_type(url_type),
433
421
                Categories.FILES_AND_FOLDERS,
 
422
                ResultType.PERSONAL,
434
423
                "text/html", search.search_string,
435
 
                checked_url, checked_url);
 
424
                checked_url, checked_url, empty_asv);
436
425
        }
437
426
 
438
 
        var category_id = has_search ?
439
 
          Categories.FILES_AND_FOLDERS : Categories.RECENT_FILES;
 
427
        var category_id = Categories.FILES_AND_FOLDERS;
440
428
 
441
429
        Set<string>? bookmark_uris = null;
442
430
        if (has_search)
456
444
        }
457
445
 
458
446
        var result_flags = has_search ? ResultFlags.EXTRACT_ORIGINS : 0;
459
 
        Unity.FilesLens.append_events_sorted (search.search_string,
460
 
                                              results, results_model,
461
 
                                              0, int64.MAX, result_flags,
462
 
                                              bookmark_uris, category_id);
 
447
        append_events_sorted (search.search_string,
 
448
                              results, results_model,
 
449
                              0, int64.MAX, result_flags,
 
450
                              bookmark_uris, category_id);
463
451
 
464
452
        if (has_search)
465
453
        {
478
466
        {
479
467
          results = yield run_zg_query (search,
480
468
                                        new Zeitgeist.TimeRange.anytime (),
481
 
                                        ResultType.MOST_RECENT_CURRENT_URI,
 
469
                                        Zeitgeist.ResultType.MOST_RECENT_CURRENT_URI,
482
470
                                        null,
483
471
                                        40,
484
472
                                        cancellable,
485
473
                                        true);
486
474
 
487
 
          Unity.FilesLens.append_events_sorted (search.search_string,
488
 
                                                results, results_model,
489
 
                                                0, int64.MAX, ResultFlags.NONE,
490
 
                                                null, Categories.DOWNLOADS);
 
475
          append_events_sorted (search.search_string,
 
476
                                results, results_model,
 
477
                                0, int64.MAX, ResultFlags.NONE,
 
478
                                null, Categories.DOWNLOADS);
491
479
        }
492
480
 
493
481
        if (has_search && use_locate)
494
482
        {
495
 
          yield perform_locate (500, search.search_string,
 
483
          yield perform_locate (search, 500, search.search_string,
496
484
                                search.results_model, cancellable,
497
485
                                Categories.FILES_AND_FOLDERS,
498
486
                                Categories.FILES_AND_FOLDERS);
506
494
      }
507
495
    }
508
496
 
509
 
    private async void update_search_async (LensSearch search,
510
 
                                            Cancellable cancellable)
 
497
    private async void update_search_async (DeprecatedScopeSearch search,
 
498
                                            GLib.Cancellable cancellable)
511
499
    {
512
500
      var results_model = search.results_model;
513
501
      var txn = new Dee.Transaction (results_model);
514
502
      var has_search = !is_search_empty (search);
515
503
 
516
 
      var filter = scope.get_filter ("type") as OptionsFilter;
 
504
      var filter = search.get_filter ("type") as OptionsFilter;
517
505
 
518
 
      var active_filters = get_current_types ();
 
506
      var active_filters = get_current_types (search);
519
507
      bool only_folders = active_filters != null &&
520
508
        active_filters.length == 1 && active_filters[0] == "folders";
521
509
      uint timer_id = 0;
523
511
      try
524
512
      {
525
513
        /* Get results ranked by recency */
526
 
        ResultSet? results = null;
 
514
        Zeitgeist.ResultSet? results = null;
527
515
        if (!only_folders)
528
516
        {
529
517
          results = yield run_zg_query (search,
530
 
                                        get_current_timerange (),
531
 
                                        ResultType.MOST_RECENT_CURRENT_URI,
 
518
                                        get_current_timerange (search),
 
519
                                        Zeitgeist.ResultType.MOST_RECENT_CURRENT_URI,
532
520
                                        filter,
533
521
                                        50,
534
522
                                        cancellable);
543
531
        if (checked_url != null)
544
532
        {
545
533
          txn.append (checked_url, urls.get_icon_for_type(url_type),
546
 
                Categories.RECENT,
 
534
                Categories.RECENT, ResultType.PERSONAL,
547
535
                "text/html", search.search_string,
548
 
                checked_url, checked_url);
 
536
                checked_url, checked_url, empty_asv);
549
537
        }
550
538
 
551
539
        /* apply filters to results found by zeitgeist */
552
540
        int64 min_size, max_size;
553
 
        get_current_size_limits (out min_size, out max_size);
 
541
        get_current_size_limits (search, out min_size, out max_size);
554
542
 
555
543
        if (results != null)
556
544
        {
557
 
          Unity.FilesLens.append_events_sorted (search.search_string,
558
 
                                                results, txn,
559
 
                                                min_size, max_size,
560
 
                                                ResultFlags.SKIP_FOLDERS);
 
545
          append_events_sorted (search.search_string,
 
546
                                results, txn,
 
547
                                min_size, max_size,
 
548
                                ResultFlags.SKIP_FOLDERS);
561
549
        }
562
550
 
563
551
        /* get recently downloaded files */
564
552
        results = yield run_zg_query (search,
565
 
                                      get_current_timerange (),
566
 
                                      ResultType.MOST_RECENT_CURRENT_URI,
 
553
                                      get_current_timerange (search),
 
554
                                      Zeitgeist.ResultType.MOST_RECENT_CURRENT_URI,
567
555
                                      filter,
568
556
                                      50,
569
557
                                      cancellable,
570
558
                                      true);
571
559
 
572
 
        Unity.FilesLens.append_events_sorted (search.search_string,
573
 
                                              results, txn,
574
 
                                              min_size, max_size,
575
 
                                              ResultFlags.NONE,
576
 
                                              null, Categories.DOWNLOADS);
 
560
        append_events_sorted (search.search_string,
 
561
                              results, txn,
 
562
                              min_size, max_size,
 
563
                              ResultFlags.NONE,
 
564
                              null, Categories.DOWNLOADS);
577
565
 
578
566
        /* commit if the origin query is taking too long, if we committed right
579
567
         * away, we'd cause flicker */
595
583
        if (filter == null ||
596
584
            !filter.filtering || filter.get_option ("folders").active)
597
585
        {
598
 
          results = yield run_zg_query (search, get_current_timerange (),
599
 
                                        ResultType.MOST_RECENT_ORIGIN,
 
586
          results = yield run_zg_query (search, get_current_timerange (search),
 
587
                                        Zeitgeist.ResultType.MOST_RECENT_ORIGIN,
600
588
                                        null, 50, cancellable);
601
589
 
602
590
          if (!txn.is_committed ()) txn.commit ();
618
606
            bookmark_uris.add (bookmark_obj.dnd_uri);
619
607
          }
620
608
 
621
 
          Unity.FilesLens.append_events_sorted (search.search_string,
622
 
                                                results, results_model,
623
 
                                                min_size, max_size,
624
 
                                                ResultFlags.USE_ORIGIN,
625
 
                                                bookmark_uris);
 
609
          append_events_sorted (search.search_string,
 
610
                                results, results_model,
 
611
                                min_size, max_size,
 
612
                                ResultFlags.USE_ORIGIN,
 
613
                                bookmark_uris);
626
614
 
627
615
          GLib.List<Device> matching_devices;
628
616
 
642
630
 
643
631
        if (has_search && use_locate)
644
632
        {
645
 
          yield perform_locate (500, search.search_string,
 
633
          yield perform_locate (search, 500, search.search_string,
646
634
                                search.results_model, cancellable);
647
635
        }
648
636
 
658
646
      }
659
647
    }
660
648
 
661
 
    private string[]? get_current_types ()
 
649
    private string[]? get_current_types (DeprecatedScopeSearch search)
662
650
    {
663
651
      /* returns null if the filter is disabled / all options selected */
664
 
      var filter = scope.get_filter ("type") as CheckOptionFilter;
 
652
      var filter = search.get_filter ("type") as CheckOptionFilter;
665
653
 
666
654
      if (filter == null || !filter.filtering) return null;
667
655
      string[] types = {};
679
667
      return types;
680
668
    }
681
669
 
682
 
    private TimeRange get_current_timerange ()
 
670
    private TimeRange get_current_timerange (DeprecatedScopeSearch search)
683
671
    {
684
 
      var filter = scope.get_filter ("modified") as RadioOptionFilter;
 
672
      var filter = search.get_filter ("modified") as RadioOptionFilter;
685
673
      Unity.FilterOption? option = filter.get_active_option ();
686
674
 
687
675
      string date = option == null ? "all" : option.id;
696
684
        return new TimeRange.anytime ();
697
685
    }
698
686
 
699
 
    private void get_current_size_limits (out int64 min_size, out int64 max_size)
 
687
    private void get_current_size_limits (DeprecatedScopeSearch search, out int64 min_size, out int64 max_size)
700
688
    {
701
 
      var filter = scope.get_filter ("size") as MultiRangeFilter;
 
689
      var filter = search.get_filter ("size") as MultiRangeFilter;
702
690
      Unity.FilterOption? min_opt = filter.get_first_active ();
703
691
      Unity.FilterOption? max_opt = filter.get_last_active ();
704
692
 
766
754
      }
767
755
    }
768
756
 
769
 
    private async ResultSet? run_zg_query (LensSearch search,
770
 
                                           TimeRange time_range,
771
 
                                           ResultType result_type,
772
 
                                           OptionsFilter? filters,
773
 
                                           uint num_results,
774
 
                                           Cancellable cancellable,
775
 
                                           bool downloads_only = false)
776
 
        throws Error
 
757
    private async Zeitgeist.ResultSet? run_zg_query (
 
758
        DeprecatedScopeSearch search,
 
759
        TimeRange time_range,
 
760
        Zeitgeist.ResultType result_type,
 
761
        OptionsFilter? filters,
 
762
        uint num_results,
 
763
        GLib.Cancellable cancellable,
 
764
        bool downloads_only = false) throws Error
777
765
    {
778
 
      ResultSet results;
 
766
      Zeitgeist.ResultSet results;
779
767
      var timer = new Timer ();
780
768
      var templates = create_template (filters, downloads_only);
781
769
      if (templates == null) return null;
824
812
      foreach (var bookmark in bookmarks)
825
813
      {
826
814
        results_model.append (bookmark.uri, bookmark.icon, category,
827
 
                              bookmark.mimetype, bookmark.display_name,
828
 
                              bookmark.dnd_uri);
 
815
                              ResultType.PERSONAL,
 
816
                              bookmark.mimetype, bookmark.display_name, "",
 
817
                              bookmark.dnd_uri, empty_asv);
829
818
      }
830
819
    }
831
820
 
836
825
      foreach (var device in devices)
837
826
      {
838
827
        results_model.append (device.uri, device.icon_name, category,
839
 
                              "", device.display_name,
840
 
                              device.dnd_uri);
 
828
                              ResultType.PERSONAL, "", device.display_name, "",
 
829
                              device.dnd_uri, empty_asv);
841
830
      }
842
831
    }
843
832
 
844
 
    private async void perform_locate (uint timeout,
 
833
    private async void perform_locate (DeprecatedScopeSearch search,
 
834
                                       uint timeout,
845
835
                                       string query,
846
836
                                       Dee.Model results_model,
847
 
                                       Cancellable cancellable,
 
837
                                       GLib.Cancellable cancellable,
848
838
                                       int files_category = Categories.RECENT,
849
839
                                       int dirs_category = Categories.FOLDERS)
850
840
      throws Error
867
857
        model_iter = results_model.next (model_iter);
868
858
      }
869
859
 
870
 
      var timerange = get_current_timerange ();
 
860
      var timerange = get_current_timerange (search);
871
861
      int64 min_size, max_size;
872
 
      get_current_size_limits (out min_size, out max_size);
873
 
      var types = get_current_types ();
 
862
      get_current_size_limits (search, out min_size, out max_size);
 
863
      var types = get_current_types (search);
874
864
 
875
865
      foreach (var info in results)
876
866
      {
898
888
        uint category_id = mimetype == "inode/directory" ?
899
889
          dirs_category : files_category;
900
890
 
901
 
        results_model.append (uri, icon_hint, category_id,
902
 
                              mimetype, info.get_display_name (), uri);
 
891
        results_model.append (uri, icon_hint, category_id, ResultType.PERSONAL,
 
892
                              mimetype, info.get_display_name (), "", uri,
 
893
                              empty_asv);
903
894
      }
904
895
    }
905
896
 
1184
1175
      scope.queue_search_changed (SearchType.DEFAULT);
1185
1176
      scope.queue_search_changed (SearchType.GLOBAL);
1186
1177
    }
1187
 
  }
1188
 
 
1189
 
  private const string ATTRS_TYPE_HIDDEN = FileAttribute.STANDARD_TYPE +
1190
 
    "," + FileAttribute.STANDARD_IS_HIDDEN;
1191
 
  private const string ATTRS_TYPE_SIZE_HIDDEN = FileAttribute.STANDARD_TYPE +
1192
 
    "," + FileAttribute.STANDARD_SIZE +
1193
 
    "," + FileAttribute.STANDARD_IS_HIDDEN;
1194
 
 
1195
 
  [Flags]
1196
 
  public enum ResultFlags
1197
 
  {
1198
 
    NONE = 0,
1199
 
    USE_ORIGIN,
1200
 
    EXTRACT_ORIGINS,
1201
 
    SKIP_FOLDERS,
1202
 
  }
1203
 
 
1204
 
  /* Appends a set of Zeitgeist.Events to our Dee.Model assuming that
1205
 
   * these events are already sorted with descending timestamps */
1206
 
  public void append_events_sorted (string search_string,
1207
 
                                    Zeitgeist.ResultSet? events,
1208
 
                                    Dee.Model results,
1209
 
                                    int64 min_size, int64 max_size,
1210
 
                                    ResultFlags flags,
1211
 
                                    Gee.Set<string>? excluded_uris = null,
1212
 
                                    int category_override = -1)
1213
 
  {
1214
 
    if (events == null) return;
1215
 
    uint category_id;
1216
 
    Set<string>? extracted_directories = null;
1217
 
 
1218
 
    if (ResultFlags.EXTRACT_ORIGINS in flags)
1219
 
    {
1220
 
      var folded_search = search_string.casefold ();
1221
 
      var origins = new HashSet<string> ();
1222
 
      // loop through all found events, take a look at origin field, add it
1223
 
      // to result set if it matches our search
1224
 
      // NOTE: using separate zg query with MOST_RECENT_ORIGINS grouping
1225
 
      // is more likely to find relevant directories, but is much slower
1226
 
      // than this kind of "approximation"
 
1178
 
 
1179
    private const string ATTRS_TYPE_HIDDEN = FileAttribute.STANDARD_TYPE +
 
1180
      "," + FileAttribute.STANDARD_IS_HIDDEN;
 
1181
    private const string ATTRS_TYPE_SIZE_HIDDEN = FileAttribute.STANDARD_TYPE +
 
1182
      "," + FileAttribute.STANDARD_SIZE +
 
1183
      "," + FileAttribute.STANDARD_IS_HIDDEN;
 
1184
 
 
1185
    [Flags]
 
1186
    public enum ResultFlags
 
1187
    {
 
1188
      NONE = 0,
 
1189
      USE_ORIGIN,
 
1190
      EXTRACT_ORIGINS,
 
1191
      SKIP_FOLDERS,
 
1192
    }
 
1193
 
 
1194
    /* Appends a set of Zeitgeist.Events to our Dee.Model assuming that
 
1195
     * these events are already sorted with descending timestamps */
 
1196
    public void append_events_sorted (string search_string,
 
1197
                                      Zeitgeist.ResultSet? events,
 
1198
                                      Dee.Model results,
 
1199
                                      int64 min_size, int64 max_size,
 
1200
                                      ResultFlags flags,
 
1201
                                      Gee.Set<string>? excluded_uris = null,
 
1202
                                      int category_override = -1)
 
1203
    {
 
1204
      if (events == null) return;
 
1205
      uint category_id;
 
1206
      Set<string>? extracted_directories = null;
 
1207
 
 
1208
      if (ResultFlags.EXTRACT_ORIGINS in flags)
 
1209
      {
 
1210
        var folded_search = search_string.casefold ();
 
1211
        var origins = new HashSet<string> ();
 
1212
        // loop through all found events, take a look at origin field, add it
 
1213
        // to result set if it matches our search
 
1214
        // NOTE: using separate zg query with MOST_RECENT_ORIGINS grouping
 
1215
        // is more likely to find relevant directories, but is much slower
 
1216
        // than this kind of "approximation"
 
1217
        foreach (var ev in events)
 
1218
        {
 
1219
          if (ev.num_subjects () > 0)
 
1220
          {
 
1221
            string origin = ev.get_subject (0).get_origin ();
 
1222
            if (origin == null || origin == "") continue;
 
1223
            var f = File.new_for_uri (origin);
 
1224
            origin = f.get_uri ();
 
1225
            if (excluded_uris != null && origin in excluded_uris) continue;
 
1226
            if (!(origin in origins) && f.is_native () && f.query_exists ())
 
1227
            {
 
1228
              string? path = f.get_path ();
 
1229
              if (path != null && path.contains ("/."))
 
1230
                continue;
 
1231
 
 
1232
              var display_name = Path.get_basename (f.get_parse_name ());
 
1233
              if (display_name.casefold ().has_prefix (folded_search))
 
1234
              {
 
1235
                string mimetype = "inode/directory";
 
1236
                string icon = Utils.get_icon_for_uri (origin, mimetype);
 
1237
                if (category_override >= 0)
 
1238
                  category_id = category_override;
 
1239
                else
 
1240
                  category_id = Categories.FOLDERS;
 
1241
                results.append (origin, icon, category_id, ResultType.PERSONAL,
 
1242
                                mimetype, display_name, "", origin, empty_asv);
 
1243
              }
 
1244
              else
 
1245
              {
 
1246
                if (extracted_directories == null)
 
1247
                {
 
1248
                  extracted_directories = new HashSet<string> ();
 
1249
                }
 
1250
                extracted_directories.add (origin);
 
1251
              }
 
1252
              origins.add (origin);
 
1253
            }
 
1254
          }
 
1255
        }
 
1256
        // we need this because of way ResultSet works with foreach
 
1257
        if (events.size () > 0) events.seek (0);
 
1258
      }
1227
1259
      foreach (var ev in events)
1228
1260
      {
1229
1261
        if (ev.num_subjects () > 0)
1230
1262
        {
1231
 
          string origin = ev.get_subject (0).get_origin ();
1232
 
          if (origin == null || origin == "") continue;
1233
 
          var f = File.new_for_uri (origin);
1234
 
          origin = f.get_uri ();
1235
 
          if (excluded_uris != null && origin in excluded_uris) continue;
1236
 
          if (!(origin in origins) && f.is_native () && f.query_exists ())
1237
 
          {
1238
 
            string? path = f.get_path ();
1239
 
            if (path != null && path.contains ("/."))
1240
 
              continue;
1241
 
 
1242
 
            var display_name = Path.get_basename (f.get_parse_name ());
1243
 
            if (display_name.casefold ().has_prefix (folded_search))
1244
 
            {
1245
 
              string mimetype = "inode/directory";
1246
 
              string icon = Utils.get_icon_for_uri (origin, mimetype);
1247
 
              if (category_override >= 0)
1248
 
                category_id = category_override;
1249
 
              else
1250
 
                category_id = Categories.FOLDERS;
1251
 
              results.append (origin, icon, category_id, mimetype,
1252
 
                              display_name, "", origin);
1253
 
            }
1254
 
            else
1255
 
            {
1256
 
              if (extracted_directories == null)
 
1263
          // FIXME: We only use the first subject...
 
1264
          Zeitgeist.Subject su = ev.get_subject (0);
 
1265
 
 
1266
          string uri;
 
1267
          string display_name;
 
1268
          string mimetype;
 
1269
 
 
1270
          if (ResultFlags.USE_ORIGIN in flags)
 
1271
          {
 
1272
            uri = su.get_origin ();
 
1273
            display_name = "";
 
1274
            mimetype = "inode/directory";
 
1275
          }
 
1276
          else
 
1277
          {
 
1278
            uri = su.get_current_uri ();
 
1279
            display_name = su.get_text ();
 
1280
            mimetype = su.get_mimetype ();
 
1281
            mimetype = su.get_mimetype () != null ?
 
1282
                       su.get_mimetype () : "application/octet-stream";
 
1283
          }
 
1284
          if (uri == null) continue;
 
1285
          File file = File.new_for_uri (uri);
 
1286
          if (excluded_uris != null && file.get_uri () in excluded_uris) continue;
 
1287
 
 
1288
          if (display_name == null || display_name == "")
 
1289
          {
 
1290
            display_name = Path.get_basename (file.get_parse_name ());
 
1291
          }
 
1292
 
 
1293
          bool check_size = min_size > 0 || max_size < int64.MAX;
 
1294
          /* Don't check existence on non-native files as http:// and
 
1295
           * friends are *very* expensive to query */
 
1296
          FileType file_type = FileType.UNKNOWN;
 
1297
          if (file.is_native ())
 
1298
          {
 
1299
            // hidden files should be ignored
 
1300
            try
 
1301
            {
 
1302
              FileInfo info = file.query_info (check_size ?
 
1303
                ATTRS_TYPE_SIZE_HIDDEN : ATTRS_TYPE_HIDDEN, 0, null);
 
1304
              string? path = file.get_path ();
 
1305
              if (path != null && path.contains ("/."))
 
1306
                continue;
 
1307
              if (check_size &&
 
1308
                (info.get_size () < min_size || info.get_size () > max_size))
1257
1309
              {
1258
 
                extracted_directories = new HashSet<string> ();
 
1310
                continue;
1259
1311
              }
1260
 
              extracted_directories.add (origin);
 
1312
              file_type = info.get_file_type ();
1261
1313
            }
1262
 
            origins.add (origin);
1263
 
          }
1264
 
        }
1265
 
      }
1266
 
      // we need this because of way ResultSet works with foreach
1267
 
      if (events.size () > 0) events.seek (0);
1268
 
    }
1269
 
    foreach (var ev in events)
1270
 
    {
1271
 
      if (ev.num_subjects () > 0)
1272
 
      {
1273
 
        // FIXME: We only use the first subject...
1274
 
        Zeitgeist.Subject su = ev.get_subject (0);
1275
 
 
1276
 
        string uri;
1277
 
        string display_name;
1278
 
        string mimetype;
1279
 
 
1280
 
        if (ResultFlags.USE_ORIGIN in flags)
1281
 
        {
1282
 
          uri = su.get_origin ();
1283
 
          display_name = "";
1284
 
          mimetype = "inode/directory";
1285
 
        }
1286
 
        else
1287
 
        {
1288
 
          uri = su.get_current_uri ();
1289
 
          display_name = su.get_text ();
1290
 
          mimetype = su.get_mimetype ();
1291
 
          mimetype = su.get_mimetype () != null ?
1292
 
                     su.get_mimetype () : "application/octet-stream";
1293
 
        }
1294
 
        if (uri == null) continue;
1295
 
        File file = File.new_for_uri (uri);
1296
 
        if (excluded_uris != null && file.get_uri () in excluded_uris) continue;
1297
 
 
1298
 
        if (display_name == null || display_name == "")
1299
 
        {
1300
 
          display_name = Path.get_basename (file.get_parse_name ());
1301
 
        }
1302
 
 
1303
 
        bool check_size = min_size > 0 || max_size < int64.MAX;
1304
 
        /* Don't check existence on non-native files as http:// and
1305
 
         * friends are *very* expensive to query */
1306
 
        FileType file_type = FileType.UNKNOWN;
1307
 
        if (file.is_native ())
1308
 
        {
1309
 
          // hidden files should be ignored
1310
 
          try
1311
 
          {
1312
 
            FileInfo info = file.query_info (check_size ?
1313
 
              ATTRS_TYPE_SIZE_HIDDEN : ATTRS_TYPE_HIDDEN, 0, null);
1314
 
            string? path = file.get_path ();
1315
 
            if (path != null && path.contains ("/."))
1316
 
              continue;
1317
 
            if (check_size &&
1318
 
              (info.get_size () < min_size || info.get_size () > max_size))
 
1314
            catch (GLib.Error e)
1319
1315
            {
 
1316
              // as error occurred file must be missing therefore ignoring it
1320
1317
              continue;
1321
1318
            }
1322
 
            file_type = info.get_file_type ();
1323
 
          }
1324
 
          catch (GLib.Error e)
1325
 
          {
1326
 
            // as error occurred file must be missing therefore ignoring it
1327
 
            continue;
1328
 
          }
 
1319
          }
 
1320
          string icon = Utils.get_icon_for_uri (uri, mimetype);
 
1321
          string comment = file.get_parse_name ();
 
1322
 
 
1323
          var is_dir = file_type == FileType.DIRECTORY;
 
1324
          if (is_dir && ResultFlags.SKIP_FOLDERS in flags) continue;
 
1325
 
 
1326
          if (category_override >= 0)
 
1327
            category_id = category_override;
 
1328
          else
 
1329
            category_id = is_dir ? Categories.FOLDERS : Categories.RECENT;
 
1330
 
 
1331
          results.append (uri, icon, category_id, ResultType.PERSONAL, mimetype,
 
1332
                          display_name, comment, uri, empty_asv);
 
1333
 
1329
1334
        }
1330
 
        string icon = Utils.get_icon_for_uri (uri, mimetype);
1331
 
        string comment = file.get_parse_name ();
1332
 
 
1333
 
        var is_dir = file_type == FileType.DIRECTORY;
1334
 
        if (is_dir && ResultFlags.SKIP_FOLDERS in flags) continue;
1335
 
 
1336
 
        if (category_override >= 0)
1337
 
          category_id = category_override;
1338
 
        else
1339
 
          category_id = is_dir ? Categories.FOLDERS : Categories.RECENT;
1340
 
 
1341
 
        results.append (uri, icon, category_id, mimetype,
1342
 
                        display_name, comment, uri);
1343
 
 
1344
1335
      }
1345
 
    }
1346
1336
 
1347
 
    if (extracted_directories != null)
1348
 
    {
1349
 
      foreach (var dir_uri in extracted_directories)
 
1337
      if (extracted_directories != null)
1350
1338
      {
1351
 
        var f = File.new_for_uri (dir_uri);
1352
 
        var display_name = Path.get_basename (f.get_parse_name ());
1353
 
        string mimetype = "inode/directory";
1354
 
        string icon = Utils.get_icon_for_uri (dir_uri, mimetype);
1355
 
        if (category_override >= 0)
1356
 
          category_id = category_override;
1357
 
        else
1358
 
          category_id = Categories.FOLDERS;
1359
 
        results.append (dir_uri, icon, category_id, mimetype,
1360
 
                        display_name, "", dir_uri);
 
1339
        foreach (var dir_uri in extracted_directories)
 
1340
        {
 
1341
          var f = File.new_for_uri (dir_uri);
 
1342
          var display_name = Path.get_basename (f.get_parse_name ());
 
1343
          string mimetype = "inode/directory";
 
1344
          string icon = Utils.get_icon_for_uri (dir_uri, mimetype);
 
1345
          if (category_override >= 0)
 
1346
            category_id = category_override;
 
1347
          else
 
1348
            category_id = Categories.FOLDERS;
 
1349
          results.append (dir_uri, icon, category_id, ResultType.PERSONAL,
 
1350
                          mimetype, display_name, "", dir_uri, empty_asv);
 
1351
        }
1361
1352
      }
1362
1353
    }
1363
1354
  }