gtksearchenginesimple.c 9.64 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/*
 * Copyright (C) 2005 Red Hat, Inc
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
Javier Jardón's avatar
Javier Jardón committed
15
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 17 18 19 20 21
 *
 * Author: Alexander Larsson <alexl@redhat.com>
 *
 * Based on nautilus-search-engine-simple.c
 */

22
#include "config.h"
23

24
#include <gio/gio.h>
25

Emmanuele Bassi's avatar
Emmanuele Bassi committed
26 27
#include <gdk/gdk.h>

28
#include "gtksearchenginesimple.h"
Matthias Clasen's avatar
Matthias Clasen committed
29
#include "gtkprivate.h"
30 31

#include <string.h>
32

33 34
#define BATCH_SIZE 500

Matthias Clasen's avatar
Matthias Clasen committed
35
typedef struct
36 37
{
  GtkSearchEngineSimple *engine;
38
  GCancellable *cancellable;
Matthias Clasen's avatar
Matthias Clasen committed
39

40
  GQueue *directories;
Matthias Clasen's avatar
Matthias Clasen committed
41

42
  gint n_processed_files;
43
  GList *hits;
Matthias Clasen's avatar
Matthias Clasen committed
44

45
  GtkQuery *query;
46
  gboolean recursive;
47 48 49
} SearchThreadData;


50
struct _GtkSearchEngineSimple
51
{
52 53
  GtkSearchEngine parent;

54
  GtkQuery *query;
Matthias Clasen's avatar
Matthias Clasen committed
55

56
  SearchThreadData *active_search;
Matthias Clasen's avatar
Matthias Clasen committed
57

58
  gboolean query_finished;
59 60 61

  GtkSearchEngineSimpleIsIndexed is_indexed_callback;
  gpointer                       is_indexed_data;
62
  GDestroyNotify                 is_indexed_data_destroy;
63 64
};

65 66 67 68
struct _GtkSearchEngineSimpleClass
{
  GtkSearchEngineClass parent_class;
};
69

70
G_DEFINE_TYPE (GtkSearchEngineSimple, _gtk_search_engine_simple, GTK_TYPE_SEARCH_ENGINE)
71 72

static void
Emmanuele Bassi's avatar
Emmanuele Bassi committed
73
gtk_search_engine_simple_dispose (GObject *object)
74
{
75
  GtkSearchEngineSimple *simple = GTK_SEARCH_ENGINE_SIMPLE (object);
Matthias Clasen's avatar
Matthias Clasen committed
76

77
  g_clear_object (&simple->query);
Matthias Clasen's avatar
Matthias Clasen committed
78

79
  if (simple->active_search)
Emmanuele Bassi's avatar
Emmanuele Bassi committed
80
    {
81 82
      g_cancellable_cancel (simple->active_search->cancellable);
      simple->active_search = NULL;
Emmanuele Bassi's avatar
Emmanuele Bassi committed
83 84
    }

85
  _gtk_search_engine_simple_set_indexed_cb (simple, NULL, NULL, NULL);
86

Emmanuele Bassi's avatar
Emmanuele Bassi committed
87
  G_OBJECT_CLASS (_gtk_search_engine_simple_parent_class)->dispose (object);
88 89 90 91 92 93 94
}

static SearchThreadData *
search_thread_data_new (GtkSearchEngineSimple *engine,
			GtkQuery              *query)
{
  SearchThreadData *data;
95 96
  const gchar *uri;
  GFile *location;
Matthias Clasen's avatar
Matthias Clasen committed
97

98
  data = g_new0 (SearchThreadData, 1);
Matthias Clasen's avatar
Matthias Clasen committed
99

100
  data->engine = g_object_ref (engine);
101 102
  data->directories = g_queue_new ();
  data->query = g_object_ref (query);
103
  data->recursive = _gtk_search_engine_get_recursive (GTK_SEARCH_ENGINE (engine));
Matthias Clasen's avatar
Matthias Clasen committed
104
  uri = gtk_query_get_location (query);
Matthias Clasen's avatar
Matthias Clasen committed
105
  if (uri != NULL)
106 107 108 109
    location = g_file_new_for_uri (uri);
  else
    location = g_file_new_for_path (g_get_home_dir ());
  g_queue_push_tail (data->directories, location);
Matthias Clasen's avatar
Matthias Clasen committed
110

111
  data->cancellable = g_cancellable_new ();
Matthias Clasen's avatar
Matthias Clasen committed
112

113 114 115
  return data;
}

Matthias Clasen's avatar
Matthias Clasen committed
116
static void
117 118
search_thread_data_free (SearchThreadData *data)
{
119 120 121 122
  g_queue_foreach (data->directories, (GFunc)g_object_unref, NULL);
  g_queue_free (data->directories);
  g_object_unref (data->cancellable);
  g_object_unref (data->query);
123
  g_object_unref (data->engine);
124

125 126 127 128 129 130 131 132 133
  g_free (data);
}

static gboolean
search_thread_done_idle (gpointer user_data)
{
  SearchThreadData *data;

  data = user_data;
Matthias Clasen's avatar
Matthias Clasen committed
134

135
  if (!g_cancellable_is_cancelled (data->cancellable))
Emmanuele Bassi's avatar
Emmanuele Bassi committed
136
    _gtk_search_engine_finished (GTK_SEARCH_ENGINE (data->engine));
Matthias Clasen's avatar
Matthias Clasen committed
137

138
  data->engine->active_search = NULL;
139
  search_thread_data_free (data);
Matthias Clasen's avatar
Matthias Clasen committed
140

141 142 143
  return FALSE;
}

Matthias Clasen's avatar
Matthias Clasen committed
144
typedef struct
145
{
146
  GList *hits;
147
  SearchThreadData *thread_data;
148
} Batch;
149 150 151 152

static gboolean
search_thread_add_hits_idle (gpointer user_data)
{
153
  Batch *batch = user_data;
154

155 156
  if (!g_cancellable_is_cancelled (batch->thread_data->cancellable))
    _gtk_search_engine_hits_added (GTK_SEARCH_ENGINE (batch->thread_data->engine), batch->hits);
157

158 159
  g_list_free_full (batch->hits, (GDestroyNotify)_gtk_search_hit_free);
  g_free (batch);
Matthias Clasen's avatar
Matthias Clasen committed
160

161 162 163 164 165 166
  return FALSE;
}

static void
send_batch (SearchThreadData *data)
{
167
  Batch *batch;
Matthias Clasen's avatar
Matthias Clasen committed
168

169
  data->n_processed_files = 0;
Matthias Clasen's avatar
Matthias Clasen committed
170

171
  if (data->hits)
172
    {
173 174
      guint id;

175 176 177
      batch = g_new (Batch, 1);
      batch->hits = data->hits;
      batch->thread_data = data;
Matthias Clasen's avatar
Matthias Clasen committed
178

179
      id = gdk_threads_add_idle (search_thread_add_hits_idle, batch);
180
      g_source_set_name_by_id (id, "[gtk+] search_thread_add_hits_idle");
181
    }
Emmanuele Bassi's avatar
Emmanuele Bassi committed
182

183
  data->hits = NULL;
184 185
}

186 187 188 189
static gboolean
is_indexed (GtkSearchEngineSimple *engine,
            GFile                 *location)
{
190
  if (engine->is_indexed_callback)
191
    {
192
      if (engine->is_indexed_callback (location, engine->is_indexed_data))
193 194 195 196 197 198 199 200 201 202 203 204
        {
          gchar *uri = g_file_get_uri (location);
          g_debug ("Simple search engine: Skipping indexed location: %s\n", uri);
          g_free (uri);

          return TRUE;
        }
    }

  return FALSE;
}

205
static void
206
visit_directory (GFile *dir, SearchThreadData *data)
207
{
208 209 210 211 212 213 214 215 216
  GFileEnumerator *enumerator;
  GFileInfo *info;
  GFile *child;
  const gchar *display_name;

  enumerator = g_file_enumerate_children (dir,
                                          G_FILE_ATTRIBUTE_STANDARD_NAME ","
                                          G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME ","
                                          G_FILE_ATTRIBUTE_STANDARD_TYPE ","
217 218 219 220
                                          G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN ","
                                          G_FILE_ATTRIBUTE_STANDARD_IS_BACKUP ","
                                          G_FILE_ATTRIBUTE_STANDARD_SIZE ","
                                          G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE ","
221
                                          G_FILE_ATTRIBUTE_STANDARD_TARGET_URI ","
222
                                          G_FILE_ATTRIBUTE_TIME_MODIFIED ","
223 224
                                          G_FILE_ATTRIBUTE_TIME_ACCESS ","
                                          G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME,
225 226 227 228
                                          G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
                                          data->cancellable, NULL);
  if (enumerator == NULL)
    return;
229

230
  while (g_file_enumerator_iterate (enumerator, &info, &child, data->cancellable, NULL))
231
    {
232 233
      if (info == NULL)
        break;
234

235 236 237
      display_name = g_file_info_get_display_name (info);
      if (display_name == NULL)
        continue;
238 239 240 241

      if (g_file_info_get_is_hidden (info))
        continue;

242
      if (gtk_query_matches_string (data->query, display_name))
243 244 245 246 247 248 249 250
        {
          GtkSearchHit *hit;

          hit = g_new (GtkSearchHit, 1);
          hit->uri = g_file_get_uri (child);
          hit->info = g_object_ref (info);
          data->hits = g_list_prepend (data->hits, hit);
        }
251

252 253 254
      data->n_processed_files++;
      if (data->n_processed_files > BATCH_SIZE)
        send_batch (data);
255

256 257 258
      if (data->recursive &&
          g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY &&
          !is_indexed (data->engine, child))
259
        g_queue_push_tail (data->directories, g_object_ref (child));
260
    }
261

262
  g_object_unref (enumerator);
263 264
}

Matthias Clasen's avatar
Matthias Clasen committed
265
static gpointer
266 267 268
search_thread_func (gpointer user_data)
{
  SearchThreadData *data;
269 270
  GFile *dir;
  guint id;
Matthias Clasen's avatar
Matthias Clasen committed
271

272
  data = user_data;
Matthias Clasen's avatar
Matthias Clasen committed
273

274 275 276 277 278 279
  while (!g_cancellable_is_cancelled (data->cancellable) &&
         (dir = g_queue_pop_head (data->directories)) != NULL)
    {
      visit_directory (dir, data);
      g_object_unref (dir);
    }
280

281 282
  if (!g_cancellable_is_cancelled (data->cancellable))
    send_batch (data);
Matthias Clasen's avatar
Matthias Clasen committed
283

284 285
  id = gdk_threads_add_idle (search_thread_done_idle, data);
  g_source_set_name_by_id (id, "[gtk+] search_thread_done_idle");
Matthias Clasen's avatar
Matthias Clasen committed
286

287 288 289 290 291 292 293 294
  return NULL;
}

static void
gtk_search_engine_simple_start (GtkSearchEngine *engine)
{
  GtkSearchEngineSimple *simple;
  SearchThreadData *data;
Matthias Clasen's avatar
Matthias Clasen committed
295

296
  simple = GTK_SEARCH_ENGINE_SIMPLE (engine);
Matthias Clasen's avatar
Matthias Clasen committed
297

298
  if (simple->active_search != NULL)
299
    return;
Matthias Clasen's avatar
Matthias Clasen committed
300

301
  if (simple->query == NULL)
302
    return;
Matthias Clasen's avatar
Matthias Clasen committed
303

304
  data = search_thread_data_new (simple, simple->query);
Matthias Clasen's avatar
Matthias Clasen committed
305

306
  g_thread_unref (g_thread_new ("file-search", search_thread_func, data));
Matthias Clasen's avatar
Matthias Clasen committed
307

308
  simple->active_search = data;
309 310 311 312 313 314
}

static void
gtk_search_engine_simple_stop (GtkSearchEngine *engine)
{
  GtkSearchEngineSimple *simple;
Matthias Clasen's avatar
Matthias Clasen committed
315

316
  simple = GTK_SEARCH_ENGINE_SIMPLE (engine);
Matthias Clasen's avatar
Matthias Clasen committed
317

318
  if (simple->active_search != NULL)
319
    {
320 321
      g_cancellable_cancel (simple->active_search->cancellable);
      simple->active_search = NULL;
322 323 324 325
    }
}

static void
Matthias Clasen's avatar
Matthias Clasen committed
326
gtk_search_engine_simple_set_query (GtkSearchEngine *engine,
327 328 329
				    GtkQuery        *query)
{
  GtkSearchEngineSimple *simple;
Matthias Clasen's avatar
Matthias Clasen committed
330

331
  simple = GTK_SEARCH_ENGINE_SIMPLE (engine);
Matthias Clasen's avatar
Matthias Clasen committed
332

333 334 335
  if (query)
    g_object_ref (query);

336 337
  if (simple->query)
    g_object_unref (simple->query);
338

339
  simple->query = query;
340 341 342 343 344 345 346
}

static void
_gtk_search_engine_simple_class_init (GtkSearchEngineSimpleClass *class)
{
  GObjectClass *gobject_class;
  GtkSearchEngineClass *engine_class;
Matthias Clasen's avatar
Matthias Clasen committed
347

348
  gobject_class = G_OBJECT_CLASS (class);
Emmanuele Bassi's avatar
Emmanuele Bassi committed
349
  gobject_class->dispose = gtk_search_engine_simple_dispose;
Matthias Clasen's avatar
Matthias Clasen committed
350

351 352 353 354 355 356 357 358 359 360 361 362 363 364
  engine_class = GTK_SEARCH_ENGINE_CLASS (class);
  engine_class->set_query = gtk_search_engine_simple_set_query;
  engine_class->start = gtk_search_engine_simple_start;
  engine_class->stop = gtk_search_engine_simple_stop;
}

static void
_gtk_search_engine_simple_init (GtkSearchEngineSimple *engine)
{
}

GtkSearchEngine *
_gtk_search_engine_simple_new (void)
{
365
  return g_object_new (GTK_TYPE_SEARCH_ENGINE_SIMPLE, NULL);
366
}
367 368 369 370

void
_gtk_search_engine_simple_set_indexed_cb (GtkSearchEngineSimple          *engine,
                                          GtkSearchEngineSimpleIsIndexed  callback,
371 372
                                          gpointer                        data,
                                          GDestroyNotify                  destroy)
373
{
374 375
  if (engine->is_indexed_data_destroy)
    engine->is_indexed_data_destroy (engine->is_indexed_data);
376

377 378 379
  engine->is_indexed_callback = callback;
  engine->is_indexed_data = data;
  engine->is_indexed_data_destroy = destroy;
380
}