nautilus-search-directory.c 30.5 KB
Newer Older
1
/*
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
 *  Copyright (C) 2005 Novell, Inc
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License as
 *  published by the Free Software Foundation; either version 2 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public
 *  License along with this program; if not, see <http://www.gnu.org/licenses/>.
 *
 *  Author: Anders Carlsson <andersca@imendio.com>
 */
19 20

#include "nautilus-search-directory.h"
21

22
#include <eel/eel-glib-extensions.h>
23
#include <gio/gio.h>
24
#include <gtk/gtk.h>
25 26 27
#include <string.h>
#include <sys/time.h>

28 29 30 31 32 33 34 35 36 37
#include "nautilus-directory-private.h"
#include "nautilus-file-private.h"
#include "nautilus-file-utilities.h"
#include "nautilus-file.h"
#include "nautilus-query.h"
#include "nautilus-search-directory-file.h"
#include "nautilus-search-engine-model.h"
#include "nautilus-search-engine.h"
#include "nautilus-search-provider.h"

38
struct _NautilusSearchDirectory
39
{
40 41
    NautilusDirectory parent_instance;

42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
    NautilusQuery *query;

    NautilusSearchEngine *engine;

    gboolean search_running;
    /* When the search directory is stopped or cancelled, we migth wait
     * until all data and signals from previous search are stopped and removed
     * from the search engine. While this situation happens we don't want to connect
     * clients to our signals, and we will wait until the search data and signals
     * are valid and ready.
     * The worst thing that can happens if we don't do this is that new clients
     * migth get the information of old searchs if they are waiting_for_file_list.
     * But that shouldn't be a big deal since old clients have the old information.
     * But anyway it's currently unused for this case since the only client is
     * nautilus-view and is not waiting_for_file_list :) .
     *
     * The other use case is for letting clients know if information of the directory
     * is outdated or not valid. This might happens for automatic
     * scheduled timeouts. */
    gboolean search_ready_and_valid;

    GList *files;
    GHashTable *files_hash;

    GList *monitor_list;
    GList *callback_list;
    GList *pending_callback_list;

    GBinding *binding;

    NautilusDirectory *base_model;
73 74
};

75 76 77 78
typedef struct
{
    gboolean monitor_hidden_files;
    NautilusFileAttributes monitor_attributes;
79

80
    gconstpointer client;
81 82
} SearchMonitor;

83 84 85
typedef struct
{
    NautilusSearchDirectory *search_directory;
86

87 88
    NautilusDirectoryCallback callback;
    gpointer callback_data;
89

90 91 92 93
    NautilusFileAttributes wait_for_attributes;
    gboolean wait_for_file_list;
    GList *file_list;
    GHashTable *non_ready_hash;
94 95
} SearchCallback;

96 97 98 99 100 101
enum
{
    PROP_0,
    PROP_BASE_MODEL,
    PROP_QUERY,
    NUM_PROPERTIES
102 103
};

104 105
G_DEFINE_TYPE_WITH_CODE (NautilusSearchDirectory, nautilus_search_directory, NAUTILUS_TYPE_DIRECTORY,
                         nautilus_ensure_extension_points ();
106 107 108 109 110 111
                         /* It looks like you’re implementing an extension point.
                          * Did you modify nautilus_ensure_extension_builtins() accordingly?
                          *
                          * • Yes
                          * • Doing it right now
                          */
112 113 114 115
                         g_io_extension_point_implement (NAUTILUS_DIRECTORY_PROVIDER_EXTENSION_POINT_NAME,
                                                         g_define_type_id,
                                                         NAUTILUS_SEARCH_DIRECTORY_PROVIDER_NAME,
                                                         0));
116

117 118
static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };

119 120
static void search_engine_hits_added (NautilusSearchEngine    *engine,
                                      GList                   *hits,
121
                                      NautilusSearchDirectory *self);
122 123
static void search_engine_error (NautilusSearchEngine    *engine,
                                 const char              *error,
124
                                 NautilusSearchDirectory *self);
125 126 127
static void search_callback_file_ready_callback (NautilusFile *file,
                                                 gpointer      data);
static void file_changed (NautilusFile            *file,
128
                          NautilusSearchDirectory *self);
129 130

static void
131
reset_file_list (NautilusSearchDirectory *self)
132
{
133 134 135 136 137
    GList *list, *monitor_list;
    NautilusFile *file;
    SearchMonitor *monitor;

    /* Remove file connections */
138
    for (list = self->files; list != NULL; list = list->next)
139 140 141 142
    {
        file = list->data;

        /* Disconnect change handler */
143
        g_signal_handlers_disconnect_by_func (file, file_changed, self);
144 145

        /* Remove monitors */
146
        for (monitor_list = self->monitor_list; monitor_list;
147 148 149 150 151 152
             monitor_list = monitor_list->next)
        {
            monitor = monitor_list->data;
            nautilus_file_monitor_remove (file, monitor);
        }
    }
153

154 155
    nautilus_file_list_free (self->files);
    self->files = NULL;
156

157
    g_hash_table_remove_all (self->files_hash);
158 159
}

160
static void
161
set_hidden_files (NautilusSearchDirectory *self)
162
{
163 164 165 166
    GList *l;
    SearchMonitor *monitor;
    gboolean monitor_hidden = FALSE;

167
    for (l = self->monitor_list; l != NULL; l = l->next)
168 169 170 171 172 173 174 175 176
    {
        monitor = l->data;
        monitor_hidden |= monitor->monitor_hidden_files;

        if (monitor_hidden)
        {
            break;
        }
    }
177

178
    nautilus_query_set_show_hidden_files (self->query, monitor_hidden);
179 180
}

181
static void
182
start_search (NautilusSearchDirectory *self)
183
{
184
    NautilusSearchEngineModel *model_provider;
185

186
    if (!self->query)
187 188 189
    {
        return;
    }
190

191
    if (self->search_running)
192 193 194
    {
        return;
    }
195

196
    if (!self->monitor_list && !self->pending_callback_list)
197 198 199
    {
        return;
    }
200

201
    /* We need to start the search engine */
202 203
    self->search_running = TRUE;
    self->search_ready_and_valid = FALSE;
204

205 206 207
    set_hidden_files (self);
    nautilus_search_provider_set_query (NAUTILUS_SEARCH_PROVIDER (self->engine),
                                        self->query);
208

209 210
    model_provider = nautilus_search_engine_get_model_provider (self->engine);
    nautilus_search_engine_model_set_model (model_provider, self->base_model);
211

212
    reset_file_list (self);
213

214
    nautilus_search_provider_start (NAUTILUS_SEARCH_PROVIDER (self->engine));
215 216 217
}

static void
218
stop_search (NautilusSearchDirectory *self)
219
{
220
    if (!self->search_running)
221 222 223
    {
        return;
    }
224

225 226
    self->search_running = FALSE;
    nautilus_search_provider_stop (NAUTILUS_SEARCH_PROVIDER (self->engine));
227

228
    reset_file_list (self);
229 230 231
}

static void
232
file_changed (NautilusFile            *file,
233
              NautilusSearchDirectory *self)
234
{
235
    GList list;
236

237 238
    list.data = file;
    list.next = NULL;
239

240
    nautilus_directory_emit_files_changed (NAUTILUS_DIRECTORY (self), &list);
241 242 243
}

static void
244 245 246 247 248 249
search_monitor_add (NautilusDirectory         *directory,
                    gconstpointer              client,
                    gboolean                   monitor_hidden_files,
                    NautilusFileAttributes     file_attributes,
                    NautilusDirectoryCallback  callback,
                    gpointer                   callback_data)
250
{
251 252
    GList *list;
    SearchMonitor *monitor;
253
    NautilusSearchDirectory *self;
254 255
    NautilusFile *file;

256
    self = NAUTILUS_SEARCH_DIRECTORY (directory);
257

258 259 260 261
    monitor = g_new0 (SearchMonitor, 1);
    monitor->monitor_hidden_files = monitor_hidden_files;
    monitor->monitor_attributes = file_attributes;
    monitor->client = client;
262

263
    self->monitor_list = g_list_prepend (self->monitor_list, monitor);
264

265 266
    if (callback != NULL)
    {
267
        (*callback)(directory, self->files, callback_data);
268
    }
269

270
    for (list = self->files; list != NULL; list = list->next)
271 272
    {
        file = list->data;
273

274 275 276 277
        /* Add monitors */
        nautilus_file_monitor_add (file, monitor, file_attributes);
    }

278
    start_search (self);
279 280 281
}

static void
282
search_monitor_remove_file_monitors (SearchMonitor           *monitor,
283
                                     NautilusSearchDirectory *self)
284
{
285 286 287
    GList *list;
    NautilusFile *file;

288
    for (list = self->files; list != NULL; list = list->next)
289 290
    {
        file = list->data;
291

292 293
        nautilus_file_monitor_remove (file, monitor);
    }
294 295 296
}

static void
297
search_monitor_destroy (SearchMonitor           *monitor,
298
                        NautilusSearchDirectory *self)
299
{
300
    search_monitor_remove_file_monitors (monitor, self);
301

302
    g_free (monitor);
303 304 305 306
}

static void
search_monitor_remove (NautilusDirectory *directory,
307
                       gconstpointer      client)
308
{
309
    NautilusSearchDirectory *self;
310 311
    SearchMonitor *monitor;
    GList *list;
312

313
    self = NAUTILUS_SEARCH_DIRECTORY (directory);
314

315
    for (list = self->monitor_list; list != NULL; list = list->next)
316 317
    {
        monitor = list->data;
318

319 320
        if (monitor->client == client)
        {
321
            self->monitor_list = g_list_delete_link (self->monitor_list, list);
322

323
            search_monitor_destroy (monitor, self);
324

325 326 327
            break;
        }
    }
328

329
    if (!self->monitor_list)
330
    {
331
        stop_search (self);
332
    }
333 334 335
}

static void
336 337 338
cancel_call_when_ready (gpointer key,
                        gpointer value,
                        gpointer user_data)
339
{
340 341
    SearchCallback *search_callback;
    NautilusFile *file;
342

343 344
    file = key;
    search_callback = user_data;
345

346 347
    nautilus_file_cancel_call_when_ready (file, search_callback_file_ready_callback,
                                          search_callback);
348 349 350 351 352
}

static void
search_callback_destroy (SearchCallback *search_callback)
{
353 354 355 356 357
    if (search_callback->non_ready_hash)
    {
        g_hash_table_foreach (search_callback->non_ready_hash, cancel_call_when_ready, search_callback);
        g_hash_table_destroy (search_callback->non_ready_hash);
    }
358

359
    nautilus_file_list_free (search_callback->file_list);
360

361
    g_free (search_callback);
362 363 364 365 366
}

static void
search_callback_invoke_and_destroy (SearchCallback *search_callback)
{
367 368 369
    search_callback->callback (NAUTILUS_DIRECTORY (search_callback->search_directory),
                               search_callback->file_list,
                               search_callback->callback_data);
370

371 372
    search_callback->search_directory->callback_list =
        g_list_remove (search_callback->search_directory->callback_list, search_callback);
373

374
    search_callback_destroy (search_callback);
375 376 377
}

static void
378 379
search_callback_file_ready_callback (NautilusFile *file,
                                     gpointer      data)
380
{
381
    SearchCallback *search_callback = data;
382

383 384 385 386 387 388
    g_hash_table_remove (search_callback->non_ready_hash, file);

    if (g_hash_table_size (search_callback->non_ready_hash) == 0)
    {
        search_callback_invoke_and_destroy (search_callback);
    }
389 390 391 392 393
}

static void
search_callback_add_file_callbacks (SearchCallback *callback)
{
394 395
    GList *file_list_copy, *list;
    NautilusFile *file;
396

397
    file_list_copy = g_list_copy (callback->file_list);
398

399 400 401
    for (list = file_list_copy; list != NULL; list = list->next)
    {
        file = list->data;
402

403 404 405 406 407 408
        nautilus_file_call_when_ready (file,
                                       callback->wait_for_attributes,
                                       search_callback_file_ready_callback,
                                       callback);
    }
    g_list_free (file_list_copy);
409
}
410

411
static SearchCallback *
412
search_callback_find (NautilusSearchDirectory   *self,
413 414
                      NautilusDirectoryCallback  callback,
                      gpointer                   callback_data)
415
{
416 417
    SearchCallback *search_callback;
    GList *list;
418

419
    for (list = self->callback_list; list != NULL; list = list->next)
420 421 422 423 424 425 426 427 428
    {
        search_callback = list->data;

        if (search_callback->callback == callback &&
            search_callback->callback_data == callback_data)
        {
            return search_callback;
        }
    }
429

430
    return NULL;
431 432 433
}

static SearchCallback *
434
search_callback_find_pending (NautilusSearchDirectory   *self,
435 436
                              NautilusDirectoryCallback  callback,
                              gpointer                   callback_data)
437
{
438 439
    SearchCallback *search_callback;
    GList *list;
440

441
    for (list = self->pending_callback_list; list != NULL; list = list->next)
442 443
    {
        search_callback = list->data;
444

445 446 447 448 449 450 451 452
        if (search_callback->callback == callback &&
            search_callback->callback_data == callback_data)
        {
            return search_callback;
        }
    }

    return NULL;
453 454 455 456 457
}

static GHashTable *
file_list_to_hash_table (GList *file_list)
{
458 459
    GList *list;
    GHashTable *table;
460

461 462 463 464
    if (!file_list)
    {
        return NULL;
    }
465

466
    table = g_hash_table_new (NULL, NULL);
467

468 469 470 471
    for (list = file_list; list != NULL; list = list->next)
    {
        g_hash_table_insert (table, list->data, list->data);
    }
472

473
    return table;
474 475 476
}

static void
477 478 479 480 481
search_call_when_ready (NautilusDirectory         *directory,
                        NautilusFileAttributes     file_attributes,
                        gboolean                   wait_for_file_list,
                        NautilusDirectoryCallback  callback,
                        gpointer                   callback_data)
482
{
483
    NautilusSearchDirectory *self;
484 485
    SearchCallback *search_callback;

486
    self = NAUTILUS_SEARCH_DIRECTORY (directory);
487

488
    search_callback = search_callback_find (self, callback, callback_data);
489 490
    if (search_callback == NULL)
    {
491
        search_callback = search_callback_find_pending (self, callback, callback_data);
492 493 494 495 496 497 498 499 500
    }

    if (search_callback)
    {
        g_warning ("tried to add a new callback while an old one was pending");
        return;
    }

    search_callback = g_new0 (SearchCallback, 1);
501
    search_callback->search_directory = self;
502 503 504 505 506
    search_callback->callback = callback;
    search_callback->callback_data = callback_data;
    search_callback->wait_for_attributes = file_attributes;
    search_callback->wait_for_file_list = wait_for_file_list;

507
    if (wait_for_file_list && !self->search_ready_and_valid)
508 509 510 511
    {
        /* Add it to the pending callback list, which will be
         * processed when the directory has valid data from the new
         * search and all data and signals from previous searchs is removed. */
512 513
        self->pending_callback_list =
            g_list_prepend (self->pending_callback_list, search_callback);
514 515

        /* We might need to start the search engine */
516
        start_search (self);
517 518 519
    }
    else
    {
520 521
        search_callback->file_list = nautilus_file_list_copy (self->files);
        search_callback->non_ready_hash = file_list_to_hash_table (self->files);
522 523 524 525 526 527 528 529 530 531

        if (!search_callback->non_ready_hash)
        {
            /* If there are no ready files, we invoke the callback
             *  with an empty list.
             */
            search_callback_invoke_and_destroy (search_callback);
        }
        else
        {
532
            self->callback_list = g_list_prepend (self->callback_list, search_callback);
533 534 535
            search_callback_add_file_callbacks (search_callback);
        }
    }
536 537 538
}

static void
539 540 541
search_cancel_callback (NautilusDirectory         *directory,
                        NautilusDirectoryCallback  callback,
                        gpointer                   callback_data)
542
{
543
    NautilusSearchDirectory *self;
544 545
    SearchCallback *search_callback;

546 547
    self = NAUTILUS_SEARCH_DIRECTORY (directory);
    search_callback = search_callback_find (self, callback, callback_data);
548

549 550
    if (search_callback)
    {
551
        self->callback_list = g_list_remove (self->callback_list, search_callback);
552

553
        search_callback_destroy (search_callback);
554

555 556
        goto done;
    }
557

558
    /* Check for a pending callback */
559
    search_callback = search_callback_find_pending (self, callback, callback_data);
560

561 562
    if (search_callback)
    {
563
        self->pending_callback_list = g_list_remove (self->pending_callback_list, search_callback);
564 565 566

        search_callback_destroy (search_callback);
    }
567 568

done:
569
    if (!self->callback_list && !self->pending_callback_list)
570
    {
571
        stop_search (self);
572
    }
573 574
}

575 576 577
static void
search_callback_add_pending_file_callbacks (SearchCallback *callback)
{
578 579
    callback->file_list = nautilus_file_list_copy (callback->search_directory->files);
    callback->non_ready_hash = file_list_to_hash_table (callback->search_directory->files);
580

581
    search_callback_add_file_callbacks (callback);
582
}
583

584
static void
585
search_directory_add_pending_files_callbacks (NautilusSearchDirectory *self)
586
{
587
    /* Add all file callbacks */
588
    g_list_foreach (self->pending_callback_list,
589
                    (GFunc) search_callback_add_pending_file_callbacks, NULL);
590
    self->callback_list = g_list_concat (self->callback_list,
591
                                         self->pending_callback_list);
592

593 594
    g_list_free (self->pending_callback_list);
    self->pending_callback_list = NULL;
595 596
}

597
static void
598
on_search_directory_search_ready_and_valid (NautilusSearchDirectory *self)
599
{
600 601
    search_directory_add_pending_files_callbacks (self);
    self->search_ready_and_valid = TRUE;
602 603
}

604
static void
605 606
search_engine_hits_added (NautilusSearchEngine    *engine,
                          GList                   *hits,
607
                          NautilusSearchDirectory *self)
608
{
609 610 611 612 613 614 615 616 617 618 619 620 621 622
    GList *hit_list;
    GList *file_list;
    NautilusFile *file;
    SearchMonitor *monitor;
    GList *monitor_list;

    file_list = NULL;

    for (hit_list = hits; hit_list != NULL; hit_list = hit_list->next)
    {
        NautilusSearchHit *hit = hit_list->data;
        const char *uri;

        uri = nautilus_search_hit_get_uri (hit);
623

624
        nautilus_search_hit_compute_scores (hit, self->query);
625

626 627
        file = nautilus_file_get_by_uri (uri);
        nautilus_file_set_search_relevance (file, nautilus_search_hit_get_relevance (hit));
Alexandru Pandelea's avatar
Alexandru Pandelea committed
628
        nautilus_file_set_search_fts_snippet (file, nautilus_search_hit_get_fts_snippet (hit));
629

630
        for (monitor_list = self->monitor_list; monitor_list; monitor_list = monitor_list->next)
631 632
        {
            monitor = monitor_list->data;
633

634 635 636
            /* Add monitors */
            nautilus_file_monitor_add (file, monitor, monitor->monitor_attributes);
        }
637

638
        g_signal_connect (file, "changed", G_CALLBACK (file_changed), self),
639

640
        file_list = g_list_prepend (file_list, file);
641
        g_hash_table_add (self->files_hash, file);
642
    }
643

644
    self->files = g_list_concat (self->files, file_list);
645

646
    nautilus_directory_emit_files_added (NAUTILUS_DIRECTORY (self), file_list);
647

648
    file = nautilus_directory_get_corresponding_file (NAUTILUS_DIRECTORY (self));
649 650
    nautilus_file_emit_changed (file);
    nautilus_file_unref (file);
651

652
    search_directory_add_pending_files_callbacks (self);
653 654 655
}

static void
656 657
search_engine_error (NautilusSearchEngine    *engine,
                     const char              *error_message,
658
                     NautilusSearchDirectory *self)
659
{
660
    GError *error;
Alexander Larsson's avatar
Alexander Larsson committed
661

662 663
    error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_FAILED,
                                 error_message);
664
    nautilus_directory_emit_load_error (NAUTILUS_DIRECTORY (self),
665 666
                                        error);
    g_error_free (error);
667 668
}

669
static void
670 671
search_engine_finished (NautilusSearchEngine         *engine,
                        NautilusSearchProviderStatus  status,
672
                        NautilusSearchDirectory      *self)
673
{
674 675 676 677 678 679 680 681 682 683
    /* If the search engine is going to restart means it finished an old search
     * that was stopped or cancelled.
     * Don't emit the done loading signal in this case, since this means the search
     * directory tried to start a new search before all the search providers were finished
     * in the search engine.
     * If we emit the done-loading signal in this situation the client will think
     * that it finished the current search, not an old one like it's actually
     * happening. */
    if (status == NAUTILUS_SEARCH_PROVIDER_STATUS_NORMAL)
    {
684 685
        on_search_directory_search_ready_and_valid (self);
        nautilus_directory_emit_done_loading (NAUTILUS_DIRECTORY (self));
686 687 688 689 690
    }
    else if (status == NAUTILUS_SEARCH_PROVIDER_STATUS_RESTARTING)
    {
        /* Remove file monitors of the files from an old search that just
         * actually finished */
691
        reset_file_list (self);
692
    }
693 694
}

695 696 697
static void
search_force_reload (NautilusDirectory *directory)
{
698
    NautilusSearchDirectory *self;
699 700
    NautilusFile *file;

701
    self = NAUTILUS_SEARCH_DIRECTORY (directory);
702

703
    if (!self->query)
704 705 706
    {
        return;
    }
707

708
    self->search_ready_and_valid = FALSE;
709

710
    /* Remove file monitors */
711 712
    reset_file_list (self);
    stop_search (self);
713

714 715 716
    file = nautilus_directory_get_corresponding_file (directory);
    nautilus_file_invalidate_all_attributes (file);
    nautilus_file_unref (file);
717 718 719 720 721
}

static gboolean
search_are_all_files_seen (NautilusDirectory *directory)
{
722
    NautilusSearchDirectory *self;
723

724
    self = NAUTILUS_SEARCH_DIRECTORY (directory);
725

726 727
    return (!self->query ||
            self->search_ready_and_valid);
728 729 730 731
}

static gboolean
search_contains_file (NautilusDirectory *directory,
732
                      NautilusFile      *file)
733
{
734
    NautilusSearchDirectory *self;
735

736 737
    self = NAUTILUS_SEARCH_DIRECTORY (directory);
    return (g_hash_table_lookup (self->files_hash, file) != NULL);
738 739 740 741 742
}

static GList *
search_get_file_list (NautilusDirectory *directory)
{
743
    NautilusSearchDirectory *self;
744

745
    self = NAUTILUS_SEARCH_DIRECTORY (directory);
746

747
    return nautilus_file_list_copy (self->files);
748 749 750 751 752 753
}


static gboolean
search_is_editable (NautilusDirectory *directory)
{
754
    return FALSE;
755 756
}

757 758 759
static gboolean
real_handles_location (GFile *location)
{
760
    g_autofree gchar *uri = NULL;
761

762
    uri = g_file_get_uri (location);
763

764
    return eel_uri_is_search (uri);
765 766
}

767
static void
768 769 770 771
search_set_property (GObject      *object,
                     guint         property_id,
                     const GValue *value,
                     GParamSpec   *pspec)
772
{
773
    NautilusSearchDirectory *self = NAUTILUS_SEARCH_DIRECTORY (object);
774

775 776 777 778
    switch (property_id)
    {
        case PROP_BASE_MODEL:
        {
779
            nautilus_search_directory_set_base_model (self, g_value_get_object (value));
780 781 782 783 784
        }
        break;

        case PROP_QUERY:
        {
785
            nautilus_search_directory_set_query (self, g_value_get_object (value));
786 787 788 789 790 791 792 793 794
        }
        break;

        default:
        {
            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
        }
        break;
    }
795 796 797
}

static void
798 799 800 801
search_get_property (GObject    *object,
                     guint       property_id,
                     GValue     *value,
                     GParamSpec *pspec)
802
{
803
    NautilusSearchDirectory *self = NAUTILUS_SEARCH_DIRECTORY (object);
804 805 806 807 808

    switch (property_id)
    {
        case PROP_BASE_MODEL:
        {
809
            g_value_set_object (value, nautilus_search_directory_get_base_model (self));
810 811
        }
        break;
812

813 814
        case PROP_QUERY:
        {
815
            g_value_take_object (value, nautilus_search_directory_get_query (self));
816 817 818 819 820 821 822 823 824
        }
        break;

        default:
        {
            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
        }
        break;
    }
825 826 827
}

static void
828
clear_base_model (NautilusSearchDirectory *self)
829
{
830
    if (self->base_model != NULL)
831
    {
832 833 834
        nautilus_directory_file_monitor_remove (self->base_model,
                                                &self->base_model);
        g_clear_object (&self->base_model);
835
    }
836 837
}

838
static void
839
search_connect_engine (NautilusSearchDirectory *self)
840
{
841
    g_signal_connect (self->engine, "hits-added",
842
                      G_CALLBACK (search_engine_hits_added),
843 844
                      self);
    g_signal_connect (self->engine, "error",
845
                      G_CALLBACK (search_engine_error),
846 847
                      self);
    g_signal_connect (self->engine, "finished",
848
                      G_CALLBACK (search_engine_finished),
849
                      self);
850 851 852
}

static void
853
search_disconnect_engine (NautilusSearchDirectory *self)
854
{
855
    g_signal_handlers_disconnect_by_func (self->engine,
856
                                          search_engine_hits_added,
857 858
                                          self);
    g_signal_handlers_disconnect_by_func (self->engine,
859
                                          search_engine_error,
860 861
                                          self);
    g_signal_handlers_disconnect_by_func (self->engine,
862
                                          search_engine_finished,
863
                                          self);
864 865
}

866 867 868
static void
search_dispose (GObject *object)
{
869
    NautilusSearchDirectory *self;
870
    GList *list;
871

872
    self = NAUTILUS_SEARCH_DIRECTORY (object);
873

874
    clear_base_model (self);
875

876
    /* Remove search monitors */
877
    if (self->monitor_list)
878
    {
879
        for (list = self->monitor_list; list != NULL; list = list->next)
880
        {
881
            search_monitor_destroy ((SearchMonitor *) list->data, self);
882 883
        }

884 885
        g_list_free (self->monitor_list);
        self->monitor_list = NULL;
886 887
    }

888
    reset_file_list (self);
889

890
    if (self->callback_list)
891 892
    {
        /* Remove callbacks */
893
        g_list_foreach (self->callback_list,
894
                        (GFunc) search_callback_destroy, NULL);
895 896
        g_list_free (self->callback_list);
        self->callback_list = NULL;
897
    }
898

899
    if (self->pending_callback_list)
900
    {
901
        g_list_foreach (self->pending_callback_list,
902
                        (GFunc) search_callback_destroy, NULL);
903 904
        g_list_free (self->pending_callback_list);
        self->pending_callback_list = NULL;
905
    }
906

907 908 909
    g_clear_object (&self->query);
    stop_search (self);
    search_disconnect_engine (self);
910

911
    g_clear_object (&self->engine);
912

913
    G_OBJECT_CLASS (nautilus_search_directory_parent_class)->dispose (object);
914 915 916 917 918
}

static void
search_finalize (GObject *object)
{
919
    NautilusSearchDirectory *self;
920

921
    self = NAUTILUS_SEARCH_DIRECTORY (object);
922

923
    g_hash_table_destroy (self->files_hash);
924

925
    G_OBJECT_CLASS (nautilus_search_directory_parent_class)->finalize (object);
926 927 928
}

static void
929
nautilus_search_directory_init (NautilusSearchDirectory *self)
930
{
931 932
    self->query = NULL;
    self->files_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
933

934 935
    self->engine = nautilus_search_engine_new ();
    search_connect_engine (self);
936 937 938 939 940
}

static void
nautilus_search_directory_class_init (NautilusSearchDirectoryClass *class)
{
941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975
    NautilusDirectoryClass *directory_class = NAUTILUS_DIRECTORY_CLASS (class);
    GObjectClass *oclass = G_OBJECT_CLASS (class);

    oclass->dispose = search_dispose;
    oclass->finalize = search_finalize;
    oclass->get_property = search_get_property;
    oclass->set_property = search_set_property;

    directory_class->are_all_files_seen = search_are_all_files_seen;
    directory_class->contains_file = search_contains_file;
    directory_class->force_reload = search_force_reload;
    directory_class->call_when_ready = search_call_when_ready;
    directory_class->cancel_callback = search_cancel_callback;

    directory_class->file_monitor_add = search_monitor_add;
    directory_class->file_monitor_remove = search_monitor_remove;

    directory_class->get_file_list = search_get_file_list;
    directory_class->is_editable = search_is_editable;
    directory_class->handles_location = real_handles_location;

    properties[PROP_BASE_MODEL] =
        g_param_spec_object ("base-model",
                             "The base model",
                             "The base directory model for this directory",
                             NAUTILUS_TYPE_DIRECTORY,
                             G_PARAM_READWRITE);
    properties[PROP_QUERY] =
        g_param_spec_object ("query",
                             "The query",
                             "The query for this search directory",
                             NAUTILUS_TYPE_QUERY,
                             G_PARAM_READWRITE);

    g_object_class_install_properties (oclass, NUM_PROPERTIES, properties);
976 977 978
}

void
979
nautilus_search_directory_set_base_model (NautilusSearchDirectory *self,
980
                                          NautilusDirectory       *base_model)
981
{
982
    if (self->base_model == base_model)
983 984 985
    {
        return;
    }
986

987
    if (self->query != NULL)
988 989 990
    {
        GFile *query_location, *model_location;
        gboolean is_equal;
991

992
        query_location = nautilus_query_get_location (self->query);
993
        model_location = nautilus_directory_get_location (base_model);
994

995
        is_equal = g_file_equal (model_location, query_location);
996

997 998
        g_object_unref (model_location);
        g_object_unref (query_location);
999

1000 1001 1002 1003 1004
        if (!is_equal)
        {
            return;
        }
    }
1005

1006 1007
    clear_base_model (self);
    self->base_model = nautilus_directory_ref (base_model);
1008

1009
    if (self->base_model != NULL)
1010
    {
1011
        nautilus_directory_file_monitor_add (base_model, &self->base_model,
1012 1013 1014
                                             TRUE, NAUTILUS_FILE_ATTRIBUTE_INFO,
                                             NULL, NULL);
    }
1015

1016
    g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_BASE_MODEL]);
1017 1018 1019
}

NautilusDirectory *
1020
nautilus_search_directory_get_base_model (NautilusSearchDirectory *self)
1021
{
1022
    return self->base_model;
1023 1024 1025 1026 1027
}

char *
nautilus_search_directory_generate_new_uri (void)
{
1028 1029
    static int counter = 0;
    char *uri;
1030

1031
    uri = g_strdup_printf (EEL_SEARCH_URI "//%d/", counter++);
1032

1033
    return uri;
1034 1035 1036
}

void
1037
nautilus_search_directory_set_query (NautilusSearchDirectory *self,
1038
                                     NautilusQuery           *query)
1039
{
1040 1041
    NautilusFile *file;
    NautilusQuery *old_query;
1042

1043
    old_query = self->query;
1044

1045
    if (self->query != query)
1046
    {
1047
        self->query = g_object_ref (query);
1048

1049
        g_clear_pointer (&self->binding, g_binding_unbind);
1050

1051 1052
        if (query)
        {
1053
            self->binding = g_object_bind_property (self->engine, "running",
1054 1055
                                                    query, "searching",
                                                    G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
1056
        }
Alexander Larsson's avatar
Alexander Larsson committed
1057

1058
        g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_QUERY]);
1059

1060 1061
        g_clear_object (&old_query);
    }
Alexander Larsson's avatar
Alexander Larsson committed
1062

1063
    file = nautilus_directory_get_existing_corresponding_file (NAUTILUS_DIRECTORY (self));
1064 1065 1066 1067 1068
    if (file != NULL)
    {
        nautilus_search_directory_file_update_display_name (NAUTILUS_SEARCH_DIRECTORY_FILE (file));
    }
    nautilus_file_unref (file);
1069 1070 1071
}

NautilusQuery *
1072
nautilus_search_directory_get_query (NautilusSearchDirectory *self)
1073
{
1074
    if (self->query != NULL)
1075
    {
1076
        return g_object_ref (self->query);
1077 1078 1079
    }

    return NULL;
1080
}