More clearly define 'named menu' in the XML parser
[glib.git] / gio / gfilenamecompleter.c
blobfc9afebf859790e4eb9c4f8294c0e58ac20764dd
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 "gfileenumerator.h"
26 #include "gfileattribute.h"
27 #include "gfile.h"
28 #include "gfileinfo.h"
29 #include "gcancellable.h"
30 #include <string.h>
31 #include "glibintl.h"
34 /**
35 * SECTION:gfilenamecompleter
36 * @short_description: Filename Completer
37 * @include: gio/gio.h
39 * Completes partial file and directory names given a partial string by
40 * looking in the file system for clues. Can return a list of possible
41 * completion strings for widget implementations.
43 **/
45 enum {
46 GOT_COMPLETION_DATA,
47 LAST_SIGNAL
50 static guint signals[LAST_SIGNAL] = { 0 };
52 typedef struct {
53 GFilenameCompleter *completer;
54 GFileEnumerator *enumerator;
55 GCancellable *cancellable;
56 gboolean should_escape;
57 GFile *dir;
58 GList *basenames;
59 gboolean dirs_only;
60 } LoadBasenamesData;
62 struct _GFilenameCompleter {
63 GObject parent;
65 GFile *basenames_dir;
66 gboolean basenames_are_escaped;
67 gboolean dirs_only;
68 GList *basenames;
70 LoadBasenamesData *basename_loader;
73 G_DEFINE_TYPE (GFilenameCompleter, g_filename_completer, G_TYPE_OBJECT);
75 static void cancel_load_basenames (GFilenameCompleter *completer);
77 static void
78 g_filename_completer_finalize (GObject *object)
80 GFilenameCompleter *completer;
82 completer = G_FILENAME_COMPLETER (object);
84 cancel_load_basenames (completer);
86 if (completer->basenames_dir)
87 g_object_unref (completer->basenames_dir);
89 g_list_foreach (completer->basenames, (GFunc)g_free, NULL);
90 g_list_free (completer->basenames);
92 G_OBJECT_CLASS (g_filename_completer_parent_class)->finalize (object);
95 static void
96 g_filename_completer_class_init (GFilenameCompleterClass *klass)
98 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
100 gobject_class->finalize = g_filename_completer_finalize;
102 * GFilenameCompleter::got-completion-data:
104 * Emitted when the file name completion information comes available.
106 signals[GOT_COMPLETION_DATA] = g_signal_new (I_("got-completion-data"),
107 G_TYPE_FILENAME_COMPLETER,
108 G_SIGNAL_RUN_LAST,
109 G_STRUCT_OFFSET (GFilenameCompleterClass, got_completion_data),
110 NULL, NULL,
111 g_cclosure_marshal_VOID__VOID,
112 G_TYPE_NONE, 0);
115 static void
116 g_filename_completer_init (GFilenameCompleter *completer)
121 * g_filename_completer_new:
123 * Creates a new filename completer.
125 * Returns: a #GFilenameCompleter.
127 GFilenameCompleter *
128 g_filename_completer_new (void)
130 return g_object_new (G_TYPE_FILENAME_COMPLETER, NULL);
133 static char *
134 longest_common_prefix (char *a, char *b)
136 char *start;
138 start = a;
140 while (g_utf8_get_char (a) == g_utf8_get_char (b))
142 a = g_utf8_next_char (a);
143 b = g_utf8_next_char (b);
146 return g_strndup (start, a - start);
149 static void
150 load_basenames_data_free (LoadBasenamesData *data)
152 if (data->enumerator)
153 g_object_unref (data->enumerator);
155 g_object_unref (data->cancellable);
156 g_object_unref (data->dir);
158 g_list_foreach (data->basenames, (GFunc)g_free, NULL);
159 g_list_free (data->basenames);
161 g_free (data);
164 static void
165 got_more_files (GObject *source_object,
166 GAsyncResult *res,
167 gpointer user_data)
169 LoadBasenamesData *data = user_data;
170 GList *infos, *l;
171 GFileInfo *info;
172 const char *name;
173 gboolean append_slash;
174 char *t;
175 char *basename;
177 if (data->completer == NULL)
179 /* Was cancelled */
180 load_basenames_data_free (data);
181 return;
184 infos = g_file_enumerator_next_files_finish (data->enumerator, res, NULL);
186 for (l = infos; l != NULL; l = l->next)
188 info = l->data;
190 if (data->dirs_only &&
191 g_file_info_get_file_type (info) != G_FILE_TYPE_DIRECTORY)
193 g_object_unref (info);
194 continue;
197 append_slash = g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY;
198 name = g_file_info_get_name (info);
199 if (name == NULL)
201 g_object_unref (info);
202 continue;
206 if (data->should_escape)
207 basename = g_uri_escape_string (name,
208 G_URI_RESERVED_CHARS_ALLOWED_IN_PATH,
209 TRUE);
210 else
211 /* If not should_escape, must be a local filename, convert to utf8 */
212 basename = g_filename_to_utf8 (name, -1, NULL, NULL, NULL);
214 if (basename)
216 if (append_slash)
218 t = basename;
219 basename = g_strconcat (basename, "/", NULL);
220 g_free (t);
223 data->basenames = g_list_prepend (data->basenames, basename);
226 g_object_unref (info);
229 g_list_free (infos);
231 if (infos)
233 /* Not last, get more files */
234 g_file_enumerator_next_files_async (data->enumerator,
235 100,
237 data->cancellable,
238 got_more_files, data);
240 else
242 data->completer->basename_loader = NULL;
244 if (data->completer->basenames_dir)
245 g_object_unref (data->completer->basenames_dir);
246 g_list_foreach (data->completer->basenames, (GFunc)g_free, NULL);
247 g_list_free (data->completer->basenames);
249 data->completer->basenames_dir = g_object_ref (data->dir);
250 data->completer->basenames = data->basenames;
251 data->completer->basenames_are_escaped = data->should_escape;
252 data->basenames = NULL;
254 g_file_enumerator_close_async (data->enumerator, 0, NULL, NULL, NULL);
256 g_signal_emit (data->completer, signals[GOT_COMPLETION_DATA], 0);
257 load_basenames_data_free (data);
262 static void
263 got_enum (GObject *source_object,
264 GAsyncResult *res,
265 gpointer user_data)
267 LoadBasenamesData *data = user_data;
269 if (data->completer == NULL)
271 /* Was cancelled */
272 load_basenames_data_free (data);
273 return;
276 data->enumerator = g_file_enumerate_children_finish (G_FILE (source_object), res, NULL);
278 if (data->enumerator == NULL)
280 data->completer->basename_loader = NULL;
282 if (data->completer->basenames_dir)
283 g_object_unref (data->completer->basenames_dir);
284 g_list_foreach (data->completer->basenames, (GFunc)g_free, NULL);
285 g_list_free (data->completer->basenames);
287 /* Mark uptodate with no basenames */
288 data->completer->basenames_dir = g_object_ref (data->dir);
289 data->completer->basenames = NULL;
290 data->completer->basenames_are_escaped = data->should_escape;
292 load_basenames_data_free (data);
293 return;
296 g_file_enumerator_next_files_async (data->enumerator,
297 100,
299 data->cancellable,
300 got_more_files, data);
303 static void
304 schedule_load_basenames (GFilenameCompleter *completer,
305 GFile *dir,
306 gboolean should_escape)
308 LoadBasenamesData *data;
310 cancel_load_basenames (completer);
312 data = g_new0 (LoadBasenamesData, 1);
313 data->completer = completer;
314 data->cancellable = g_cancellable_new ();
315 data->dir = g_object_ref (dir);
316 data->should_escape = should_escape;
317 data->dirs_only = completer->dirs_only;
319 completer->basename_loader = data;
321 g_file_enumerate_children_async (dir,
322 G_FILE_ATTRIBUTE_STANDARD_NAME "," G_FILE_ATTRIBUTE_STANDARD_TYPE,
323 0, 0,
324 data->cancellable,
325 got_enum, data);
328 static void
329 cancel_load_basenames (GFilenameCompleter *completer)
331 LoadBasenamesData *loader;
333 if (completer->basename_loader)
335 loader = completer->basename_loader;
336 loader->completer = NULL;
338 g_cancellable_cancel (loader->cancellable);
340 completer->basename_loader = NULL;
345 /* Returns a list of possible matches and the basename to use for it */
346 static GList *
347 init_completion (GFilenameCompleter *completer,
348 const char *initial_text,
349 char **basename_out)
351 gboolean should_escape;
352 GFile *file, *parent;
353 char *basename;
354 char *t;
355 int len;
357 *basename_out = NULL;
359 should_escape = ! (g_path_is_absolute (initial_text) || *initial_text == '~');
361 len = strlen (initial_text);
363 if (len > 0 &&
364 initial_text[len - 1] == '/')
365 return NULL;
367 file = g_file_parse_name (initial_text);
368 parent = g_file_get_parent (file);
369 if (parent == NULL)
371 g_object_unref (file);
372 return NULL;
375 if (completer->basenames_dir == NULL ||
376 completer->basenames_are_escaped != should_escape ||
377 !g_file_equal (parent, completer->basenames_dir))
379 schedule_load_basenames (completer, parent, should_escape);
380 g_object_unref (file);
381 return NULL;
384 basename = g_file_get_basename (file);
385 if (should_escape)
387 t = basename;
388 basename = g_uri_escape_string (basename, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, TRUE);
389 g_free (t);
391 else
393 t = basename;
394 basename = g_filename_to_utf8 (basename, -1, NULL, NULL, NULL);
395 g_free (t);
397 if (basename == NULL)
398 return NULL;
401 *basename_out = basename;
403 return completer->basenames;
407 * g_filename_completer_get_completion_suffix:
408 * @completer: the filename completer.
409 * @initial_text: text to be completed.
411 * Obtains a completion for @initial_text from @completer.
413 * Returns: a completed string, or %NULL if no completion exists.
414 * This string is not owned by GIO, so remember to g_free() it
415 * when finished.
417 char *
418 g_filename_completer_get_completion_suffix (GFilenameCompleter *completer,
419 const char *initial_text)
421 GList *possible_matches, *l;
422 char *prefix;
423 char *suffix;
424 char *possible_match;
425 char *lcp;
427 g_return_val_if_fail (G_IS_FILENAME_COMPLETER (completer), NULL);
428 g_return_val_if_fail (initial_text != NULL, NULL);
430 possible_matches = init_completion (completer, initial_text, &prefix);
432 suffix = NULL;
434 for (l = possible_matches; l != NULL; l = l->next)
436 possible_match = l->data;
438 if (g_str_has_prefix (possible_match, prefix))
440 if (suffix == NULL)
441 suffix = g_strdup (possible_match + strlen (prefix));
442 else
444 lcp = longest_common_prefix (suffix,
445 possible_match + strlen (prefix));
446 g_free (suffix);
447 suffix = lcp;
449 if (*suffix == 0)
450 break;
455 g_free (prefix);
457 return suffix;
461 * g_filename_completer_get_completions:
462 * @completer: the filename completer.
463 * @initial_text: text to be completed.
465 * Gets an array of completion strings for a given initial text.
467 * Returns: (array zero-terminated=1) (transfer full): array of strings with possible completions for @initial_text.
468 * This array must be freed by g_strfreev() when finished.
470 char **
471 g_filename_completer_get_completions (GFilenameCompleter *completer,
472 const char *initial_text)
474 GList *possible_matches, *l;
475 char *prefix;
476 char *possible_match;
477 GPtrArray *res;
479 g_return_val_if_fail (G_IS_FILENAME_COMPLETER (completer), NULL);
480 g_return_val_if_fail (initial_text != NULL, NULL);
482 possible_matches = init_completion (completer, initial_text, &prefix);
484 res = g_ptr_array_new ();
485 for (l = possible_matches; l != NULL; l = l->next)
487 possible_match = l->data;
489 if (g_str_has_prefix (possible_match, prefix))
490 g_ptr_array_add (res,
491 g_strconcat (initial_text, possible_match + strlen (prefix), NULL));
494 g_free (prefix);
496 g_ptr_array_add (res, NULL);
498 return (char**)g_ptr_array_free (res, FALSE);
502 * g_filename_completer_set_dirs_only:
503 * @completer: the filename completer.
504 * @dirs_only: a #gboolean.
506 * If @dirs_only is %TRUE, @completer will only
507 * complete directory names, and not file names.
509 void
510 g_filename_completer_set_dirs_only (GFilenameCompleter *completer,
511 gboolean dirs_only)
513 g_return_if_fail (G_IS_FILENAME_COMPLETER (completer));
515 completer->dirs_only = dirs_only;