1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as
5 * published by the Free Software Foundation; either version 2 of the
6 * License, or (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
18 * James Willcox <jwillcox@cs.indiana.edu>
29 #include <gconf/gconf-client.h>
30 #include <libbonoboui.h>
31 #include <libgnomevfs/gnome-vfs.h>
32 #include "gnome-recent-model.h"
33 #include "gnome-recent-view.h"
35 #define GNOME_RECENT_MODEL_BASE_KEY "/desktop/gnome/recent_files"
36 #define GNOME_RECENT_MODEL_GLOBAL_LIMIT_KEY "global_limit"
37 #define GNOME_RECENT_MODEL_GLOBAL_LIMIT_ENV "GNOME_RECENT_MODEL_GLOBAL_LIMIT"
38 #define GNOME_RECENT_MODEL_GLOBAL_LIST "gnome-recent-global"
40 static void gnome_recent_model_class_init (GnomeRecentModelClass * klass);
41 static void gnome_recent_model_init (GnomeRecentModel * recent);
42 static gchar *gnome_recent_model_gconf_key (GnomeRecentModel * recent);
43 static void gnome_recent_model_notify_cb (GConfClient *client,
47 static GSList * gnome_recent_model_delete_from_list (GnomeRecentModel *recent,
50 static GSList * gnome_recent_model_gconf_to_list (GConfValue* value);
51 static void gnome_recent_model_g_slist_deep_free (GSList *list);
52 static void gnome_recent_model_set_appname (GnomeRecentModel *recent, gchar *appname);
54 struct _GnomeRecentModel {
55 GObject parent_instance; /* We emit signals */
57 gchar *appname; /* the app that owns this object */
58 GConfClient *gconf_client; /* we use GConf to store stuff */
59 unsigned int limit; /* maximum number of items to store */
61 GnomeRecentModel *global; /* Another GnomeRecentModel object,
62 * representing the global
66 GHashTable *monitors; /* A hash table holding
67 * GnomeVfsMonitorHandle objects.
71 struct _GnomeRecentModelClass {
72 GObjectClass parent_class;
74 void (*changed) (GnomeRecentModel *recent, const GSList *list);
77 struct _GnomeRecentModelMenuData {
78 GnomeRecentModel *recent;
82 typedef struct _GnomeRecentModelMenuData GnomeRecentModelMenuData;
89 /* GObject properties */
96 static GType model_signals[LAST_SIGNAL] = { 0 };
97 static GObjectClass *parent_class = NULL;
100 * gnome_recent_model_get_type:
103 * This returns a GType representing a GnomeRecentModel object.
108 gnome_recent_model_get_type (void)
110 static GType gnome_recent_model_type = 0;
112 if(!gnome_recent_model_type) {
113 static const GTypeInfo gnome_recent_model_info = {
114 sizeof (GnomeRecentModelClass),
115 NULL, /* base init */
116 NULL, /* base finalize */
117 (GClassInitFunc)gnome_recent_model_class_init, /* class init */
118 NULL, /* class finalize */
119 NULL, /* class data */
120 sizeof (GnomeRecentModel),
122 (GInstanceInitFunc) gnome_recent_model_init
125 gnome_recent_model_type = g_type_register_static (G_TYPE_OBJECT,
127 &gnome_recent_model_info, 0);
130 return gnome_recent_model_type;
134 gnome_recent_model_set_property (GObject *object,
139 GnomeRecentModel *recent = GNOME_RECENT_MODEL (object);
145 appname = g_strdup (g_value_get_string (value));
146 gnome_recent_model_set_appname (recent, appname);
149 gnome_recent_model_set_limit (GNOME_RECENT_MODEL (recent),
150 g_value_get_int (value));
158 gnome_recent_model_get_property (GObject *object,
163 GnomeRecentModel *recent = GNOME_RECENT_MODEL (object);
168 g_value_set_string (value, recent->appname);
171 g_value_set_int (value, recent->limit);
174 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
179 gnome_recent_model_class_init (GnomeRecentModelClass * klass)
181 GObjectClass *object_class;
183 object_class = G_OBJECT_CLASS (klass);
185 parent_class = g_type_class_peek_parent (klass);
187 object_class->set_property = gnome_recent_model_set_property;
188 object_class->get_property = gnome_recent_model_get_property;
190 model_signals[CHANGED] = g_signal_new ("changed",
191 G_OBJECT_CLASS_TYPE (object_class),
193 G_STRUCT_OFFSET (GnomeRecentModelClass, changed),
195 g_cclosure_marshal_VOID__POINTER,
199 g_object_class_install_property (object_class,
201 g_param_spec_string ("appname",
203 "The name of the application using this object.",
206 g_object_class_install_property (object_class,
208 g_param_spec_int ("limit",
210 "The maximum number of items to be allowed in the list.",
216 klass->changed = NULL;
221 gnome_recent_model_init (GnomeRecentModel * recent)
226 if (!gconf_init (argc, argv, NULL))
228 g_warning ("GConf Initialization failed.");
232 if (!gnome_vfs_init ()) {
233 g_warning ("gnome-vfs initialization failed.");
237 recent->gconf_client = gconf_client_get_default ();
238 recent->monitors = g_hash_table_new (g_str_hash, g_str_equal);
242 gnome_recent_model_get_global_limit (GnomeRecentModel *model)
247 key = g_strdup_printf ("%s/%s", GNOME_RECENT_MODEL_BASE_KEY,
248 GNOME_RECENT_MODEL_GLOBAL_LIMIT_KEY);
251 limit = gconf_client_get_int (model->gconf_client,
255 /* ok, gconf schemas are not functioning, so assign a sane value */
266 * gnome_recent_model_new:
267 * @appname: The name of your application.
268 * @limit: The maximum number of items allowed.
270 * This creates a new GnomeRecentModel object.
272 * Returns: a GnomeRecentModel object
275 gnome_recent_model_new (const gchar *appname, gint limit)
277 GnomeRecentModel *model;
279 g_return_val_if_fail (appname, NULL);
280 g_return_val_if_fail (limit > 0, NULL);
282 model = GNOME_RECENT_MODEL (g_object_new (gnome_recent_model_get_type (),
288 g_return_val_if_fail (model, NULL);
294 * gnome_recent_model_new_global:
297 * This creates a new GnomeRecentModel object, with the global history list.
299 * Returns: a GnomeRecentModel object
302 gnome_recent_model_new_global (void)
304 GnomeRecentModel *model;
307 model = GNOME_RECENT_MODEL (g_object_new(gnome_recent_model_get_type (),
309 GNOME_RECENT_MODEL_GLOBAL_LIST,
312 g_return_val_if_fail (model, NULL);
314 limit = gnome_recent_model_get_global_limit (model);
315 gnome_recent_model_set_limit (model, limit);
323 gnome_recent_model_monitor_cb (GnomeVFSMonitorHandle *handle,
324 const gchar *monitor_uri,
325 const gchar *info_uri,
326 GnomeVFSMonitorEventType event_type,
329 GnomeRecentModel *recent= GNOME_RECENT_MODEL (data);
331 g_return_if_fail (recent);
333 /* if a file was deleted, we just remove it from our list */
334 switch (event_type) {
335 case GNOME_VFS_MONITOR_EVENT_DELETED:
336 gnome_recent_model_delete (recent, monitor_uri);
337 g_hash_table_remove (recent->monitors, monitor_uri);
346 gnome_recent_model_monitor_uri (GnomeRecentModel *recent, const gchar *uri)
348 GnomeVFSMonitorHandle *handle=NULL;
349 GnomeVFSResult result;
351 g_return_if_fail (recent);
352 g_return_if_fail (GNOME_IS_RECENT_MODEL (recent));
353 g_return_if_fail (uri);
355 handle = g_hash_table_lookup (recent->monitors, uri);
356 if (handle == NULL) {
358 /* this is a new uri, so we need to monitor it */
359 result = gnome_vfs_monitor_add (&handle,
361 GNOME_VFS_MONITOR_FILE,
362 gnome_recent_model_monitor_cb,
364 if (result == GNOME_VFS_OK) {
365 g_hash_table_insert (recent->monitors,
373 gnome_recent_model_monitor_uri_list (GnomeRecentModel *recent,
381 uri = (const gchar *)p->data;
383 gnome_recent_model_monitor_uri (recent, uri);
391 gnome_recent_model_monitor_cancel (GnomeRecentModel *recent, const gchar *uri)
393 g_return_if_fail (recent);
394 g_return_if_fail (GNOME_IS_RECENT_MODEL (recent));
395 g_return_if_fail (uri);
397 g_hash_table_remove (recent->monitors, uri);
402 * gnome_recent_model_add:
403 * @recent: A GnomeRecentModel object.
404 * @uri: The URI you want to add to the list.
406 * This function adds a URI to the list of recently used URIs.
408 * Returns: a gboolean
411 gnome_recent_model_add (GnomeRecentModel * recent, const gchar * uri)
416 g_return_val_if_fail (recent, FALSE);
417 g_return_val_if_fail (GNOME_IS_RECENT_MODEL (recent), FALSE);
418 g_return_val_if_fail (recent->gconf_client, FALSE);
419 g_return_val_if_fail (uri, FALSE);
421 gconf_key = gnome_recent_model_gconf_key (recent);
424 uri_lst = gconf_client_get_list (recent->gconf_client,
426 GCONF_VALUE_STRING, NULL);
428 /* if this is already in our list, remove it */
429 uri_lst = gnome_recent_model_delete_from_list (recent, uri_lst, uri);
431 /* prepend the new one */
432 uri_lst = g_slist_prepend (uri_lst, g_strdup (uri));
434 /* if we're over the limit, delete from the end */
435 while (g_slist_length (uri_lst) > recent->limit)
438 tmp_uri = g_slist_nth_data (uri_lst, g_slist_length (uri_lst)-1);
439 uri_lst = g_slist_remove (uri_lst, tmp_uri);
443 gconf_client_set_list (recent->gconf_client,
448 gconf_client_suggest_sync (recent->gconf_client, NULL);
450 /* add to the global list */
452 gnome_recent_model_add (GNOME_RECENT_MODEL (recent->global), uri);
455 gnome_recent_model_g_slist_deep_free (uri_lst);
462 * gnome_recent_model_delete:
463 * @recent: A GnomeRecentModel object.
464 * @uri: The URI you want to delete from the list.
466 * This function deletes a URI from the list of recently used URIs.
468 * Returns: a gboolean
471 gnome_recent_model_delete (GnomeRecentModel * recent, const gchar * uri)
475 gboolean ret = FALSE;
478 g_return_val_if_fail (recent, FALSE);
479 g_return_val_if_fail (GNOME_IS_RECENT_MODEL (recent), FALSE);
480 g_return_val_if_fail (recent->gconf_client, FALSE);
481 g_return_val_if_fail (uri, FALSE);
483 gconf_key = gnome_recent_model_gconf_key (recent);
484 uri_lst = gconf_client_get_list (recent->gconf_client,
486 GCONF_VALUE_STRING, NULL);
488 new_uri_lst = gnome_recent_model_delete_from_list (recent, uri_lst, uri);
490 /* if it wasn't deleted, no need to cause unneeded updates */
492 if (new_uri_lst == uri_lst) {
496 uri_lst = new_uri_lst;
499 /* delete it from gconf */
500 gconf_client_set_list (recent->gconf_client,
505 gconf_client_suggest_sync (recent->gconf_client, NULL);
507 /* delete from the global list */
509 gnome_recent_model_delete (GNOME_RECENT_MODEL (recent->global), uri);
513 gnome_recent_model_g_slist_deep_free (new_uri_lst);
519 * gnome_recent_model_get_list:
520 * @recent: A GnomeRecentModel object.
522 * This returns a linked list of strings (URIs) currently held
525 * Returns: A GSList *
528 gnome_recent_model_get_list (GnomeRecentModel * recent)
531 gchar *gconf_key = gnome_recent_model_gconf_key (recent);
533 g_return_val_if_fail (recent, NULL);
534 g_return_val_if_fail (recent->gconf_client, NULL);
535 g_return_val_if_fail (GNOME_IS_RECENT_MODEL (recent), NULL);
537 uri_lst = gconf_client_get_list (recent->gconf_client,
539 GCONF_VALUE_STRING, NULL);
543 /* FIXME: This sucks. */
544 gnome_recent_model_monitor_uri_list (recent, uri_lst);
552 * gnome_recent_model_set_limit:
553 * @recent: A GnomeRecentModel object.
554 * @limit: The maximum number of items allowed in the list.
556 * Use this function to constrain the number of items allowed in the list.
557 * The default is %GNOME_RECENT_MODEL_DEFAULT_LIMIT.
561 gnome_recent_model_set_limit (GnomeRecentModel *recent, gint limit)
567 g_return_if_fail (recent);
568 g_return_if_fail (GNOME_IS_RECENT_MODEL (recent));
569 g_return_if_fail (limit > 0);
570 recent->limit = limit;
572 list = gnome_recent_model_get_list (recent);
573 len = g_slist_length (list);
575 if (len <= limit) return;
577 /* if we're over the limit, delete from the end */
578 i=g_slist_length (list);
579 while (i > recent->limit)
581 gchar *uri = g_slist_nth_data (list, i-1);
582 gnome_recent_model_delete (recent, uri);
587 gnome_recent_model_g_slist_deep_free (list);
592 * gnome_recent_model_get_limit:
593 * @recent: A GnomeRecentModel object.
597 gnome_recent_model_get_limit (GnomeRecentModel *recent)
599 g_return_val_if_fail (recent, -1);
600 g_return_val_if_fail (GNOME_IS_RECENT_MODEL (recent), -1);
602 return recent->limit;
607 * gnome_recent_model_clear:
608 * @recent: A GnomeRecentModel object.
610 * This function clears the list of recently used URIs.
614 gnome_recent_model_clear (GnomeRecentModel *recent)
618 g_return_if_fail (recent);
619 g_return_if_fail (recent->gconf_client);
620 g_return_if_fail (GNOME_IS_RECENT_MODEL (recent));
622 key = gnome_recent_model_gconf_key (recent);
624 gconf_client_unset (recent->gconf_client, key, NULL);
628 gnome_recent_model_set_appname (GnomeRecentModel *recent, gchar *appname)
633 g_return_if_fail (recent);
634 g_return_if_fail (appname);
636 recent->appname = appname;
638 /* if this isn't the global list embed a global one */
639 if (strcmp (appname, GNOME_RECENT_MODEL_GLOBAL_LIST)) {
640 recent->global = gnome_recent_model_new_global ();
643 /* Set up the gconf notification stuff */
644 key = gnome_recent_model_gconf_key (recent);
645 gconf_client_add_dir (recent->gconf_client,
646 GNOME_RECENT_MODEL_BASE_KEY, GCONF_CLIENT_PRELOAD_NONE, NULL);
647 notify_id = gconf_client_notify_add (recent->gconf_client,
649 gnome_recent_model_notify_cb,
658 gnome_recent_model_delete_from_list (GnomeRecentModel *recent, GSList *list,
664 for (i = 0; i < g_slist_length (list); i++) {
665 text = g_slist_nth_data (list, i);
667 if (!strcmp (text, uri)) {
668 list = g_slist_remove (list, text);
676 /* this takes a list of GConfValues, and returns a list of strings */
678 gnome_recent_model_gconf_to_list (GConfValue* value)
683 g_return_val_if_fail (value, NULL);
685 iter = gconf_value_get_list(value);
689 GConfValue* element = iter->data;
690 gchar *text = g_strdup (gconf_value_get_string (element));
692 list = g_slist_prepend (list, text);
694 iter = g_slist_next(iter);
697 list = g_slist_reverse (list);
703 gnome_recent_model_g_slist_deep_free (GSList *list)
721 gnome_recent_model_gconf_key (GnomeRecentModel * model)
725 g_return_val_if_fail (model, NULL);
727 key = g_strdup_printf ("%s/%s", GNOME_RECENT_MODEL_BASE_KEY, model->appname);
733 print_list (GSList *list)
736 g_print ("%s, ", (char *)list->data);
744 /* this is the gconf notification callback. */
746 gnome_recent_model_notify_cb (GConfClient *client, guint cnxn_id,
747 GConfEntry *entry, gpointer user_data)
750 GnomeRecentModel *recent = user_data;
752 if (entry->value == NULL) {
753 g_signal_emit (G_OBJECT(recent), model_signals[CHANGED], 0, NULL);
757 list = gnome_recent_model_gconf_to_list (entry->value);
759 gnome_recent_model_monitor_uri_list (recent, list);
761 g_signal_emit (G_OBJECT(recent), model_signals[CHANGED], 0, list);
763 gnome_recent_model_g_slist_deep_free (list);
768 gnome_recent_model_get_appname (GnomeRecentModel *model)
770 return g_strdup (model->appname);