Fix up includes in section docs
[platform/upstream/glib.git] / gio / gfilenamecompleter.c
1 /* GIO - GLib Input, Output and Streaming Library
2  * 
3  * Copyright (C) 2006-2007 Red Hat, Inc.
4  *
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.
9  *
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.
14  *
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.
19  *
20  * Author: Alexander Larsson <alexl@redhat.com>
21  */
22
23 #include <config.h>
24 #include "gfilenamecompleter.h"
25 #include "gurifuncs.h"
26 #include "gfile.h"
27 #include <string.h>
28 #include "glibintl.h"
29
30 #include "gioalias.h"
31
32 /**
33  * SECTION:gfilenamecompleter
34  * @short_description: Filename Completer
35  * @include: gio.h
36  * 
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 implementation.
40  * 
41  **/
42
43 enum {
44   GOT_COMPLETION_DATA,
45   LAST_SIGNAL
46 };
47
48 static guint signals[LAST_SIGNAL] = { 0 };
49
50 typedef struct {
51   GFilenameCompleter *completer;
52   GFileEnumerator *enumerator;
53   GCancellable *cancellable;
54   gboolean should_escape;
55   GFile *dir;
56   GList *basenames;
57   gboolean dirs_only;
58 } LoadBasenamesData;
59
60 struct _GFilenameCompleter {
61   GObject parent;
62
63   GFile *basenames_dir;
64   gboolean basenames_are_escaped;
65   GList *basenames;
66   gboolean dirs_only;
67
68   LoadBasenamesData *basename_loader;
69 };
70
71 G_DEFINE_TYPE (GFilenameCompleter, g_filename_completer, G_TYPE_OBJECT);
72
73 static void cancel_load_basenames (GFilenameCompleter *completer);
74
75 static void
76 g_filename_completer_finalize (GObject *object)
77 {
78   GFilenameCompleter *completer;
79
80   completer = G_FILENAME_COMPLETER (object);
81
82   cancel_load_basenames (completer);
83
84   if (completer->basenames_dir)
85     g_object_unref (completer->basenames_dir);
86
87   g_list_foreach (completer->basenames, (GFunc)g_free, NULL);
88   g_list_free (completer->basenames);
89   
90   if (G_OBJECT_CLASS (g_filename_completer_parent_class)->finalize)
91     (*G_OBJECT_CLASS (g_filename_completer_parent_class)->finalize) (object);
92 }
93
94 static void
95 g_filename_completer_class_init (GFilenameCompleterClass *klass)
96 {
97   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
98   
99   gobject_class->finalize = g_filename_completer_finalize;
100   /**
101    * GFilenameCompleter::got-completion-data:
102    * 
103    * Emitted when the file name completion information comes available.
104    **/
105   signals[GOT_COMPLETION_DATA] = g_signal_new (I_("got_completion_data"),
106                                           G_TYPE_FILENAME_COMPLETER,
107                                           G_SIGNAL_RUN_LAST,
108                                           G_STRUCT_OFFSET (GFilenameCompleterClass, got_completion_data),
109                                           NULL, NULL,
110                                           g_cclosure_marshal_VOID__VOID,
111                                           G_TYPE_NONE, 0);
112 }
113
114 static void
115 g_filename_completer_init (GFilenameCompleter *completer)
116 {
117 }
118
119 /**
120  * g_filename_completer_new:
121  * 
122  * Creates a new filename completer.
123  * 
124  * Returns: a #GFilenameCompleter.
125  **/
126 GFilenameCompleter *
127 g_filename_completer_new (void)
128 {
129   return g_object_new (G_TYPE_FILENAME_COMPLETER, NULL);
130 }
131
132 static char *
133 longest_common_prefix (char *a, char *b)
134 {
135   char *start;
136
137   start = a;
138
139   while (g_utf8_get_char (a) == g_utf8_get_char (b))
140     {
141       a = g_utf8_next_char (a);
142       b = g_utf8_next_char (b);
143     }
144
145   return g_strndup (start, a - start);
146 }
147
148 static void
149 load_basenames_data_free (LoadBasenamesData *data)
150 {
151   if (data->enumerator)
152     g_object_unref (data->enumerator);
153   
154   g_object_unref (data->cancellable);
155   g_object_unref (data->dir);
156   
157   g_list_foreach (data->basenames, (GFunc)g_free, NULL);
158   g_list_free (data->basenames);
159   
160   g_free (data);
161 }
162
163 static void
164 got_more_files (GObject *source_object,
165                 GAsyncResult *res,
166                 gpointer user_data)
167 {
168   LoadBasenamesData *data = user_data;
169   GList *infos, *l;
170   GFileInfo *info;
171   const char *name;
172   gboolean append_slash;
173   char *t;
174   char *basename;
175
176   if (data->completer == NULL)
177     {
178       /* Was cancelled */
179       load_basenames_data_free (data);
180       return;
181     }
182
183   infos = g_file_enumerator_next_files_finish (data->enumerator, res, NULL);
184
185   for (l = infos; l != NULL; l = l->next)
186     {
187       info = l->data;
188
189       if (data->dirs_only &&
190           g_file_info_get_file_type (info) != G_FILE_TYPE_DIRECTORY)
191         {
192           g_object_unref (info);
193           continue;
194         }
195       
196       append_slash = g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY;
197       name = g_file_info_get_name (info);
198       if (name == NULL)
199         {
200           g_object_unref (info);
201           continue;
202         }
203
204       
205       if (data->should_escape)
206         basename = g_uri_escape_string (name,
207                                         G_URI_RESERVED_CHARS_ALLOWED_IN_PATH,
208                                         TRUE);
209       else
210         /* If not should_escape, must be a local filename, convert to utf8 */
211         basename = g_filename_to_utf8 (name, -1, NULL, NULL, NULL);
212       
213       if (basename)
214         {
215           if (append_slash)
216             {
217               t = basename;
218               basename = g_strconcat (basename, "/", NULL);
219               g_free (t);
220             }
221           
222           data->basenames = g_list_prepend (data->basenames, basename);
223         }
224       
225       g_object_unref (info);
226     }
227   
228   g_list_free (infos);
229   
230   if (infos)
231     {
232       /* Not last, get more files */
233       g_file_enumerator_next_files_async (data->enumerator,
234                                           100,
235                                           0,
236                                           data->cancellable,
237                                           got_more_files, data);
238     }
239   else
240     {
241       data->completer->basename_loader = NULL;
242       
243       if (data->completer->basenames_dir)
244         g_object_unref (data->completer->basenames_dir);
245       g_list_foreach (data->completer->basenames, (GFunc)g_free, NULL);
246       g_list_free (data->completer->basenames);
247       
248       data->completer->basenames_dir = g_object_ref (data->dir);
249       data->completer->basenames = data->basenames;
250       data->completer->basenames_are_escaped = data->should_escape;
251       data->basenames = NULL;
252       
253       g_file_enumerator_close_async (data->enumerator, 0, NULL, NULL, NULL);
254
255       g_signal_emit (data->completer, signals[GOT_COMPLETION_DATA], 0);
256       load_basenames_data_free (data);
257     }
258 }
259
260
261 static void
262 got_enum (GObject *source_object,
263           GAsyncResult *res,
264           gpointer user_data)
265 {
266   LoadBasenamesData *data = user_data;
267
268   if (data->completer == NULL)
269     {
270       /* Was cancelled */
271       load_basenames_data_free (data);
272       return;
273     }
274   
275   data->enumerator = g_file_enumerate_children_finish (G_FILE (source_object), res, NULL);
276   
277   if (data->enumerator == NULL)
278     {
279       data->completer->basename_loader = NULL;
280
281       if (data->completer->basenames_dir)
282         g_object_unref (data->completer->basenames_dir);
283       g_list_foreach (data->completer->basenames, (GFunc)g_free, NULL);
284       g_list_free (data->completer->basenames);
285
286       /* Mark uptodate with no basenames */
287       data->completer->basenames_dir = g_object_ref (data->dir);
288       data->completer->basenames = NULL;
289       data->completer->basenames_are_escaped = data->should_escape;
290       
291       load_basenames_data_free (data);
292       return;
293     }
294   
295   g_file_enumerator_next_files_async (data->enumerator,
296                                       100,
297                                       0,
298                                       data->cancellable,
299                                       got_more_files, data);
300 }
301
302 static void
303 schedule_load_basenames (GFilenameCompleter *completer,
304                          GFile *dir,
305                          gboolean should_escape)
306 {
307   LoadBasenamesData *data;
308
309   cancel_load_basenames (completer);
310
311   data = g_new0 (LoadBasenamesData, 1);
312   data->completer = completer;
313   data->cancellable = g_cancellable_new ();
314   data->dir = g_object_ref (dir);
315   data->should_escape = should_escape;
316   data->dirs_only = completer->dirs_only;
317
318   completer->basename_loader = data;
319   
320   g_file_enumerate_children_async (dir,
321                                    G_FILE_ATTRIBUTE_STD_NAME "," G_FILE_ATTRIBUTE_STD_TYPE,
322                                    0, 0,
323                                    data->cancellable,
324                                    got_enum, data);
325 }
326
327 static void
328 cancel_load_basenames (GFilenameCompleter *completer)
329 {
330   LoadBasenamesData *loader;
331   
332   if (completer->basename_loader)
333     {
334       loader = completer->basename_loader; 
335       loader->completer = NULL;
336       
337       g_cancellable_cancel (loader->cancellable);
338       
339       completer->basename_loader = NULL;
340     }
341 }
342
343
344 /* Returns a list of possible matches and the basename to use for it */
345 static GList *
346 init_completion (GFilenameCompleter *completer,
347                  const char *initial_text,
348                  char **basename_out)
349 {
350   gboolean should_escape;
351   GFile *file, *parent;
352   char *basename;
353   char *t;
354   int len;
355
356   *basename_out = NULL;
357   
358   should_escape = ! (g_path_is_absolute (initial_text) || *initial_text == '~');
359
360   len = strlen (initial_text);
361   
362   if (len > 0 &&
363       initial_text[len - 1] == '/')
364     return NULL;
365   
366   file = g_file_parse_name (initial_text);
367   parent = g_file_get_parent (file);
368   if (parent == NULL)
369     {
370       g_object_unref (file);
371       return NULL;
372     }
373
374   if (completer->basenames_dir == NULL ||
375       completer->basenames_are_escaped != should_escape ||
376       !g_file_equal (parent, completer->basenames_dir))
377     {
378       schedule_load_basenames (completer, parent, should_escape);
379       g_object_unref (file);
380       return NULL;
381     }
382   
383   basename = g_file_get_basename (file);
384   if (should_escape)
385     {
386       t = basename;
387       basename = g_uri_escape_string (basename, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, TRUE);
388       g_free (t);
389     }
390   else
391     {
392       t = basename;
393       basename = g_filename_to_utf8 (basename, -1, NULL, NULL, NULL);
394       g_free (t);
395       
396       if (basename == NULL)
397         return NULL;
398     }
399
400   *basename_out = basename;
401
402   return completer->basenames;
403 }
404
405 /**
406  * g_filename_completer_get_completion_suffix:
407  * @completer: the filename completer.
408  * @initial_text: text to be completed.
409  *
410  * Obtains a completion for @initial_text from @completer.
411  *  
412  * Returns: a completed string, or %NULL if no completion exists. 
413  *     This string is not owned by GIO, so remember to g_free() it 
414  *     when finished.
415  **/
416 char *
417 g_filename_completer_get_completion_suffix (GFilenameCompleter *completer,
418                                             const char *initial_text)
419 {
420   GList *possible_matches, *l;
421   char *prefix;
422   char *suffix;
423   char *possible_match;
424   char *lcp;
425
426   g_return_val_if_fail (G_IS_FILENAME_COMPLETER (completer), NULL);
427   g_return_val_if_fail (initial_text != NULL, NULL);
428
429   possible_matches = init_completion (completer, initial_text, &prefix);
430
431   suffix = NULL;
432   
433   for (l = possible_matches; l != NULL; l = l->next)
434     {
435       possible_match = l->data;
436       
437       if (g_str_has_prefix (possible_match, prefix))
438         {
439           if (suffix == NULL)
440             suffix = g_strdup (possible_match + strlen (prefix));
441           else
442             {
443               lcp = longest_common_prefix (suffix,
444                                            possible_match + strlen (prefix));
445               g_free (suffix);
446               suffix = lcp;
447               
448               if (*suffix == 0)
449                 break;
450             }
451         }
452     }
453
454   g_free (prefix);
455   
456   return suffix;
457 }
458
459 /**
460  * g_filename_completer_get_completions:
461  * @completer: the filename completer.
462  * @initial_text: text to be completed.
463  * 
464  * Gets an array of completion strings for a given initial text.
465  * 
466  * Returns: array of strings with possible completions for @initial_text.
467  * This array must be freed by g_strfreev() when finished. 
468  **/
469 char **
470 g_filename_completer_get_completions (GFilenameCompleter *completer,
471                                       const char *initial_text)
472 {
473   GList *possible_matches, *l;
474   char *prefix;
475   char *possible_match;
476   GPtrArray *res;
477
478   g_return_val_if_fail (G_IS_FILENAME_COMPLETER (completer), NULL);
479   g_return_val_if_fail (initial_text != NULL, NULL);
480
481   possible_matches = init_completion (completer, initial_text, &prefix);
482
483   res = g_ptr_array_new ();
484   for (l = possible_matches; l != NULL; l = l->next)
485     {
486       possible_match = l->data;
487       
488       if (g_str_has_prefix (possible_match, prefix))
489         g_ptr_array_add (res,
490                          g_strconcat (initial_text, possible_match + strlen (prefix), NULL));
491     }
492
493   g_free (prefix);
494   
495   return (char**)g_ptr_array_free (res, FALSE);
496 }
497
498 /**
499  * g_filename_completer_set_dirs_only:
500  * @completer: the filename completer.
501  * @dirs_only: a #gboolean.
502  * 
503  * If @dirs_only is %TRUE, @completer will only 
504  * complete directory names, and not file names.
505  **/
506 void
507 g_filename_completer_set_dirs_only (GFilenameCompleter *completer,
508                                     gboolean dirs_only)
509 {
510   g_return_if_fail (G_IS_FILENAME_COMPLETER (completer));
511
512   completer->dirs_only = dirs_only;
513 }
514
515 #define __G_FILENAME_COMPLETER_C__
516 #include "gioaliasdef.c"