Fix the build
[glib.git] / gio / gfilenamecompleter.c
blobcccc7229f93f82cdeb48b6cbaadd479fb30439f6
1 /* GIO - GLib Input, Output and Streaming Library
2 *
3 * Copyright (C) 2006-2007 Red Hat, Inc.
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General
16 * Public License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18 * Boston, MA 02111-1307, USA.
20 * Author: Alexander Larsson <alexl@redhat.com>
23 #include <config.h>
24 #include "gfilenamecompleter.h"
25 #include "gfile.h"
26 #include <string.h>
27 #include "glibintl.h"
29 #include "gioalias.h"
31 /**
32 * SECTION:gfilenamecompleter
33 * @short_description: Filename Completer
34 * @include: gio/gio.h
36 * Completes partial file and directory names given a partial string by
37 * looking in the file system for clues. Can return a list of possible
38 * completion strings for widget implementations.
40 **/
42 enum {
43 GOT_COMPLETION_DATA,
44 LAST_SIGNAL
47 static guint signals[LAST_SIGNAL] = { 0 };
49 typedef struct {
50 GFilenameCompleter *completer;
51 GFileEnumerator *enumerator;
52 GCancellable *cancellable;
53 gboolean should_escape;
54 GFile *dir;
55 GList *basenames;
56 gboolean dirs_only;
57 } LoadBasenamesData;
59 struct _GFilenameCompleter {
60 GObject parent;
62 GFile *basenames_dir;
63 gboolean basenames_are_escaped;
64 GList *basenames;
65 gboolean dirs_only;
67 LoadBasenamesData *basename_loader;
70 G_DEFINE_TYPE (GFilenameCompleter, g_filename_completer, G_TYPE_OBJECT);
72 static void cancel_load_basenames (GFilenameCompleter *completer);
74 static void
75 g_filename_completer_finalize (GObject *object)
77 GFilenameCompleter *completer;
79 completer = G_FILENAME_COMPLETER (object);
81 cancel_load_basenames (completer);
83 if (completer->basenames_dir)
84 g_object_unref (completer->basenames_dir);
86 g_list_foreach (completer->basenames, (GFunc)g_free, NULL);
87 g_list_free (completer->basenames);
89 if (G_OBJECT_CLASS (g_filename_completer_parent_class)->finalize)
90 (*G_OBJECT_CLASS (g_filename_completer_parent_class)->finalize) (object);
93 static void
94 g_filename_completer_class_init (GFilenameCompleterClass *klass)
96 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
98 gobject_class->finalize = g_filename_completer_finalize;
99 /**
100 * GFilenameCompleter::got-completion-data:
102 * Emitted when the file name completion information comes available.
104 signals[GOT_COMPLETION_DATA] = g_signal_new (I_("got_completion_data"),
105 G_TYPE_FILENAME_COMPLETER,
106 G_SIGNAL_RUN_LAST,
107 G_STRUCT_OFFSET (GFilenameCompleterClass, got_completion_data),
108 NULL, NULL,
109 g_cclosure_marshal_VOID__VOID,
110 G_TYPE_NONE, 0);
113 static void
114 g_filename_completer_init (GFilenameCompleter *completer)
119 * g_filename_completer_new:
121 * Creates a new filename completer.
123 * Returns: a #GFilenameCompleter.
125 GFilenameCompleter *
126 g_filename_completer_new (void)
128 return g_object_new (G_TYPE_FILENAME_COMPLETER, NULL);
131 static char *
132 longest_common_prefix (char *a, char *b)
134 char *start;
136 start = a;
138 while (g_utf8_get_char (a) == g_utf8_get_char (b))
140 a = g_utf8_next_char (a);
141 b = g_utf8_next_char (b);
144 return g_strndup (start, a - start);
147 static void
148 load_basenames_data_free (LoadBasenamesData *data)
150 if (data->enumerator)
151 g_object_unref (data->enumerator);
153 g_object_unref (data->cancellable);
154 g_object_unref (data->dir);
156 g_list_foreach (data->basenames, (GFunc)g_free, NULL);
157 g_list_free (data->basenames);
159 g_free (data);
162 static void
163 got_more_files (GObject *source_object,
164 GAsyncResult *res,
165 gpointer user_data)
167 LoadBasenamesData *data = user_data;
168 GList *infos, *l;
169 GFileInfo *info;
170 const char *name;
171 gboolean append_slash;
172 char *t;
173 char *basename;
175 if (data->completer == NULL)
177 /* Was cancelled */
178 load_basenames_data_free (data);
179 return;
182 infos = g_file_enumerator_next_files_finish (data->enumerator, res, NULL);
184 for (l = infos; l != NULL; l = l->next)
186 info = l->data;
188 if (data->dirs_only &&
189 g_file_info_get_file_type (info) != G_FILE_TYPE_DIRECTORY)
191 g_object_unref (info);
192 continue;
195 append_slash = g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY;
196 name = g_file_info_get_name (info);
197 if (name == NULL)
199 g_object_unref (info);
200 continue;
204 if (data->should_escape)
205 basename = g_uri_escape_string (name,
206 G_URI_RESERVED_CHARS_ALLOWED_IN_PATH,
207 TRUE);
208 else
209 /* If not should_escape, must be a local filename, convert to utf8 */
210 basename = g_filename_to_utf8 (name, -1, NULL, NULL, NULL);
212 if (basename)
214 if (append_slash)
216 t = basename;
217 basename = g_strconcat (basename, "/", NULL);
218 g_free (t);
221 data->basenames = g_list_prepend (data->basenames, basename);
224 g_object_unref (info);
227 g_list_free (infos);
229 if (infos)
231 /* Not last, get more files */
232 g_file_enumerator_next_files_async (data->enumerator,
233 100,
235 data->cancellable,
236 got_more_files, data);
238 else
240 data->completer->basename_loader = NULL;
242 if (data->completer->basenames_dir)
243 g_object_unref (data->completer->basenames_dir);
244 g_list_foreach (data->completer->basenames, (GFunc)g_free, NULL);
245 g_list_free (data->completer->basenames);
247 data->completer->basenames_dir = g_object_ref (data->dir);
248 data->completer->basenames = data->basenames;
249 data->completer->basenames_are_escaped = data->should_escape;
250 data->basenames = NULL;
252 g_file_enumerator_close_async (data->enumerator, 0, NULL, NULL, NULL);
254 g_signal_emit (data->completer, signals[GOT_COMPLETION_DATA], 0);
255 load_basenames_data_free (data);
260 static void
261 got_enum (GObject *source_object,
262 GAsyncResult *res,
263 gpointer user_data)
265 LoadBasenamesData *data = user_data;
267 if (data->completer == NULL)
269 /* Was cancelled */
270 load_basenames_data_free (data);
271 return;
274 data->enumerator = g_file_enumerate_children_finish (G_FILE (source_object), res, NULL);
276 if (data->enumerator == NULL)
278 data->completer->basename_loader = NULL;
280 if (data->completer->basenames_dir)
281 g_object_unref (data->completer->basenames_dir);
282 g_list_foreach (data->completer->basenames, (GFunc)g_free, NULL);
283 g_list_free (data->completer->basenames);
285 /* Mark uptodate with no basenames */
286 data->completer->basenames_dir = g_object_ref (data->dir);
287 data->completer->basenames = NULL;
288 data->completer->basenames_are_escaped = data->should_escape;
290 load_basenames_data_free (data);
291 return;
294 g_file_enumerator_next_files_async (data->enumerator,
295 100,
297 data->cancellable,
298 got_more_files, data);
301 static void
302 schedule_load_basenames (GFilenameCompleter *completer,
303 GFile *dir,
304 gboolean should_escape)
306 LoadBasenamesData *data;
308 cancel_load_basenames (completer);
310 data = g_new0 (LoadBasenamesData, 1);
311 data->completer = completer;
312 data->cancellable = g_cancellable_new ();
313 data->dir = g_object_ref (dir);
314 data->should_escape = should_escape;
315 data->dirs_only = completer->dirs_only;
317 completer->basename_loader = data;
319 g_file_enumerate_children_async (dir,
320 G_FILE_ATTRIBUTE_STANDARD_NAME "," G_FILE_ATTRIBUTE_STANDARD_TYPE,
321 0, 0,
322 data->cancellable,
323 got_enum, data);
326 static void
327 cancel_load_basenames (GFilenameCompleter *completer)
329 LoadBasenamesData *loader;
331 if (completer->basename_loader)
333 loader = completer->basename_loader;
334 loader->completer = NULL;
336 g_cancellable_cancel (loader->cancellable);
338 completer->basename_loader = NULL;
343 /* Returns a list of possible matches and the basename to use for it */
344 static GList *
345 init_completion (GFilenameCompleter *completer,
346 const char *initial_text,
347 char **basename_out)
349 gboolean should_escape;
350 GFile *file, *parent;
351 char *basename;
352 char *t;
353 int len;
355 *basename_out = NULL;
357 should_escape = ! (g_path_is_absolute (initial_text) || *initial_text == '~');
359 len = strlen (initial_text);
361 if (len > 0 &&
362 initial_text[len - 1] == '/')
363 return NULL;
365 file = g_file_parse_name (initial_text);
366 parent = g_file_get_parent (file);
367 if (parent == NULL)
369 g_object_unref (file);
370 return NULL;
373 if (completer->basenames_dir == NULL ||
374 completer->basenames_are_escaped != should_escape ||
375 !g_file_equal (parent, completer->basenames_dir))
377 schedule_load_basenames (completer, parent, should_escape);
378 g_object_unref (file);
379 return NULL;
382 basename = g_file_get_basename (file);
383 if (should_escape)
385 t = basename;
386 basename = g_uri_escape_string (basename, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, TRUE);
387 g_free (t);
389 else
391 t = basename;
392 basename = g_filename_to_utf8 (basename, -1, NULL, NULL, NULL);
393 g_free (t);
395 if (basename == NULL)
396 return NULL;
399 *basename_out = basename;
401 return completer->basenames;
405 * g_filename_completer_get_completion_suffix:
406 * @completer: the filename completer.
407 * @initial_text: text to be completed.
409 * Obtains a completion for @initial_text from @completer.
411 * Returns: a completed string, or %NULL if no completion exists.
412 * This string is not owned by GIO, so remember to g_free() it
413 * when finished.
415 char *
416 g_filename_completer_get_completion_suffix (GFilenameCompleter *completer,
417 const char *initial_text)
419 GList *possible_matches, *l;
420 char *prefix;
421 char *suffix;
422 char *possible_match;
423 char *lcp;
425 g_return_val_if_fail (G_IS_FILENAME_COMPLETER (completer), NULL);
426 g_return_val_if_fail (initial_text != NULL, NULL);
428 possible_matches = init_completion (completer, initial_text, &prefix);
430 suffix = NULL;
432 for (l = possible_matches; l != NULL; l = l->next)
434 possible_match = l->data;
436 if (g_str_has_prefix (possible_match, prefix))
438 if (suffix == NULL)
439 suffix = g_strdup (possible_match + strlen (prefix));
440 else
442 lcp = longest_common_prefix (suffix,
443 possible_match + strlen (prefix));
444 g_free (suffix);
445 suffix = lcp;
447 if (*suffix == 0)
448 break;
453 g_free (prefix);
455 return suffix;
459 * g_filename_completer_get_completions:
460 * @completer: the filename completer.
461 * @initial_text: text to be completed.
463 * Gets an array of completion strings for a given initial text.
465 * Returns: array of strings with possible completions for @initial_text.
466 * This array must be freed by g_strfreev() when finished.
468 char **
469 g_filename_completer_get_completions (GFilenameCompleter *completer,
470 const char *initial_text)
472 GList *possible_matches, *l;
473 char *prefix;
474 char *possible_match;
475 GPtrArray *res;
477 g_return_val_if_fail (G_IS_FILENAME_COMPLETER (completer), NULL);
478 g_return_val_if_fail (initial_text != NULL, NULL);
480 possible_matches = init_completion (completer, initial_text, &prefix);
482 res = g_ptr_array_new ();
483 for (l = possible_matches; l != NULL; l = l->next)
485 possible_match = l->data;
487 if (g_str_has_prefix (possible_match, prefix))
488 g_ptr_array_add (res,
489 g_strconcat (initial_text, possible_match + strlen (prefix), NULL));
492 g_free (prefix);
494 return (char**)g_ptr_array_free (res, FALSE);
498 * g_filename_completer_set_dirs_only:
499 * @completer: the filename completer.
500 * @dirs_only: a #gboolean.
502 * If @dirs_only is %TRUE, @completer will only
503 * complete directory names, and not file names.
505 void
506 g_filename_completer_set_dirs_only (GFilenameCompleter *completer,
507 gboolean dirs_only)
509 g_return_if_fail (G_IS_FILENAME_COMPLETER (completer));
511 completer->dirs_only = dirs_only;
514 #define __G_FILENAME_COMPLETER_C__
515 #include "gioaliasdef.c"