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, 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>
24 #include "gfilenamecompleter.h"
25 #include "gurifuncs.h"
35 static guint signals[LAST_SIGNAL] = { 0 };
38 GFilenameCompleter *completer;
39 GFileEnumerator *enumerator;
40 GCancellable *cancellable;
41 gboolean should_escape;
47 struct _GFilenameCompleter {
51 gboolean basenames_are_escaped;
55 LoadBasenamesData *basename_loader;
58 G_DEFINE_TYPE (GFilenameCompleter, g_filename_completer, G_TYPE_OBJECT);
60 static void cancel_load_basenames (GFilenameCompleter *completer);
63 g_filename_completer_finalize (GObject *object)
65 GFilenameCompleter *completer;
67 completer = G_FILENAME_COMPLETER (object);
69 cancel_load_basenames (completer);
71 if (completer->basenames_dir)
72 g_object_unref (completer->basenames_dir);
74 g_list_foreach (completer->basenames, (GFunc)g_free, NULL);
75 g_list_free (completer->basenames);
77 if (G_OBJECT_CLASS (g_filename_completer_parent_class)->finalize)
78 (*G_OBJECT_CLASS (g_filename_completer_parent_class)->finalize) (object);
82 g_filename_completer_class_init (GFilenameCompleterClass *klass)
84 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
86 gobject_class->finalize = g_filename_completer_finalize;
88 signals[GOT_COMPLETION_DATA] = g_signal_new (I_("got_completion_data"),
89 G_TYPE_FILENAME_COMPLETER,
91 G_STRUCT_OFFSET (GFilenameCompleterClass, got_completion_data),
93 g_cclosure_marshal_VOID__VOID,
98 g_filename_completer_init (GFilenameCompleter *completer)
103 * g_filename_completer_new:
105 * Returns: a new #GFilenameCompleter.
108 g_filename_completer_new (void)
110 return g_object_new (G_TYPE_FILENAME_COMPLETER, NULL);
114 longest_common_prefix (char *a, char *b)
120 while (g_utf8_get_char (a) == g_utf8_get_char (b))
122 a = g_utf8_next_char (a);
123 b = g_utf8_next_char (b);
126 return g_strndup (start, a - start);
130 load_basenames_data_free (LoadBasenamesData *data)
132 if (data->enumerator)
133 g_object_unref (data->enumerator);
135 g_object_unref (data->cancellable);
136 g_object_unref (data->dir);
138 g_list_foreach (data->basenames, (GFunc)g_free, NULL);
139 g_list_free (data->basenames);
145 got_more_files (GObject *source_object,
149 LoadBasenamesData *data = user_data;
153 gboolean append_slash;
157 if (data->completer == NULL)
160 load_basenames_data_free (data);
164 infos = g_file_enumerator_next_files_finish (data->enumerator, res, NULL);
166 for (l = infos; l != NULL; l = l->next)
170 if (data->dirs_only &&
171 g_file_info_get_file_type (info) != G_FILE_TYPE_DIRECTORY)
173 g_object_unref (info);
177 append_slash = g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY;
178 name = g_file_info_get_name (info);
181 g_object_unref (info);
186 if (data->should_escape)
187 basename = g_uri_escape_string (name,
188 G_URI_RESERVED_CHARS_ALLOWED_IN_PATH,
191 /* If not should_escape, must be a local filename, convert to utf8 */
192 basename = g_filename_to_utf8 (name, -1, NULL, NULL, NULL);
199 basename = g_strconcat (basename, "/", NULL);
203 data->basenames = g_list_prepend (data->basenames, basename);
206 g_object_unref (info);
213 /* Not last, get more files */
214 g_file_enumerator_next_files_async (data->enumerator,
218 got_more_files, data);
222 data->completer->basename_loader = NULL;
224 if (data->completer->basenames_dir)
225 g_object_unref (data->completer->basenames_dir);
226 g_list_foreach (data->completer->basenames, (GFunc)g_free, NULL);
227 g_list_free (data->completer->basenames);
229 data->completer->basenames_dir = g_object_ref (data->dir);
230 data->completer->basenames = data->basenames;
231 data->completer->basenames_are_escaped = data->should_escape;
232 data->basenames = NULL;
234 g_file_enumerator_close_async (data->enumerator, 0, NULL, NULL, NULL);
236 g_signal_emit (data->completer, signals[GOT_COMPLETION_DATA], 0);
237 load_basenames_data_free (data);
243 got_enum (GObject *source_object,
247 LoadBasenamesData *data = user_data;
249 if (data->completer == NULL)
252 load_basenames_data_free (data);
256 data->enumerator = g_file_enumerate_children_finish (G_FILE (source_object), res, NULL);
258 if (data->enumerator == NULL)
260 data->completer->basename_loader = NULL;
262 if (data->completer->basenames_dir)
263 g_object_unref (data->completer->basenames_dir);
264 g_list_foreach (data->completer->basenames, (GFunc)g_free, NULL);
265 g_list_free (data->completer->basenames);
267 /* Mark uptodate with no basenames */
268 data->completer->basenames_dir = g_object_ref (data->dir);
269 data->completer->basenames = NULL;
270 data->completer->basenames_are_escaped = data->should_escape;
272 load_basenames_data_free (data);
276 g_file_enumerator_next_files_async (data->enumerator,
280 got_more_files, data);
284 schedule_load_basenames (GFilenameCompleter *completer,
286 gboolean should_escape)
288 LoadBasenamesData *data;
290 cancel_load_basenames (completer);
292 data = g_new0 (LoadBasenamesData, 1);
293 data->completer = completer;
294 data->cancellable = g_cancellable_new ();
295 data->dir = g_object_ref (dir);
296 data->should_escape = should_escape;
297 data->dirs_only = completer->dirs_only;
299 completer->basename_loader = data;
301 g_file_enumerate_children_async (dir,
302 G_FILE_ATTRIBUTE_STD_NAME "," G_FILE_ATTRIBUTE_STD_TYPE,
309 cancel_load_basenames (GFilenameCompleter *completer)
311 LoadBasenamesData *loader;
313 if (completer->basename_loader)
315 loader = completer->basename_loader;
316 loader->completer = NULL;
318 g_cancellable_cancel (loader->cancellable);
320 completer->basename_loader = NULL;
325 /* Returns a list of possible matches and the basename to use for it */
327 init_completion (GFilenameCompleter *completer,
328 const char *initial_text,
331 gboolean should_escape;
332 GFile *file, *parent;
337 *basename_out = NULL;
339 should_escape = ! (g_path_is_absolute (initial_text) || *initial_text == '~');
341 len = strlen (initial_text);
344 initial_text[len - 1] == '/')
347 file = g_file_parse_name (initial_text);
348 parent = g_file_get_parent (file);
351 g_object_unref (file);
355 if (completer->basenames_dir == NULL ||
356 completer->basenames_are_escaped != should_escape ||
357 !g_file_equal (parent, completer->basenames_dir))
359 schedule_load_basenames (completer, parent, should_escape);
360 g_object_unref (file);
364 basename = g_file_get_basename (file);
368 basename = g_uri_escape_string (basename, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, TRUE);
374 basename = g_filename_to_utf8 (basename, -1, NULL, NULL, NULL);
377 if (basename == NULL)
381 *basename_out = basename;
383 return completer->basenames;
387 * g_filename_completer_get_completion_suffix:
388 * @completer: the filename completer.
389 * @initial_text: text to be completed.
391 * Returns: a completed string. This string is not owned by GIO, so
392 * remember to g_free() it when finished.
395 g_filename_completer_get_completion_suffix (GFilenameCompleter *completer,
396 const char *initial_text)
398 GList *possible_matches, *l;
401 char *possible_match;
404 g_return_val_if_fail (G_IS_FILENAME_COMPLETER (completer), NULL);
405 g_return_val_if_fail (initial_text != NULL, NULL);
407 possible_matches = init_completion (completer, initial_text, &prefix);
411 for (l = possible_matches; l != NULL; l = l->next)
413 possible_match = l->data;
415 if (g_str_has_prefix (possible_match, prefix))
418 suffix = g_strdup (possible_match + strlen (prefix));
421 lcp = longest_common_prefix (suffix,
422 possible_match + strlen (prefix));
438 * g_filename_completer_get_completions:
439 * @completer: the filename completer.
440 * @initial_text: text to be completed.
442 * Returns: array of strings with possible completions for @initial_text.
443 * This array must be freed by g_strfreev() when finished.
446 g_filename_completer_get_completions (GFilenameCompleter *completer,
447 const char *initial_text)
449 GList *possible_matches, *l;
451 char *possible_match;
454 g_return_val_if_fail (G_IS_FILENAME_COMPLETER (completer), NULL);
455 g_return_val_if_fail (initial_text != NULL, NULL);
457 possible_matches = init_completion (completer, initial_text, &prefix);
459 res = g_ptr_array_new ();
460 for (l = possible_matches; l != NULL; l = l->next)
462 possible_match = l->data;
464 if (g_str_has_prefix (possible_match, prefix))
465 g_ptr_array_add (res,
466 g_strconcat (initial_text, possible_match + strlen (prefix), NULL));
471 return (char**)g_ptr_array_free (res, FALSE);
475 * g_filename_completer_set_dirs_only:
476 * @completer: the filename completer.
479 * If @dirs_only is %TRUE, @completer will only
480 * complete directory names, and not file names.
483 g_filename_completer_set_dirs_only (GFilenameCompleter *completer,
486 g_return_if_fail (G_IS_FILENAME_COMPLETER (completer));
488 completer->dirs_only = dirs_only;