* manuals/anjuta-manual/C/debugger.xml:
[anjuta-git-plugin.git] / libegg / egg-recent-files-module.c
blob3ea600e770d4162272915971b70a1118c9f171a0
1 /* egg-recent-files-module.c: GnomeVFS module to browse recent files list
3 * Copyright (C) 2003 Independent kids, Inc., Kristian Rietveld
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
10 * This program 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
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 #include <libgnomevfs/gnome-vfs-context.h>
21 #include <libgnomevfs/gnome-vfs-method.h>
22 #include <libgnomevfs/gnome-vfs-module.h>
23 #include <libgnomevfs/gnome-vfs-utils.h>
24 #include <libgnomevfs/gnome-vfs-ops.h>
25 #include <glib.h>
26 #include <string.h>
28 #include "egg-recent-model.h"
30 /* WARNING: there is some really evil code in here. Yes, I need to fix
31 * some of that code up someday
34 static gboolean utf8_string_match (const gchar *a,
35 const gchar *b);
37 /* our shared EggRecentModel */
38 static EggRecentModel *recent_model = NULL;
39 G_LOCK_DEFINE_STATIC (recent_model);
41 /* la la la ... evil monitoring code */
42 static GList *monitoring_handles = NULL;
43 static GList *current_files = NULL;
44 G_LOCK_DEFINE_STATIC (monitoring_lock);
47 static void
48 monitoring_add_handle (gpointer handle)
50 G_LOCK (monitoring_lock);
51 monitoring_handles = g_list_append (monitoring_handles, handle);
52 G_UNLOCK (monitoring_lock);
55 static void
56 monitoring_remove_handle (gpointer handle)
58 G_LOCK (monitoring_lock);
59 monitoring_handles = g_list_remove (monitoring_handles, handle);
60 G_UNLOCK (monitoring_lock);
63 /* called in monitoring_lock */
64 static void
65 monitoring_refresh_current_files (void)
67 GList *i;
68 GList *list;
70 list = egg_recent_model_get_list (recent_model);
72 g_list_foreach (current_files, (GFunc)g_free, NULL);
73 g_list_free (current_files);
74 current_files = NULL;
76 for (i = list; i; i = i->next)
77 current_files = g_list_prepend (current_files,
78 egg_recent_item_get_uri_utf8 (i->data));
80 current_files = g_list_reverse (current_files);
82 EGG_RECENT_ITEM_LIST_UNREF (list);
85 static void
86 monitoring_handle_changed (EggRecentModel *model,
87 GList *list)
89 GList *i;
91 G_LOCK (monitoring_lock);
93 for (i = list; i; i = i->next) {
94 gchar *name;
95 GList *j;
96 gboolean handled = FALSE;
97 EggRecentItem *item = (EggRecentItem *)i->data;
99 name = egg_recent_item_get_uri_utf8 (item);
101 for (j = current_files; j; j = j->next) {
102 if (utf8_string_match (name, j->data)) {
103 g_free (j->data);
104 current_files = g_list_remove_link (current_files, j);
105 handled = TRUE;
106 break;
110 g_free (name);
112 if (!handled) {
113 /* file was not in above list, a new file! */
114 GnomeVFSURI *uri;
115 gchar *tmp;
116 int l, k;
118 tmp = egg_recent_item_get_uri_utf8 (item);
120 l = strlen (tmp);
122 for (k = l - 1; k >= 0; k--)
123 if (tmp[k] == '/')
124 break;
126 name = g_strdup_printf ("recent-files:///%s",
127 tmp + k + 1);
128 g_free (tmp);
130 uri = gnome_vfs_uri_new (name);
131 g_free (name);
134 for (j = monitoring_handles; j; j = j->next)
135 gnome_vfs_monitor_callback (j->data,
136 uri,
137 GNOME_VFS_MONITOR_EVENT_CREATED);
139 gnome_vfs_uri_unref (uri);
143 /* current_files now contains files which weren't handled above,
144 * so these files can be deleted
147 for (i = current_files; i; i = i->next) {
148 int l, k;
149 gchar *tmp = i->data;
150 gchar *name;
151 GList *j;
152 GnomeVFSURI *uri;
154 l = strlen (tmp);
156 for (k = l - 1; k >= 0; k--)
157 if (tmp[k] == '/')
158 break;
160 name = g_strdup_printf ("recent-files:///%s", tmp + k + 1);
161 uri = gnome_vfs_uri_new (name);
162 g_free (name);
164 for (j = monitoring_handles; j; j = j->next)
165 gnome_vfs_monitor_callback (j->data,
166 uri,
167 GNOME_VFS_MONITOR_EVENT_DELETED);
169 gnome_vfs_uri_unref (uri);
172 /* update current_files */
173 monitoring_refresh_current_files ();
175 G_UNLOCK (monitoring_lock);
178 static void
179 monitoring_setup (void)
181 /* we need to sort on MRU, else we won't get the 10 most recently
182 * used items here, according to mister snorp.
184 G_LOCK (recent_model);
185 recent_model = egg_recent_model_new (EGG_RECENT_MODEL_SORT_MRU);
186 G_UNLOCK (recent_model);
188 monitoring_refresh_current_files ();
190 G_LOCK (recent_model);
191 g_signal_connect (recent_model, "changed",
192 G_CALLBACK (monitoring_handle_changed), NULL);
193 G_UNLOCK (recent_model);
196 static void
197 monitoring_cleanup (void)
199 G_LOCK (recent_model);
200 if (recent_model)
201 g_object_unref (G_OBJECT (recent_model));
202 recent_model = NULL;
203 G_UNLOCK (recent_model);
207 /* somewhat more sane code */
209 static gchar *
210 get_filename_from_uri (const GnomeVFSURI *uri)
212 gchar *path;
213 gchar *ret;
215 path = gnome_vfs_unescape_string (uri->text, G_DIR_SEPARATOR_S);
217 if (!path)
218 return NULL;
220 if (path[0] != G_DIR_SEPARATOR) {
221 g_free (path);
222 return NULL;
225 ret = g_strdup (path + 1);
226 g_free (path);
228 return ret;
231 static gboolean
232 utf8_string_match (const gchar *a,
233 const gchar *b)
235 gchar *normalized_a;
236 gchar *normalized_b;
237 gboolean ret;
238 gint tmp;
240 normalized_a = g_utf8_normalize (a, -1, G_NORMALIZE_ALL);
241 normalized_b = g_utf8_normalize (b, -1, G_NORMALIZE_ALL);
243 /* evil */
244 tmp = strlen (normalized_b) - strlen (normalized_a);
246 ret = !strcmp (normalized_b + tmp, normalized_a);
248 g_free (normalized_a);
249 g_free (normalized_b);
251 return ret;
254 static EggRecentItem *
255 get_recent_item_from_list (const gchar *filename)
257 GList *list, *i;
259 G_LOCK (recent_model);
260 list = egg_recent_model_get_list (recent_model);
261 G_UNLOCK (recent_model);
263 if (!list)
264 return NULL;
266 for (i = list; i; i = i->next) {
267 gchar *name;
268 EggRecentItem *item = (EggRecentItem *)i->data;
270 name = egg_recent_item_get_uri_for_display (item);
272 if (!strncmp (name, "recent-files:", 13)) {
273 /* continue here to avoid endless loops */
274 g_free (name);
275 continue;
278 if (utf8_string_match (filename, name)) {
279 g_free (name);
281 egg_recent_item_ref (item);
282 EGG_RECENT_ITEM_LIST_UNREF (list);
284 return item;
287 g_free (name);
290 EGG_RECENT_ITEM_LIST_UNREF (list);
292 return NULL;
295 static gchar *
296 get_real_uri_from_virtual_uri (const GnomeVFSURI *uri)
298 gchar *filename;
299 gchar *utf8_uri;
300 EggRecentItem *item;
302 filename = get_filename_from_uri (uri);
303 if (!filename)
304 return NULL;
306 item = get_recent_item_from_list (filename);
307 if (!item) {
308 g_free (filename);
309 return NULL;
312 utf8_uri = egg_recent_item_get_uri_utf8 (item);
314 g_free (filename);
315 egg_recent_item_unref (item);
317 return utf8_uri;
322 static GnomeVFSResult
323 do_open (GnomeVFSMethod *method,
324 GnomeVFSMethodHandle **method_handle,
325 GnomeVFSURI *uri,
326 GnomeVFSOpenMode mode,
327 GnomeVFSContext *context)
329 gchar *text_uri;
330 GnomeVFSResult res;
331 GnomeVFSHandle *handle = NULL;
333 _GNOME_VFS_METHOD_PARAM_CHECK (method_handle != NULL);
334 _GNOME_VFS_METHOD_PARAM_CHECK (uri != NULL);
336 text_uri = get_real_uri_from_virtual_uri (uri);
337 if (!text_uri)
338 return GNOME_VFS_ERROR_INVALID_URI;
340 res = gnome_vfs_open (&handle, text_uri, mode);
341 g_free (text_uri);
343 *method_handle = (GnomeVFSMethodHandle *)handle;
345 return res;
348 static GnomeVFSResult
349 do_close (GnomeVFSMethod *method,
350 GnomeVFSMethodHandle *method_handle,
351 GnomeVFSContext *context)
353 GnomeVFSHandle *handle = (GnomeVFSHandle *)method_handle;
355 return gnome_vfs_close (handle);
358 static GnomeVFSResult
359 do_read (GnomeVFSMethod *method,
360 GnomeVFSMethodHandle *method_handle,
361 gpointer buffer,
362 GnomeVFSFileSize num_bytes,
363 GnomeVFSFileSize *bytes_read,
364 GnomeVFSContext *context)
366 GnomeVFSHandle *handle = (GnomeVFSHandle *)method_handle;
368 return gnome_vfs_read (handle, buffer, num_bytes, bytes_read);
371 static GnomeVFSResult
372 do_write (GnomeVFSMethod *method,
373 GnomeVFSMethodHandle *method_handle,
374 gconstpointer buffer,
375 GnomeVFSFileSize num_bytes,
376 GnomeVFSFileSize *bytes_written,
377 GnomeVFSContext *context)
379 GnomeVFSHandle *handle = (GnomeVFSHandle *)method_handle;
381 return gnome_vfs_write (handle, buffer, num_bytes, bytes_written);
384 static GnomeVFSResult
385 do_seek (GnomeVFSMethod *method,
386 GnomeVFSMethodHandle *method_handle,
387 GnomeVFSSeekPosition whence,
388 GnomeVFSFileOffset offset,
389 GnomeVFSContext *context)
391 GnomeVFSHandle *handle = (GnomeVFSHandle *)method_handle;
393 return gnome_vfs_seek (handle, whence, offset);
396 static GnomeVFSResult
397 do_tell (GnomeVFSMethod *method,
398 GnomeVFSMethodHandle *method_handle,
399 GnomeVFSFileSize *offset_return)
401 GnomeVFSHandle *handle = (GnomeVFSHandle *)method_handle;
403 return gnome_vfs_tell (handle, offset_return);
406 static GnomeVFSResult
407 do_truncate_handle (GnomeVFSMethod *method,
408 GnomeVFSMethodHandle *method_handle,
409 GnomeVFSFileSize where,
410 GnomeVFSContext *context)
412 GnomeVFSHandle *handle = (GnomeVFSHandle *)method_handle;
414 return gnome_vfs_truncate_handle (handle, where);
417 typedef struct {
418 GList *items;
419 GList *current;
421 GnomeVFSFileInfoOptions options;
422 } DirectoryHandle;
424 static DirectoryHandle *
425 directory_handle_new (GnomeVFSFileInfoOptions options)
427 DirectoryHandle *handle;
429 handle = g_new0 (DirectoryHandle, 1);
431 G_LOCK (recent_model);
432 handle->items = egg_recent_model_get_list (recent_model);
433 G_UNLOCK (recent_model);
434 handle->current = handle->items;
435 handle->options = options;
437 return handle;
440 static void
441 directory_handle_free (DirectoryHandle *handle)
443 EGG_RECENT_ITEM_LIST_UNREF (handle->items);
445 g_free (handle);
448 static GnomeVFSResult
449 do_open_directory (GnomeVFSMethod *method,
450 GnomeVFSMethodHandle **method_handle,
451 GnomeVFSURI *uri,
452 GnomeVFSFileInfoOptions options,
453 GnomeVFSContext *context)
455 const gchar *scheme;
456 gchar *tmp;
458 _GNOME_VFS_METHOD_PARAM_CHECK (uri != NULL);
460 scheme = gnome_vfs_uri_get_scheme (uri);
461 if (strncmp (scheme, "recent-files", 12))
462 return GNOME_VFS_ERROR_INVALID_URI;
464 tmp = uri->text; /* evil ? */
465 if (!tmp || strlen (tmp) != 1 || tmp[0] != GNOME_VFS_URI_PATH_CHR)
466 return GNOME_VFS_ERROR_INVALID_URI;
468 *method_handle = (GnomeVFSMethodHandle *)directory_handle_new (options);
470 return GNOME_VFS_OK;
473 static GnomeVFSResult
474 do_close_directory (GnomeVFSMethod *method,
475 GnomeVFSMethodHandle *method_handle,
476 GnomeVFSContext *context)
478 DirectoryHandle *handle = (DirectoryHandle *)method_handle;
480 directory_handle_free (handle);
482 return GNOME_VFS_OK;
485 static GnomeVFSResult
486 do_read_directory (GnomeVFSMethod *method,
487 GnomeVFSMethodHandle *method_handle,
488 GnomeVFSFileInfo *file_info,
489 GnomeVFSContext *context)
491 DirectoryHandle *handle = (DirectoryHandle *)method_handle;
492 GnomeVFSResult res;
493 gchar *uri;
495 if (!handle->current)
496 return GNOME_VFS_ERROR_EOF;
498 uri = egg_recent_item_get_uri ((EggRecentItem *)handle->current->data);
499 res = gnome_vfs_get_file_info (uri, file_info, handle->options);
500 g_free (uri);
502 handle->current = handle->current->next;
504 return res; /* FIXME: or the res from _get_file_info? */
507 static GnomeVFSResult
508 do_get_file_info (GnomeVFSMethod *method,
509 GnomeVFSURI *uri,
510 GnomeVFSFileInfo *file_info,
511 GnomeVFSFileInfoOptions options,
512 GnomeVFSContext *context)
514 gchar *text_uri;
515 GnomeVFSResult res;
517 _GNOME_VFS_METHOD_PARAM_CHECK (uri != NULL);
519 if (strlen (uri->text) == 1 && uri->text[0] == GNOME_VFS_URI_PATH_CHR) {
520 /* handle root directory */
521 file_info->valid_fields = 0;
523 g_free (file_info->name);
524 file_info->name = g_strdup ("Recent Files");
526 file_info->type = GNOME_VFS_FILE_TYPE_DIRECTORY;
527 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_TYPE;
529 g_free (file_info->mime_type);
530 file_info->mime_type = g_strdup ("x-directory/normal");
531 file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
533 return GNOME_VFS_OK;
536 text_uri = get_real_uri_from_virtual_uri (uri);
537 if (!text_uri)
538 return GNOME_VFS_ERROR_INVALID_URI;
540 res = gnome_vfs_get_file_info (text_uri, file_info, options);
541 g_free (text_uri);
543 return res;
546 static GnomeVFSResult
547 do_get_file_info_from_handle (GnomeVFSMethod *method,
548 GnomeVFSMethodHandle *method_handle,
549 GnomeVFSFileInfo *file_info,
550 GnomeVFSFileInfoOptions options,
551 GnomeVFSContext *context)
553 GnomeVFSHandle *handle = (GnomeVFSHandle *)method_handle;
555 return gnome_vfs_get_file_info_from_handle (handle,
556 file_info, options);
559 static gboolean
560 do_is_local (GnomeVFSMethod *method,
561 const GnomeVFSURI *uri)
563 gchar *text_uri;
564 GnomeVFSURI *tmp_uri;
565 gboolean ret;
567 _GNOME_VFS_METHOD_PARAM_CHECK (uri != NULL);
569 text_uri = get_real_uri_from_virtual_uri (uri);
570 if (!text_uri)
571 return GNOME_VFS_ERROR_INVALID_URI;
573 tmp_uri = gnome_vfs_uri_new (text_uri);
574 g_free (text_uri);
575 if (!tmp_uri)
576 return GNOME_VFS_ERROR_INTERNAL;
578 ret = gnome_vfs_uri_is_local (tmp_uri);
579 gnome_vfs_uri_unref (tmp_uri);
581 return ret;
584 static GnomeVFSResult
585 do_unlink (GnomeVFSMethod *method,
586 GnomeVFSURI *uri,
587 GnomeVFSContext *context)
589 gchar *text_uri;
591 _GNOME_VFS_METHOD_PARAM_CHECK (uri != NULL);
593 text_uri = get_real_uri_from_virtual_uri (uri);
594 if (!text_uri)
595 return GNOME_VFS_ERROR_INVALID_URI;
597 G_LOCK (recent_model);
598 egg_recent_model_delete (recent_model, text_uri);
599 G_UNLOCK (recent_model);
601 g_free (text_uri);
603 return GNOME_VFS_OK;
606 static GnomeVFSResult
607 do_check_same_fs (GnomeVFSMethod *method,
608 GnomeVFSURI *source_uri,
609 GnomeVFSURI *target_uri,
610 gboolean *same_fs_return,
611 GnomeVFSContext *context)
613 gchar *a, *b;
614 GnomeVFSResult res;
616 _GNOME_VFS_METHOD_PARAM_CHECK (source_uri != NULL);
617 _GNOME_VFS_METHOD_PARAM_CHECK (target_uri != NULL);
619 a = get_real_uri_from_virtual_uri (source_uri);
620 if (!a)
621 return GNOME_VFS_ERROR_INVALID_URI;
623 b = get_real_uri_from_virtual_uri (target_uri);
624 if (!b) {
625 g_free (a);
626 return GNOME_VFS_ERROR_INVALID_URI;
629 res = gnome_vfs_check_same_fs (a, b, same_fs_return);
631 g_free (a);
632 g_free (b);
634 return res;
637 static GnomeVFSResult
638 do_set_file_info (GnomeVFSMethod *method,
639 GnomeVFSURI *uri,
640 const GnomeVFSFileInfo *info,
641 GnomeVFSSetFileInfoMask mask,
642 GnomeVFSContext *context)
644 gchar *text_uri;
645 GnomeVFSResult res;
647 _GNOME_VFS_METHOD_PARAM_CHECK (uri != NULL);
649 text_uri = get_real_uri_from_virtual_uri (uri);
650 if (!text_uri)
651 return GNOME_VFS_ERROR_INVALID_URI;
653 res = gnome_vfs_set_file_info (text_uri, (GnomeVFSFileInfo *)info, mask);
654 g_free (text_uri);
656 return res;
659 static GnomeVFSResult
660 do_truncate (GnomeVFSMethod *method,
661 GnomeVFSURI *uri,
662 GnomeVFSFileSize where,
663 GnomeVFSContext *context)
665 gchar *text_uri;
666 GnomeVFSResult res;
668 _GNOME_VFS_METHOD_PARAM_CHECK (uri != NULL);
670 text_uri = get_real_uri_from_virtual_uri (uri);
671 if (!text_uri)
672 return GNOME_VFS_ERROR_INVALID_URI;
674 res = gnome_vfs_truncate (text_uri, where);
675 g_free (text_uri);
677 return res;
680 typedef struct {
681 /* will prolly take up 4 bytes anyway ... */
682 short foo:1;
683 } DummyMonitoringHandle;
685 static GnomeVFSResult
686 do_monitor_add (GnomeVFSMethod *method,
687 GnomeVFSMethodHandle **method_handle_return,
688 GnomeVFSURI *uri,
689 GnomeVFSMonitorType monitor_type)
691 gchar *tmp;
692 const gchar *scheme;
694 _GNOME_VFS_METHOD_PARAM_CHECK (uri != NULL);
696 scheme = gnome_vfs_uri_get_scheme (uri);
697 if (strncmp (scheme, "recent-files", 12))
698 return GNOME_VFS_ERROR_INVALID_URI;
700 tmp = uri->text; /* evil? */
701 if (!tmp || strlen (tmp) != 1 || tmp[0] != GNOME_VFS_URI_PATH_CHR)
702 return GNOME_VFS_ERROR_NOT_SUPPORTED;
704 if (monitor_type != GNOME_VFS_MONITOR_DIRECTORY)
705 return GNOME_VFS_ERROR_BAD_PARAMETERS;
707 /* accepted*/
708 *method_handle_return = (GnomeVFSMethodHandle *)g_new (DummyMonitoringHandle, 1);
710 monitoring_add_handle (*method_handle_return);
712 return GNOME_VFS_OK;
715 static GnomeVFSResult
716 do_monitor_cancel (GnomeVFSMethod *method,
717 GnomeVFSMethodHandle *method_handle)
719 /* remove handle */
720 monitoring_remove_handle (method_handle);
722 g_free (method_handle);
724 return GNOME_VFS_OK;
727 static GnomeVFSResult
728 do_file_control (GnomeVFSMethod *method,
729 GnomeVFSMethodHandle *method_handle,
730 const char *operation,
731 gpointer operation_data,
732 GnomeVFSContext *context)
734 GnomeVFSHandle *handle = (GnomeVFSHandle *)method_handle;
736 return gnome_vfs_file_control (handle, operation, operation_data);
739 static GnomeVFSMethod method = {
740 sizeof (GnomeVFSMethod),
741 do_open,
742 NULL, /* do_create */
743 do_close,
744 do_read,
745 do_write,
746 do_seek,
747 do_tell,
748 do_truncate_handle,
749 do_open_directory,
750 do_close_directory,
751 do_read_directory,
752 do_get_file_info,
753 do_get_file_info_from_handle,
754 do_is_local,
755 NULL, /* do_make_directory */
756 NULL, /* do_remove_directory */
757 NULL, /* do_move */
758 do_unlink,
759 do_check_same_fs,
760 do_set_file_info,
761 do_truncate,
762 NULL, /* do_find_directory */
763 NULL, /* do_create_symbolic_link */
764 do_monitor_add,
765 do_monitor_cancel,
766 do_file_control,
767 NULL, /* do_forget_cache */
768 NULL /* do_get_freespace */
772 /* shouldn't need locking here IIRC */
773 GnomeVFSMethod *
774 vfs_module_init (const char *method_name, const char *args)
776 monitoring_setup ();
778 return &method;
781 void
782 vfs_module_shutdown (GnomeVFSMethod *method)
784 monitoring_cleanup ();