]> git.sur5r.net Git - glabels/blob - glabels2/src/gnome-recent-model.c
Initial revision
[glabels] / glabels2 / src / gnome-recent-model.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /**
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.
7  *
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.
12  *
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.
16  *
17  * Authors:
18  *   James Willcox <jwillcox@cs.indiana.edu>
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <stdio.h>
26 #include <string.h>
27 #include <stdlib.h>
28 #include <gtk/gtk.h>
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"
34
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"
39
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,
44                                                 guint cnxn_id,
45                                                 GConfEntry *entry,
46                                                 gpointer user_data);
47 static GSList * gnome_recent_model_delete_from_list (GnomeRecentModel *recent,
48                                                GSList *list,
49                                                const gchar *uri);
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);
53
54 struct _GnomeRecentModel {
55         GObject parent_instance;        /* We emit signals */
56
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 */
60
61         GnomeRecentModel *global;       /* Another GnomeRecentModel object,
62                                          * representing the global
63                                          * recent uri list
64                                          */
65
66         GHashTable *monitors;           /* A hash table holding
67                                          * GnomeVfsMonitorHandle objects.
68                                          */
69 };
70
71 struct _GnomeRecentModelClass {
72         GObjectClass parent_class;
73         
74         void (*changed) (GnomeRecentModel *recent, const GSList *list);
75 };
76
77 struct _GnomeRecentModelMenuData {
78         GnomeRecentModel *recent;
79         gchar *uri;
80 };
81
82 typedef struct _GnomeRecentModelMenuData GnomeRecentModelMenuData;
83
84 enum {
85         CHANGED,
86         LAST_SIGNAL
87 };
88
89 /* GObject properties */
90 enum {
91         PROP_BOGUS,
92         PROP_APPNAME,
93         PROP_LIMIT,
94 };
95
96 static GType model_signals[LAST_SIGNAL] = { 0 };
97 static GObjectClass *parent_class = NULL;
98
99 /**
100  * gnome_recent_model_get_type:
101  * @:
102  *
103  * This returns a GType representing a GnomeRecentModel object.
104  *
105  * Returns: a GType
106  */
107 GType
108 gnome_recent_model_get_type (void)
109 {
110         static GType gnome_recent_model_type = 0;
111
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),
121                         0,
122                         (GInstanceInitFunc) gnome_recent_model_init
123                 };
124
125                 gnome_recent_model_type = g_type_register_static (G_TYPE_OBJECT,
126                                                         "GnomeRecentModel",
127                                                         &gnome_recent_model_info, 0);
128         }
129
130         return gnome_recent_model_type;
131 }
132
133 static void
134 gnome_recent_model_set_property (GObject *object,
135                            guint prop_id,
136                            const GValue *value,
137                            GParamSpec *pspec)
138 {
139         GnomeRecentModel *recent = GNOME_RECENT_MODEL (object);
140         gchar *appname;
141
142         switch (prop_id)
143         {
144                 case PROP_APPNAME:
145                         appname = g_strdup (g_value_get_string (value));
146                         gnome_recent_model_set_appname (recent, appname);
147                 break;
148                 case PROP_LIMIT:
149                         gnome_recent_model_set_limit (GNOME_RECENT_MODEL (recent),
150                                                 g_value_get_int (value));
151                 break;
152                 default:
153                 break;
154         }
155 }
156
157 static void
158 gnome_recent_model_get_property (GObject *object,
159                            guint prop_id,
160                            GValue *value,
161                            GParamSpec *pspec)
162 {
163         GnomeRecentModel *recent = GNOME_RECENT_MODEL (object);
164
165         switch (prop_id)
166         {
167                 case PROP_APPNAME:
168                         g_value_set_string (value, recent->appname);
169                 break;
170                 case PROP_LIMIT:
171                         g_value_set_int (value, recent->limit);
172                 break;
173                 default:
174                         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
175         }
176 }
177
178 static void
179 gnome_recent_model_class_init (GnomeRecentModelClass * klass)
180 {
181         GObjectClass *object_class;
182
183         object_class = G_OBJECT_CLASS (klass);
184
185         parent_class = g_type_class_peek_parent (klass);
186
187         object_class->set_property = gnome_recent_model_set_property;
188         object_class->get_property = gnome_recent_model_get_property;
189
190         model_signals[CHANGED] = g_signal_new ("changed",
191                         G_OBJECT_CLASS_TYPE (object_class),
192                         G_SIGNAL_RUN_LAST,
193                         G_STRUCT_OFFSET (GnomeRecentModelClass, changed),
194                         NULL, NULL,
195                         g_cclosure_marshal_VOID__POINTER,
196                         G_TYPE_NONE, 1,
197                         G_TYPE_POINTER);
198
199         g_object_class_install_property (object_class,
200                                          PROP_APPNAME,
201                                          g_param_spec_string ("appname",
202                                                               "Application Name",
203                                                               "The name of the application using this object.",
204                                                               "gnome-app",
205                                                               G_PARAM_READWRITE));
206         g_object_class_install_property (object_class,
207                                          PROP_LIMIT,
208                                          g_param_spec_int    ("limit",
209                                                               "Limit",
210                                                               "The maximum number of items to be allowed in the list.",
211                                                               1,
212                                                               1000,
213                                                               10,
214                                                               G_PARAM_READWRITE));
215
216         klass->changed = NULL;
217 }
218
219
220 static void
221 gnome_recent_model_init (GnomeRecentModel * recent)
222 {
223         int argc=0;
224         char **argv=NULL;
225
226         if (!gconf_init (argc, argv, NULL))
227         {
228                 g_warning ("GConf Initialization failed.");
229                 return;
230         }
231         
232         if (!gnome_vfs_init ()) {
233                 g_warning ("gnome-vfs initialization failed.");
234                 return;
235         }
236
237         recent->gconf_client = gconf_client_get_default ();
238         recent->monitors = g_hash_table_new (g_str_hash, g_str_equal);
239 }
240
241 static gint
242 gnome_recent_model_get_global_limit (GnomeRecentModel *model)
243 {
244         char *key;
245         gint limit;
246
247         key = g_strdup_printf ("%s/%s", GNOME_RECENT_MODEL_BASE_KEY,
248                                GNOME_RECENT_MODEL_GLOBAL_LIMIT_KEY);
249
250
251         limit = gconf_client_get_int (model->gconf_client,
252                                       key, NULL);
253
254         if (limit <= 0) {
255                 /* ok, gconf schemas are not functioning, so assign a sane value */
256                 limit = 10;
257         }
258
259         g_free (key);
260         
261         return limit;
262 }
263
264
265 /**
266  * gnome_recent_model_new:
267  * @appname: The name of your application.
268  * @limit:  The maximum number of items allowed.
269  *
270  * This creates a new GnomeRecentModel object.
271  *
272  * Returns: a GnomeRecentModel object
273  */
274 GnomeRecentModel *
275 gnome_recent_model_new (const gchar *appname, gint limit)
276 {
277         GnomeRecentModel *model;
278
279         g_return_val_if_fail (appname, NULL);
280         g_return_val_if_fail (limit > 0, NULL);
281
282         model = GNOME_RECENT_MODEL (g_object_new (gnome_recent_model_get_type (),
283                                            "appname",
284                                            appname,
285                                            "limit",
286                                            limit, NULL));
287
288         g_return_val_if_fail (model, NULL);
289         
290         return model;
291 }
292
293 /**
294  * gnome_recent_model_new_global:
295  * @
296  *
297  * This creates a new GnomeRecentModel object, with the global history list.
298  *
299  * Returns: a GnomeRecentModel object
300  */
301 GnomeRecentModel *
302 gnome_recent_model_new_global (void)
303 {
304         GnomeRecentModel *model;
305         gint limit;
306
307         model = GNOME_RECENT_MODEL (g_object_new(gnome_recent_model_get_type (),
308                                            "appname",
309                                            GNOME_RECENT_MODEL_GLOBAL_LIST,
310                                            NULL));
311
312         g_return_val_if_fail (model, NULL);
313
314         limit = gnome_recent_model_get_global_limit (model);
315         gnome_recent_model_set_limit (model, limit);
316         
317         return model;
318 }
319
320
321
322 static void
323 gnome_recent_model_monitor_cb (GnomeVFSMonitorHandle *handle,
324                          const gchar *monitor_uri,
325                          const gchar *info_uri,
326                          GnomeVFSMonitorEventType event_type,
327                          gpointer data)
328 {
329         GnomeRecentModel *recent= GNOME_RECENT_MODEL (data);
330
331         g_return_if_fail (recent);
332
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);
338                         break;
339                 default:
340                 break;
341         }
342
343 }
344
345 static void
346 gnome_recent_model_monitor_uri (GnomeRecentModel *recent, const gchar *uri)
347 {
348         GnomeVFSMonitorHandle *handle=NULL;
349         GnomeVFSResult result;
350
351         g_return_if_fail (recent);
352         g_return_if_fail (GNOME_IS_RECENT_MODEL (recent));
353         g_return_if_fail (uri);
354
355         handle = g_hash_table_lookup (recent->monitors, uri);
356         if (handle == NULL) {
357
358                 /* this is a new uri, so we need to monitor it */
359                 result = gnome_vfs_monitor_add (&handle,
360                                        uri,
361                                        GNOME_VFS_MONITOR_FILE,
362                                        gnome_recent_model_monitor_cb,
363                                        recent);
364                 if (result == GNOME_VFS_OK) {
365                         g_hash_table_insert (recent->monitors,
366                                              g_strdup (uri),
367                                              handle);
368                 }
369         }
370 }
371
372 static void
373 gnome_recent_model_monitor_uri_list (GnomeRecentModel *recent,
374                                      GSList *list)
375 {
376         GSList *p;
377         const gchar *uri;
378
379         p = list;
380         while (p != NULL) {
381                 uri = (const gchar *)p->data;
382
383                 gnome_recent_model_monitor_uri (recent, uri);
384
385                 p = p->next;
386         }
387 }
388
389 #if 0
390 static void
391 gnome_recent_model_monitor_cancel (GnomeRecentModel *recent, const gchar *uri)
392 {
393         g_return_if_fail (recent);
394         g_return_if_fail (GNOME_IS_RECENT_MODEL (recent));
395         g_return_if_fail (uri);
396
397         g_hash_table_remove (recent->monitors, uri);
398 }
399 #endif
400
401 /**
402  * gnome_recent_model_add:
403  * @recent:  A GnomeRecentModel object.
404  * @uri: The URI you want to add to the list.
405  *
406  * This function adds a URI to the list of recently used URIs.
407  *
408  * Returns: a gboolean
409  */
410 gboolean
411 gnome_recent_model_add (GnomeRecentModel * recent, const gchar * uri)
412 {
413         GSList *uri_lst;
414         gchar *gconf_key;
415
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);
420
421         gconf_key = gnome_recent_model_gconf_key (recent);
422
423
424         uri_lst = gconf_client_get_list (recent->gconf_client,
425                                        gconf_key,
426                                        GCONF_VALUE_STRING, NULL);
427
428         /* if this is already in our list, remove it */
429         uri_lst = gnome_recent_model_delete_from_list (recent, uri_lst, uri);
430
431         /* prepend the new one */
432         uri_lst = g_slist_prepend (uri_lst, g_strdup (uri));
433
434         /* if we're over the limit, delete from the end */
435         while (g_slist_length (uri_lst) > recent->limit)
436         {
437                 gchar *tmp_uri;
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);
440                 g_free (tmp_uri);
441         }
442         
443         gconf_client_set_list (recent->gconf_client,
444                               gconf_key,
445                               GCONF_VALUE_STRING,
446                               uri_lst, NULL);
447
448         gconf_client_suggest_sync (recent->gconf_client, NULL);
449
450         /* add to the global list */
451         if (recent->global)
452                 gnome_recent_model_add (GNOME_RECENT_MODEL (recent->global), uri);
453
454         g_free (gconf_key);
455         gnome_recent_model_g_slist_deep_free (uri_lst);
456
457         return TRUE;
458 }
459
460
461 /**
462  * gnome_recent_model_delete:
463  * @recent:  A GnomeRecentModel object.
464  * @uri: The URI you want to delete from the list.
465  *
466  * This function deletes a URI from the list of recently used URIs.
467  *
468  * Returns: a gboolean
469  */
470 gboolean
471 gnome_recent_model_delete (GnomeRecentModel * recent, const gchar * uri)
472 {
473         GSList *uri_lst;
474         GSList *new_uri_lst;
475         gboolean ret = FALSE;
476         gchar *gconf_key;
477
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);
482
483         gconf_key = gnome_recent_model_gconf_key (recent);
484         uri_lst = gconf_client_get_list (recent->gconf_client,
485                                        gconf_key,
486                                        GCONF_VALUE_STRING, NULL);
487
488         new_uri_lst = gnome_recent_model_delete_from_list (recent, uri_lst, uri);
489
490         /* if it wasn't deleted, no need to cause unneeded updates */
491         /*
492         if (new_uri_lst == uri_lst) {
493                 return FALSE;
494         }
495         else
496                 uri_lst = new_uri_lst;
497         */
498
499         /* delete it from gconf */
500         gconf_client_set_list (recent->gconf_client,
501                                gconf_key,
502                                GCONF_VALUE_STRING,
503                                new_uri_lst,
504                                NULL);
505         gconf_client_suggest_sync (recent->gconf_client, NULL);
506
507         /* delete from the global list */
508         if (recent->global)
509                 gnome_recent_model_delete (GNOME_RECENT_MODEL (recent->global), uri);
510
511
512         g_free (gconf_key);
513         gnome_recent_model_g_slist_deep_free (new_uri_lst);
514
515         return ret;
516 }
517
518 /**
519  * gnome_recent_model_get_list:
520  * @recent: A GnomeRecentModel object.
521  *
522  * This returns a linked list of strings (URIs) currently held
523  * by this object.
524  *
525  * Returns: A GSList *
526  */
527 GSList *
528 gnome_recent_model_get_list (GnomeRecentModel * recent)
529 {
530         GSList *uri_lst;
531         gchar *gconf_key = gnome_recent_model_gconf_key (recent);
532
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);
536
537         uri_lst = gconf_client_get_list (recent->gconf_client,
538                                        gconf_key,
539                                        GCONF_VALUE_STRING, NULL);
540
541         g_free (gconf_key);
542
543         /* FIXME:  This sucks. */
544         gnome_recent_model_monitor_uri_list (recent, uri_lst);
545
546         return uri_lst;
547 }
548
549
550
551 /**
552  * gnome_recent_model_set_limit:
553  * @recent: A GnomeRecentModel object.
554  * @limit: The maximum number of items allowed in the list.
555  *
556  * Use this function to constrain the number of items allowed in the list.
557  * The default is %GNOME_RECENT_MODEL_DEFAULT_LIMIT.
558  *
559  */
560 void
561 gnome_recent_model_set_limit (GnomeRecentModel *recent, gint limit)
562 {
563         GSList *list;
564         int len;
565         unsigned int i;
566
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;
571
572         list = gnome_recent_model_get_list (recent);
573         len = g_slist_length (list);
574
575         if (len <= limit) return;
576
577         /* if we're over the limit, delete from the end */
578         i=g_slist_length (list);
579         while (i > recent->limit)
580         {
581                 gchar *uri = g_slist_nth_data (list, i-1);
582                 gnome_recent_model_delete (recent, uri);
583
584                 i--;
585         }
586
587         gnome_recent_model_g_slist_deep_free (list);
588 }
589
590
591 /**
592  * gnome_recent_model_get_limit:
593  * @recent: A GnomeRecentModel object.
594  *
595  */
596 gint
597 gnome_recent_model_get_limit (GnomeRecentModel *recent)
598 {
599         g_return_val_if_fail (recent, -1);
600         g_return_val_if_fail (GNOME_IS_RECENT_MODEL (recent), -1);
601
602         return recent->limit;
603 }
604
605
606 /**
607  * gnome_recent_model_clear:
608  * @recent: A GnomeRecentModel object.
609  *
610  * This function clears the list of recently used URIs.
611  *
612  */
613 void
614 gnome_recent_model_clear (GnomeRecentModel *recent)
615 {
616         gchar *key;
617
618         g_return_if_fail (recent);
619         g_return_if_fail (recent->gconf_client);
620         g_return_if_fail (GNOME_IS_RECENT_MODEL (recent));
621
622         key = gnome_recent_model_gconf_key (recent);
623
624         gconf_client_unset (recent->gconf_client, key, NULL);
625 }
626
627 static void
628 gnome_recent_model_set_appname (GnomeRecentModel *recent, gchar *appname)
629 {
630         gchar *key;
631         gint notify_id;
632
633         g_return_if_fail (recent);
634         g_return_if_fail (appname);
635
636         recent->appname = appname;
637
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 ();
641         }
642
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,
648                                             key,
649                                             gnome_recent_model_notify_cb,
650                                             recent, NULL, NULL);
651
652
653
654         g_free (key);
655 }
656
657 static GSList *
658 gnome_recent_model_delete_from_list (GnomeRecentModel *recent, GSList *list,
659                                const gchar *uri)
660 {
661         unsigned int i;
662         gchar *text;
663
664         for (i = 0; i < g_slist_length (list); i++) {
665                 text = g_slist_nth_data (list, i);
666                 
667                 if (!strcmp (text, uri)) {
668                         list = g_slist_remove (list, text);
669                         g_free (text);
670                 }
671         }
672
673         return list;
674 }
675
676 /* this takes a list of GConfValues, and returns a list of strings */
677 static GSList *
678 gnome_recent_model_gconf_to_list (GConfValue* value)
679 {    
680         GSList* iter;
681         GSList *list = NULL;
682
683         g_return_val_if_fail (value, NULL);
684
685         iter = gconf_value_get_list(value);
686
687         while (iter != NULL)
688         {
689                 GConfValue* element = iter->data;
690                 gchar *text = g_strdup (gconf_value_get_string (element));
691
692                 list = g_slist_prepend (list, text);
693
694                 iter = g_slist_next(iter);
695         }
696
697         list = g_slist_reverse (list);
698
699         return list;
700 }
701
702 static void
703 gnome_recent_model_g_slist_deep_free (GSList *list)
704 {
705         GSList *lst;
706
707         if (list == NULL)
708                 return;
709
710         lst = list;
711         while (lst) {
712                 g_free (lst->data);
713                 lst->data = NULL;
714                 lst = lst->next;
715         }
716
717         g_slist_free (list);
718 }
719
720 static gchar *
721 gnome_recent_model_gconf_key (GnomeRecentModel * model)
722 {
723         gchar *key;
724
725         g_return_val_if_fail (model, NULL);
726
727         key = g_strdup_printf ("%s/%s", GNOME_RECENT_MODEL_BASE_KEY, model->appname);
728         return key;
729 }
730
731 /*
732 static void
733 print_list (GSList *list)
734 {
735         while (list) {
736                 g_print ("%s, ", (char *)list->data);
737
738                 list = list->next;
739         }
740         g_print ("\n\n");
741 }
742 */
743
744 /* this is the gconf notification callback. */
745 static void
746 gnome_recent_model_notify_cb (GConfClient *client, guint cnxn_id,
747                         GConfEntry *entry, gpointer user_data)
748 {
749         GSList *list=NULL;
750         GnomeRecentModel *recent = user_data;
751
752         if (entry->value == NULL) {
753                 g_signal_emit (G_OBJECT(recent), model_signals[CHANGED], 0, NULL);
754                 return;
755         }
756
757         list = gnome_recent_model_gconf_to_list (entry->value);
758
759         gnome_recent_model_monitor_uri_list (recent, list);
760
761         g_signal_emit (G_OBJECT(recent), model_signals[CHANGED], 0, list);
762
763         gnome_recent_model_g_slist_deep_free (list);
764 }
765
766
767 gchar *
768 gnome_recent_model_get_appname (GnomeRecentModel *model)
769 {
770         return g_strdup (model->appname);
771 }