1 /* GIO - GLib Input, Output and Streaming Library
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, see <http://www.gnu.org/licenses/>.
18 * Author: Alexander Larsson <alexl@redhat.com>
22 #include "gfilenamecompleter.h"
23 #include "gfileenumerator.h"
24 #include "gfileattribute.h"
26 #include "gfileinfo.h"
27 #include "gcancellable.h"
33 * SECTION:gfilenamecompleter
34 * @short_description: Filename Completer
37 * Completes partial file and directory names given a partial string by
38 * looking in the file system for clues. Can return a list of possible
39 * completion strings for widget implementations.
48 static guint signals[LAST_SIGNAL] = { 0 };
51 GFilenameCompleter *completer;
52 GFileEnumerator *enumerator;
53 GCancellable *cancellable;
54 gboolean should_escape;
60 struct _GFilenameCompleter {
64 gboolean basenames_are_escaped;
68 LoadBasenamesData *basename_loader;
71 G_DEFINE_TYPE (GFilenameCompleter, g_filename_completer, G_TYPE_OBJECT);
73 static void cancel_load_basenames (GFilenameCompleter *completer);
76 g_filename_completer_finalize (GObject *object)
78 GFilenameCompleter *completer;
80 completer = G_FILENAME_COMPLETER (object);
82 cancel_load_basenames (completer);
84 if (completer->basenames_dir)
85 g_object_unref (completer->basenames_dir);
87 g_list_free_full (completer->basenames, g_free);
89 G_OBJECT_CLASS (g_filename_completer_parent_class)->finalize (object);
93 g_filename_completer_class_init (GFilenameCompleterClass *klass)
95 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
97 gobject_class->finalize = g_filename_completer_finalize;
99 * GFilenameCompleter::got-completion-data:
101 * Emitted when the file name completion information comes available.
103 signals[GOT_COMPLETION_DATA] = g_signal_new (I_("got-completion-data"),
104 G_TYPE_FILENAME_COMPLETER,
106 G_STRUCT_OFFSET (GFilenameCompleterClass, got_completion_data),
108 g_cclosure_marshal_VOID__VOID,
113 g_filename_completer_init (GFilenameCompleter *completer)
118 * g_filename_completer_new:
120 * Creates a new filename completer.
122 * Returns: a #GFilenameCompleter.
125 g_filename_completer_new (void)
127 return g_object_new (G_TYPE_FILENAME_COMPLETER, NULL);
131 longest_common_prefix (char *a, char *b)
137 while (g_utf8_get_char (a) == g_utf8_get_char (b))
139 a = g_utf8_next_char (a);
140 b = g_utf8_next_char (b);
143 return g_strndup (start, a - start);
147 load_basenames_data_free (LoadBasenamesData *data)
149 if (data->enumerator)
150 g_object_unref (data->enumerator);
152 g_object_unref (data->cancellable);
153 g_object_unref (data->dir);
155 g_list_free_full (data->basenames, g_free);
161 got_more_files (GObject *source_object,
165 LoadBasenamesData *data = user_data;
169 gboolean append_slash;
173 if (data->completer == NULL)
176 load_basenames_data_free (data);
180 infos = g_file_enumerator_next_files_finish (data->enumerator, res, NULL);
182 for (l = infos; l != NULL; l = l->next)
186 if (data->dirs_only &&
187 g_file_info_get_file_type (info) != G_FILE_TYPE_DIRECTORY)
189 g_object_unref (info);
193 append_slash = g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY;
194 name = g_file_info_get_name (info);
197 g_object_unref (info);
202 if (data->should_escape)
203 basename = g_uri_escape_string (name,
204 G_URI_RESERVED_CHARS_ALLOWED_IN_PATH,
207 /* If not should_escape, must be a local filename, convert to utf8 */
208 basename = g_filename_to_utf8 (name, -1, NULL, NULL, NULL);
215 basename = g_strconcat (basename, "/", NULL);
219 data->basenames = g_list_prepend (data->basenames, basename);
222 g_object_unref (info);
229 /* Not last, get more files */
230 g_file_enumerator_next_files_async (data->enumerator,
234 got_more_files, data);
238 data->completer->basename_loader = NULL;
240 if (data->completer->basenames_dir)
241 g_object_unref (data->completer->basenames_dir);
242 g_list_free_full (data->completer->basenames, g_free);
244 data->completer->basenames_dir = g_object_ref (data->dir);
245 data->completer->basenames = data->basenames;
246 data->completer->basenames_are_escaped = data->should_escape;
247 data->basenames = NULL;
249 g_file_enumerator_close_async (data->enumerator, 0, NULL, NULL, NULL);
251 g_signal_emit (data->completer, signals[GOT_COMPLETION_DATA], 0);
252 load_basenames_data_free (data);
258 got_enum (GObject *source_object,
262 LoadBasenamesData *data = user_data;
264 if (data->completer == NULL)
267 load_basenames_data_free (data);
271 data->enumerator = g_file_enumerate_children_finish (G_FILE (source_object), res, NULL);
273 if (data->enumerator == NULL)
275 data->completer->basename_loader = NULL;
277 if (data->completer->basenames_dir)
278 g_object_unref (data->completer->basenames_dir);
279 g_list_free_full (data->completer->basenames, g_free);
281 /* Mark uptodate with no basenames */
282 data->completer->basenames_dir = g_object_ref (data->dir);
283 data->completer->basenames = NULL;
284 data->completer->basenames_are_escaped = data->should_escape;
286 load_basenames_data_free (data);
290 g_file_enumerator_next_files_async (data->enumerator,
294 got_more_files, data);
298 schedule_load_basenames (GFilenameCompleter *completer,
300 gboolean should_escape)
302 LoadBasenamesData *data;
304 cancel_load_basenames (completer);
306 data = g_new0 (LoadBasenamesData, 1);
307 data->completer = completer;
308 data->cancellable = g_cancellable_new ();
309 data->dir = g_object_ref (dir);
310 data->should_escape = should_escape;
311 data->dirs_only = completer->dirs_only;
313 completer->basename_loader = data;
315 g_file_enumerate_children_async (dir,
316 G_FILE_ATTRIBUTE_STANDARD_NAME "," G_FILE_ATTRIBUTE_STANDARD_TYPE,
323 cancel_load_basenames (GFilenameCompleter *completer)
325 LoadBasenamesData *loader;
327 if (completer->basename_loader)
329 loader = completer->basename_loader;
330 loader->completer = NULL;
332 g_cancellable_cancel (loader->cancellable);
334 completer->basename_loader = NULL;
339 /* Returns a list of possible matches and the basename to use for it */
341 init_completion (GFilenameCompleter *completer,
342 const char *initial_text,
345 gboolean should_escape;
346 GFile *file, *parent;
351 *basename_out = NULL;
353 should_escape = ! (g_path_is_absolute (initial_text) || *initial_text == '~');
355 len = strlen (initial_text);
358 initial_text[len - 1] == '/')
361 file = g_file_parse_name (initial_text);
362 parent = g_file_get_parent (file);
365 g_object_unref (file);
369 if (completer->basenames_dir == NULL ||
370 completer->basenames_are_escaped != should_escape ||
371 !g_file_equal (parent, completer->basenames_dir))
373 schedule_load_basenames (completer, parent, should_escape);
374 g_object_unref (file);
378 basename = g_file_get_basename (file);
382 basename = g_uri_escape_string (basename, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, TRUE);
388 basename = g_filename_to_utf8 (basename, -1, NULL, NULL, NULL);
391 if (basename == NULL)
395 *basename_out = basename;
397 return completer->basenames;
401 * g_filename_completer_get_completion_suffix:
402 * @completer: the filename completer.
403 * @initial_text: text to be completed.
405 * Obtains a completion for @initial_text from @completer.
407 * Returns: a completed string, or %NULL if no completion exists.
408 * This string is not owned by GIO, so remember to g_free() it
412 g_filename_completer_get_completion_suffix (GFilenameCompleter *completer,
413 const char *initial_text)
415 GList *possible_matches, *l;
418 char *possible_match;
421 g_return_val_if_fail (G_IS_FILENAME_COMPLETER (completer), NULL);
422 g_return_val_if_fail (initial_text != NULL, NULL);
424 possible_matches = init_completion (completer, initial_text, &prefix);
428 for (l = possible_matches; l != NULL; l = l->next)
430 possible_match = l->data;
432 if (g_str_has_prefix (possible_match, prefix))
435 suffix = g_strdup (possible_match + strlen (prefix));
438 lcp = longest_common_prefix (suffix,
439 possible_match + strlen (prefix));
455 * g_filename_completer_get_completions:
456 * @completer: the filename completer.
457 * @initial_text: text to be completed.
459 * Gets an array of completion strings for a given initial text.
461 * Returns: (array zero-terminated=1) (transfer full): array of strings with possible completions for @initial_text.
462 * This array must be freed by g_strfreev() when finished.
465 g_filename_completer_get_completions (GFilenameCompleter *completer,
466 const char *initial_text)
468 GList *possible_matches, *l;
470 char *possible_match;
473 g_return_val_if_fail (G_IS_FILENAME_COMPLETER (completer), NULL);
474 g_return_val_if_fail (initial_text != NULL, NULL);
476 possible_matches = init_completion (completer, initial_text, &prefix);
478 res = g_ptr_array_new ();
479 for (l = possible_matches; l != NULL; l = l->next)
481 possible_match = l->data;
483 if (g_str_has_prefix (possible_match, prefix))
484 g_ptr_array_add (res,
485 g_strconcat (initial_text, possible_match + strlen (prefix), NULL));
490 g_ptr_array_add (res, NULL);
492 return (char**)g_ptr_array_free (res, FALSE);
496 * g_filename_completer_set_dirs_only:
497 * @completer: the filename completer.
498 * @dirs_only: a #gboolean.
500 * If @dirs_only is %TRUE, @completer will only
501 * complete directory names, and not file names.
504 g_filename_completer_set_dirs_only (GFilenameCompleter *completer,
507 g_return_if_fail (G_IS_FILENAME_COMPLETER (completer));
509 completer->dirs_only = dirs_only;