nexmon – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 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, see <http://www.gnu.org/licenses/>. |
||
17 | * |
||
18 | * Author: Alexander Larsson <alexl@redhat.com> |
||
19 | */ |
||
20 | |||
21 | #include "config.h" |
||
22 | #include "gfilenamecompleter.h" |
||
23 | #include "gfileenumerator.h" |
||
24 | #include "gfileattribute.h" |
||
25 | #include "gfile.h" |
||
26 | #include "gfileinfo.h" |
||
27 | #include "gcancellable.h" |
||
28 | #include <string.h> |
||
29 | #include "glibintl.h" |
||
30 | |||
31 | |||
32 | /** |
||
33 | * SECTION:gfilenamecompleter |
||
34 | * @short_description: Filename Completer |
||
35 | * @include: gio/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 implementations. |
||
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 | gboolean dirs_only; |
||
66 | GList *basenames; |
||
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_free_full (completer->basenames, g_free); |
||
88 | |||
89 | G_OBJECT_CLASS (g_filename_completer_parent_class)->finalize (object); |
||
90 | } |
||
91 | |||
92 | static void |
||
93 | g_filename_completer_class_init (GFilenameCompleterClass *klass) |
||
94 | { |
||
95 | GObjectClass *gobject_class = G_OBJECT_CLASS (klass); |
||
96 | |||
97 | gobject_class->finalize = g_filename_completer_finalize; |
||
98 | /** |
||
99 | * GFilenameCompleter::got-completion-data: |
||
100 | * |
||
101 | * Emitted when the file name completion information comes available. |
||
102 | **/ |
||
103 | signals[GOT_COMPLETION_DATA] = g_signal_new (I_("got-completion-data"), |
||
104 | G_TYPE_FILENAME_COMPLETER, |
||
105 | G_SIGNAL_RUN_LAST, |
||
106 | G_STRUCT_OFFSET (GFilenameCompleterClass, got_completion_data), |
||
107 | NULL, NULL, |
||
108 | g_cclosure_marshal_VOID__VOID, |
||
109 | G_TYPE_NONE, 0); |
||
110 | } |
||
111 | |||
112 | static void |
||
113 | g_filename_completer_init (GFilenameCompleter *completer) |
||
114 | { |
||
115 | } |
||
116 | |||
117 | /** |
||
118 | * g_filename_completer_new: |
||
119 | * |
||
120 | * Creates a new filename completer. |
||
121 | * |
||
122 | * Returns: a #GFilenameCompleter. |
||
123 | **/ |
||
124 | GFilenameCompleter * |
||
125 | g_filename_completer_new (void) |
||
126 | { |
||
127 | return g_object_new (G_TYPE_FILENAME_COMPLETER, NULL); |
||
128 | } |
||
129 | |||
130 | static char * |
||
131 | longest_common_prefix (char *a, char *b) |
||
132 | { |
||
133 | char *start; |
||
134 | |||
135 | start = a; |
||
136 | |||
137 | while (g_utf8_get_char (a) == g_utf8_get_char (b)) |
||
138 | { |
||
139 | a = g_utf8_next_char (a); |
||
140 | b = g_utf8_next_char (b); |
||
141 | } |
||
142 | |||
143 | return g_strndup (start, a - start); |
||
144 | } |
||
145 | |||
146 | static void |
||
147 | load_basenames_data_free (LoadBasenamesData *data) |
||
148 | { |
||
149 | if (data->enumerator) |
||
150 | g_object_unref (data->enumerator); |
||
151 | |||
152 | g_object_unref (data->cancellable); |
||
153 | g_object_unref (data->dir); |
||
154 | |||
155 | g_list_free_full (data->basenames, g_free); |
||
156 | |||
157 | g_free (data); |
||
158 | } |
||
159 | |||
160 | static void |
||
161 | got_more_files (GObject *source_object, |
||
162 | GAsyncResult *res, |
||
163 | gpointer user_data) |
||
164 | { |
||
165 | LoadBasenamesData *data = user_data; |
||
166 | GList *infos, *l; |
||
167 | GFileInfo *info; |
||
168 | const char *name; |
||
169 | gboolean append_slash; |
||
170 | char *t; |
||
171 | char *basename; |
||
172 | |||
173 | if (data->completer == NULL) |
||
174 | { |
||
175 | /* Was cancelled */ |
||
176 | load_basenames_data_free (data); |
||
177 | return; |
||
178 | } |
||
179 | |||
180 | infos = g_file_enumerator_next_files_finish (data->enumerator, res, NULL); |
||
181 | |||
182 | for (l = infos; l != NULL; l = l->next) |
||
183 | { |
||
184 | info = l->data; |
||
185 | |||
186 | if (data->dirs_only && |
||
187 | g_file_info_get_file_type (info) != G_FILE_TYPE_DIRECTORY) |
||
188 | { |
||
189 | g_object_unref (info); |
||
190 | continue; |
||
191 | } |
||
192 | |||
193 | append_slash = g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY; |
||
194 | name = g_file_info_get_name (info); |
||
195 | if (name == NULL) |
||
196 | { |
||
197 | g_object_unref (info); |
||
198 | continue; |
||
199 | } |
||
200 | |||
201 | |||
202 | if (data->should_escape) |
||
203 | basename = g_uri_escape_string (name, |
||
204 | G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, |
||
205 | TRUE); |
||
206 | else |
||
207 | /* If not should_escape, must be a local filename, convert to utf8 */ |
||
208 | basename = g_filename_to_utf8 (name, -1, NULL, NULL, NULL); |
||
209 | |||
210 | if (basename) |
||
211 | { |
||
212 | if (append_slash) |
||
213 | { |
||
214 | t = basename; |
||
215 | basename = g_strconcat (basename, "/", NULL); |
||
216 | g_free (t); |
||
217 | } |
||
218 | |||
219 | data->basenames = g_list_prepend (data->basenames, basename); |
||
220 | } |
||
221 | |||
222 | g_object_unref (info); |
||
223 | } |
||
224 | |||
225 | g_list_free (infos); |
||
226 | |||
227 | if (infos) |
||
228 | { |
||
229 | /* Not last, get more files */ |
||
230 | g_file_enumerator_next_files_async (data->enumerator, |
||
231 | 100, |
||
232 | 0, |
||
233 | data->cancellable, |
||
234 | got_more_files, data); |
||
235 | } |
||
236 | else |
||
237 | { |
||
238 | data->completer->basename_loader = NULL; |
||
239 | |||
240 | if (data->completer->basenames_dir) |
||
241 | g_object_unref (data->completer->basenames_dir); |
||
242 | g_list_free_full (data->completer->basenames, g_free); |
||
243 | |||
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; |
||
248 | |||
249 | g_file_enumerator_close_async (data->enumerator, 0, NULL, NULL, NULL); |
||
250 | |||
251 | g_signal_emit (data->completer, signals[GOT_COMPLETION_DATA], 0); |
||
252 | load_basenames_data_free (data); |
||
253 | } |
||
254 | } |
||
255 | |||
256 | |||
257 | static void |
||
258 | got_enum (GObject *source_object, |
||
259 | GAsyncResult *res, |
||
260 | gpointer user_data) |
||
261 | { |
||
262 | LoadBasenamesData *data = user_data; |
||
263 | |||
264 | if (data->completer == NULL) |
||
265 | { |
||
266 | /* Was cancelled */ |
||
267 | load_basenames_data_free (data); |
||
268 | return; |
||
269 | } |
||
270 | |||
271 | data->enumerator = g_file_enumerate_children_finish (G_FILE (source_object), res, NULL); |
||
272 | |||
273 | if (data->enumerator == NULL) |
||
274 | { |
||
275 | data->completer->basename_loader = NULL; |
||
276 | |||
277 | if (data->completer->basenames_dir) |
||
278 | g_object_unref (data->completer->basenames_dir); |
||
279 | g_list_free_full (data->completer->basenames, g_free); |
||
280 | |||
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; |
||
285 | |||
286 | load_basenames_data_free (data); |
||
287 | return; |
||
288 | } |
||
289 | |||
290 | g_file_enumerator_next_files_async (data->enumerator, |
||
291 | 100, |
||
292 | 0, |
||
293 | data->cancellable, |
||
294 | got_more_files, data); |
||
295 | } |
||
296 | |||
297 | static void |
||
298 | schedule_load_basenames (GFilenameCompleter *completer, |
||
299 | GFile *dir, |
||
300 | gboolean should_escape) |
||
301 | { |
||
302 | LoadBasenamesData *data; |
||
303 | |||
304 | cancel_load_basenames (completer); |
||
305 | |||
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; |
||
312 | |||
313 | completer->basename_loader = data; |
||
314 | |||
315 | g_file_enumerate_children_async (dir, |
||
316 | G_FILE_ATTRIBUTE_STANDARD_NAME "," G_FILE_ATTRIBUTE_STANDARD_TYPE, |
||
317 | 0, 0, |
||
318 | data->cancellable, |
||
319 | got_enum, data); |
||
320 | } |
||
321 | |||
322 | static void |
||
323 | cancel_load_basenames (GFilenameCompleter *completer) |
||
324 | { |
||
325 | LoadBasenamesData *loader; |
||
326 | |||
327 | if (completer->basename_loader) |
||
328 | { |
||
329 | loader = completer->basename_loader; |
||
330 | loader->completer = NULL; |
||
331 | |||
332 | g_cancellable_cancel (loader->cancellable); |
||
333 | |||
334 | completer->basename_loader = NULL; |
||
335 | } |
||
336 | } |
||
337 | |||
338 | |||
339 | /* Returns a list of possible matches and the basename to use for it */ |
||
340 | static GList * |
||
341 | init_completion (GFilenameCompleter *completer, |
||
342 | const char *initial_text, |
||
343 | char **basename_out) |
||
344 | { |
||
345 | gboolean should_escape; |
||
346 | GFile *file, *parent; |
||
347 | char *basename; |
||
348 | char *t; |
||
349 | int len; |
||
350 | |||
351 | *basename_out = NULL; |
||
352 | |||
353 | should_escape = ! (g_path_is_absolute (initial_text) || *initial_text == '~'); |
||
354 | |||
355 | len = strlen (initial_text); |
||
356 | |||
357 | if (len > 0 && |
||
358 | initial_text[len - 1] == '/') |
||
359 | return NULL; |
||
360 | |||
361 | file = g_file_parse_name (initial_text); |
||
362 | parent = g_file_get_parent (file); |
||
363 | if (parent == NULL) |
||
364 | { |
||
365 | g_object_unref (file); |
||
366 | return NULL; |
||
367 | } |
||
368 | |||
369 | if (completer->basenames_dir == NULL || |
||
370 | completer->basenames_are_escaped != should_escape || |
||
371 | !g_file_equal (parent, completer->basenames_dir)) |
||
372 | { |
||
373 | schedule_load_basenames (completer, parent, should_escape); |
||
374 | g_object_unref (file); |
||
375 | return NULL; |
||
376 | } |
||
377 | |||
378 | basename = g_file_get_basename (file); |
||
379 | if (should_escape) |
||
380 | { |
||
381 | t = basename; |
||
382 | basename = g_uri_escape_string (basename, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, TRUE); |
||
383 | g_free (t); |
||
384 | } |
||
385 | else |
||
386 | { |
||
387 | t = basename; |
||
388 | basename = g_filename_to_utf8 (basename, -1, NULL, NULL, NULL); |
||
389 | g_free (t); |
||
390 | |||
391 | if (basename == NULL) |
||
392 | return NULL; |
||
393 | } |
||
394 | |||
395 | *basename_out = basename; |
||
396 | |||
397 | return completer->basenames; |
||
398 | } |
||
399 | |||
400 | /** |
||
401 | * g_filename_completer_get_completion_suffix: |
||
402 | * @completer: the filename completer. |
||
403 | * @initial_text: text to be completed. |
||
404 | * |
||
405 | * Obtains a completion for @initial_text from @completer. |
||
406 | * |
||
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 |
||
409 | * when finished. |
||
410 | **/ |
||
411 | char * |
||
412 | g_filename_completer_get_completion_suffix (GFilenameCompleter *completer, |
||
413 | const char *initial_text) |
||
414 | { |
||
415 | GList *possible_matches, *l; |
||
416 | char *prefix; |
||
417 | char *suffix; |
||
418 | char *possible_match; |
||
419 | char *lcp; |
||
420 | |||
421 | g_return_val_if_fail (G_IS_FILENAME_COMPLETER (completer), NULL); |
||
422 | g_return_val_if_fail (initial_text != NULL, NULL); |
||
423 | |||
424 | possible_matches = init_completion (completer, initial_text, &prefix); |
||
425 | |||
426 | suffix = NULL; |
||
427 | |||
428 | for (l = possible_matches; l != NULL; l = l->next) |
||
429 | { |
||
430 | possible_match = l->data; |
||
431 | |||
432 | if (g_str_has_prefix (possible_match, prefix)) |
||
433 | { |
||
434 | if (suffix == NULL) |
||
435 | suffix = g_strdup (possible_match + strlen (prefix)); |
||
436 | else |
||
437 | { |
||
438 | lcp = longest_common_prefix (suffix, |
||
439 | possible_match + strlen (prefix)); |
||
440 | g_free (suffix); |
||
441 | suffix = lcp; |
||
442 | |||
443 | if (*suffix == 0) |
||
444 | break; |
||
445 | } |
||
446 | } |
||
447 | } |
||
448 | |||
449 | g_free (prefix); |
||
450 | |||
451 | return suffix; |
||
452 | } |
||
453 | |||
454 | /** |
||
455 | * g_filename_completer_get_completions: |
||
456 | * @completer: the filename completer. |
||
457 | * @initial_text: text to be completed. |
||
458 | * |
||
459 | * Gets an array of completion strings for a given initial text. |
||
460 | * |
||
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. |
||
463 | **/ |
||
464 | char ** |
||
465 | g_filename_completer_get_completions (GFilenameCompleter *completer, |
||
466 | const char *initial_text) |
||
467 | { |
||
468 | GList *possible_matches, *l; |
||
469 | char *prefix; |
||
470 | char *possible_match; |
||
471 | GPtrArray *res; |
||
472 | |||
473 | g_return_val_if_fail (G_IS_FILENAME_COMPLETER (completer), NULL); |
||
474 | g_return_val_if_fail (initial_text != NULL, NULL); |
||
475 | |||
476 | possible_matches = init_completion (completer, initial_text, &prefix); |
||
477 | |||
478 | res = g_ptr_array_new (); |
||
479 | for (l = possible_matches; l != NULL; l = l->next) |
||
480 | { |
||
481 | possible_match = l->data; |
||
482 | |||
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)); |
||
486 | } |
||
487 | |||
488 | g_free (prefix); |
||
489 | |||
490 | g_ptr_array_add (res, NULL); |
||
491 | |||
492 | return (char**)g_ptr_array_free (res, FALSE); |
||
493 | } |
||
494 | |||
495 | /** |
||
496 | * g_filename_completer_set_dirs_only: |
||
497 | * @completer: the filename completer. |
||
498 | * @dirs_only: a #gboolean. |
||
499 | * |
||
500 | * If @dirs_only is %TRUE, @completer will only |
||
501 | * complete directory names, and not file names. |
||
502 | **/ |
||
503 | void |
||
504 | g_filename_completer_set_dirs_only (GFilenameCompleter *completer, |
||
505 | gboolean dirs_only) |
||
506 | { |
||
507 | g_return_if_fail (G_IS_FILENAME_COMPLETER (completer)); |
||
508 | |||
509 | completer->dirs_only = dirs_only; |
||
510 | } |