source: trunk/third/eog/collection/eog-collection-model.c @ 19173

Revision 19173, 17.4 KB checked in by ghudson, 21 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r19172, which included commits to RCS files with non-trunk default branches.
Line 
1#include "eog-collection-model.h"
2#include "bonobo/bonobo-moniker-util.h"
3#include <libgnomevfs/gnome-vfs-ops.h>
4#include <libgnomevfs/gnome-vfs-directory.h>
5#include <libgnome/gnome-macros.h>
6
7#include "eog-collection-marshal.h"
8
9/* Signal IDs */
10enum {
11        IMAGE_CHANGED,
12        IMAGE_ADDED,
13        IMAGE_REMOVED,
14        SELECTION_CHANGED,
15        SELECTED_ALL,
16        SELECTED_NONE,
17        BASE_URI_CHANGED,
18        LAST_SIGNAL
19};
20
21static guint eog_model_signals[LAST_SIGNAL];
22
23typedef struct {
24        EogCollectionModel *model;
25        GnomeVFSURI *uri;
26        GnomeVFSFileInfo *info;
27        GnomeVFSHandle *handle;
28} LoadingContext;
29
30struct _EogCollectionModelPrivate {
31        /* holds for every id the corresponding cimage */
32        GHashTable *id_image_mapping;
33
34        GSList *selected_images;
35
36        /* base uri e.g. from a directory */
37        gchar *base_uri;
38};
39
40static void eog_collection_model_class_init (EogCollectionModelClass *klass);
41static void eog_collection_model_instance_init (EogCollectionModel *object);
42static void eog_collection_model_dispose (GObject *object);
43static void eog_collection_model_finalize (GObject *object);
44
45GNOME_CLASS_BOILERPLATE (EogCollectionModel, eog_collection_model,
46                         GObject, G_TYPE_OBJECT);
47
48static void
49free_hash_image (gpointer key, gpointer value, gpointer data)
50{
51        g_object_unref (G_OBJECT (value));
52}
53
54static void
55loading_context_free (LoadingContext *ctx)
56{
57        if (ctx->uri)
58                gnome_vfs_uri_unref (ctx->uri);
59        ctx->uri = NULL;
60
61        if (ctx->info)
62                gnome_vfs_file_info_unref (ctx->info);
63        ctx->info = NULL;
64       
65        if (ctx->handle)
66                gnome_vfs_close (ctx->handle);
67        ctx->handle = NULL;
68       
69        g_free (ctx);
70}
71
72static void
73eog_collection_model_dispose (GObject *obj)
74{
75        EogCollectionModel *model;
76       
77        g_return_if_fail (obj != NULL);
78        g_return_if_fail (EOG_IS_COLLECTION_MODEL (obj));
79
80        model = EOG_COLLECTION_MODEL (obj);
81
82        if (model->priv->selected_images)
83                g_slist_free (model->priv->selected_images);
84        model->priv->selected_images = NULL;
85
86        g_hash_table_foreach (model->priv->id_image_mapping,
87                              (GHFunc) free_hash_image, NULL);
88        g_hash_table_destroy (model->priv->id_image_mapping);
89        model->priv->id_image_mapping = NULL;
90
91        if (model->priv->base_uri)
92                g_free (model->priv->base_uri);
93        model->priv->base_uri = NULL;
94
95        GNOME_CALL_PARENT (G_OBJECT_CLASS, dispose, (obj));
96}
97
98static void
99eog_collection_model_finalize (GObject *obj)
100{
101        EogCollectionModel *model;
102       
103        g_return_if_fail (obj != NULL);
104        g_return_if_fail (EOG_IS_COLLECTION_MODEL (obj));
105
106        model = EOG_COLLECTION_MODEL (obj);
107
108        g_free (model->priv);
109
110        GNOME_CALL_PARENT (G_OBJECT_CLASS, finalize, (obj));
111}
112
113static void
114eog_collection_model_instance_init (EogCollectionModel *obj)
115{
116        EogCollectionModelPrivate *priv;
117
118        priv = g_new0(EogCollectionModelPrivate, 1);
119        priv->id_image_mapping = NULL;
120        priv->selected_images = NULL;
121        priv->base_uri = NULL;
122        obj->priv = priv;
123}
124
125static void
126eog_collection_model_class_init (EogCollectionModelClass *klass)
127{
128        GObjectClass *object_class = (GObjectClass*) klass;
129       
130        object_class->dispose = eog_collection_model_dispose;
131        object_class->finalize = eog_collection_model_finalize;
132
133        eog_model_signals[IMAGE_CHANGED] =
134                g_signal_new ("image-changed",
135                              G_TYPE_FROM_CLASS (object_class),
136                              G_SIGNAL_RUN_FIRST,
137                              G_STRUCT_OFFSET (EogCollectionModelClass, image_changed),
138                              NULL,
139                              NULL,
140                              eog_collection_marshal_VOID__INT,
141                              G_TYPE_NONE,
142                              1,
143                              G_TYPE_INT);
144        eog_model_signals[IMAGE_ADDED] =
145                g_signal_new ("image-added",
146                              G_TYPE_FROM_CLASS (object_class),
147                              G_SIGNAL_RUN_FIRST,
148                              G_STRUCT_OFFSET (EogCollectionModelClass, image_added),
149                              NULL,
150                              NULL,
151                              eog_collection_marshal_VOID__INT,
152                              G_TYPE_NONE,
153                              1,
154                              G_TYPE_INT);
155        eog_model_signals[IMAGE_REMOVED] =
156                g_signal_new ("image-removed",
157                              G_TYPE_FROM_CLASS (object_class),
158                              G_SIGNAL_RUN_FIRST,
159                              G_STRUCT_OFFSET (EogCollectionModelClass, image_removed),
160                              NULL,
161                              NULL,
162                              eog_collection_marshal_VOID__INT,
163                              G_TYPE_NONE,
164                              1,
165                              G_TYPE_INT);
166        eog_model_signals[SELECTION_CHANGED] =
167                g_signal_new ("selection-changed",
168                              G_TYPE_FROM_CLASS (object_class),
169                              G_SIGNAL_RUN_FIRST,
170                              G_STRUCT_OFFSET (EogCollectionModelClass, selection_changed),
171                              NULL,
172                              NULL,
173                              g_cclosure_marshal_VOID__INT,
174                              G_TYPE_NONE,
175                              1,
176                              G_TYPE_INT);
177        eog_model_signals[SELECTED_ALL] =
178                g_signal_new ("selected-all",
179                              G_TYPE_FROM_CLASS (object_class),
180                              G_SIGNAL_RUN_FIRST,
181                              G_STRUCT_OFFSET (EogCollectionModelClass, selected_all),
182                              NULL,
183                              NULL,
184                              g_cclosure_marshal_VOID__VOID,
185                              G_TYPE_NONE,
186                              0);
187        eog_model_signals[SELECTED_NONE] =
188                g_signal_new ("selected-none",
189                              G_TYPE_FROM_CLASS (object_class),
190                              G_SIGNAL_RUN_FIRST,
191                              G_STRUCT_OFFSET (EogCollectionModelClass, selected_none),
192                              NULL,
193                              NULL,
194                              g_cclosure_marshal_VOID__VOID,
195                              G_TYPE_NONE,
196                              0);
197        eog_model_signals[BASE_URI_CHANGED] =
198                g_signal_new ("base-uri-changed",
199                              G_TYPE_FROM_CLASS (object_class),
200                              G_SIGNAL_RUN_FIRST,
201                              G_STRUCT_OFFSET (EogCollectionModelClass, base_uri_changed),
202                              NULL,
203                              NULL,
204                              g_cclosure_marshal_VOID__VOID,
205                              G_TYPE_NONE,
206                              0);
207}
208
209void
210eog_collection_model_construct (EogCollectionModel *model)
211{
212        g_return_if_fail (model != NULL);
213        g_return_if_fail (EOG_IS_COLLECTION_MODEL (model));
214
215        /* init hash table */
216        model->priv->id_image_mapping = g_hash_table_new ((GHashFunc) g_direct_hash,
217                                                          (GCompareFunc) g_direct_equal);
218}
219
220EogCollectionModel*
221eog_collection_model_new (void)
222{
223        EogCollectionModel *model;
224       
225        model = EOG_COLLECTION_MODEL (g_object_new (EOG_TYPE_COLLECTION_MODEL, NULL));
226
227        eog_collection_model_construct (model);
228
229        return model;
230}
231
232typedef struct {
233        EogCollectionModel *model;
234        EogCollectionModelForeachFunc func;
235        gpointer data;
236        gboolean cont;
237} ForeachData;
238
239static void
240do_foreach (gpointer key, gpointer value, gpointer user_data)
241{
242        ForeachData *data = user_data;
243
244        if (data->cont)
245                data->cont = data->func (data->model, value,
246                                         data->data);
247}
248
249void
250eog_collection_model_foreach (EogCollectionModel *model,
251                              EogCollectionModelForeachFunc func,
252                              gpointer data)
253{
254        ForeachData *foreach_data;
255
256        g_return_if_fail (EOG_IS_COLLECTION_MODEL (model));
257
258        foreach_data = g_new0 (ForeachData, 1);
259        foreach_data->model = model;
260        foreach_data->func = func;
261        foreach_data->data = data;
262        foreach_data->cont = TRUE;
263        g_hash_table_foreach (model->priv->id_image_mapping, do_foreach,
264                              foreach_data);
265        g_free (foreach_data);
266}
267
268typedef struct {
269        EogCollectionModel *model;
270        GQuark id;
271} RemoveItemData;
272
273static gboolean
274remove_item_idle (gpointer user_data)
275{
276        EogCollectionModelPrivate *priv;
277        RemoveItemData *data = user_data;
278        CImage *image;
279
280        priv = data->model->priv;
281
282        image = g_hash_table_lookup (priv->id_image_mapping,
283                                     GINT_TO_POINTER (data->id));
284        if (!image) {
285                g_warning ("Could not find image %i!", data->id);
286                return FALSE;
287        }
288
289        g_hash_table_remove (priv->id_image_mapping, GINT_TO_POINTER(data->id));
290
291        if (g_slist_find (priv->selected_images, image)) {
292                priv->selected_images = g_slist_remove (priv->selected_images,
293                                                        image);
294        }
295
296        g_signal_emit_by_name (G_OBJECT (data->model),
297                               "image-removed", data->id);
298
299        g_free (data);
300
301        return FALSE;
302}
303
304void
305eog_collection_model_remove_item (EogCollectionModel *model, GQuark id)
306{
307        RemoveItemData *data;
308
309        g_return_if_fail (EOG_IS_COLLECTION_MODEL (model));
310
311        /*
312         * We need to do that in the idle loop as there could still be
313         * some loading or a g_hash_table_foreach running.
314         */
315        data = g_new0 (RemoveItemData, 1);
316        data->model = model;
317        data->id = id;
318        gtk_idle_add (remove_item_idle, data);
319}
320
321static gboolean
322directory_visit_cb (const gchar *rel_path,
323                    GnomeVFSFileInfo *info,
324                    gboolean recursing_will_loop,
325                    gpointer data,
326                    gboolean *recurse)
327{
328        CImage *img;
329        LoadingContext *ctx;
330        GnomeVFSURI *uri;
331        EogCollectionModel *model;
332        EogCollectionModelPrivate *priv;
333        GQuark id;
334        static gint count = 0;
335       
336        ctx = (LoadingContext*) data;
337        model = ctx->model;
338        priv = model->priv;
339
340        if (g_ascii_strncasecmp (info->mime_type, "image/", 6) != 0) {
341                return TRUE;
342        }
343               
344        uri = gnome_vfs_uri_append_file_name (ctx->uri, rel_path);     
345
346        img = cimage_new_uri (uri);                     
347        gnome_vfs_uri_unref (uri);
348        id = cimage_get_unique_id (img);
349
350        /* add image infos to internal lists */
351        g_hash_table_insert (priv->id_image_mapping,
352                             GINT_TO_POINTER (id),
353                             img);
354
355        g_signal_emit_by_name (G_OBJECT (model),
356                               "image-added", id);
357
358        if (count++ % 50 == 0)
359                while (gtk_events_pending ())
360                        gtk_main_iteration ();
361
362        return TRUE;
363}
364
365static gint
366real_dir_loading (LoadingContext *ctx)
367{
368        EogCollectionModel *model;
369        EogCollectionModelPrivate *priv;
370
371        g_return_val_if_fail (ctx->info->type == GNOME_VFS_FILE_TYPE_DIRECTORY, FALSE);
372
373        model = ctx->model;
374        priv = model->priv;
375
376        gnome_vfs_directory_visit_uri (ctx->uri,
377                                       GNOME_VFS_FILE_INFO_DEFAULT |
378                                       GNOME_VFS_FILE_INFO_FOLLOW_LINKS |
379                                       GNOME_VFS_FILE_INFO_GET_MIME_TYPE,
380                                       GNOME_VFS_DIRECTORY_VISIT_DEFAULT,
381                                       directory_visit_cb,
382                                       ctx);
383
384        loading_context_free (ctx);
385        return FALSE;
386}
387       
388
389static gint
390real_file_loading (LoadingContext *ctx)
391{
392        EogCollectionModel *model;
393        EogCollectionModelPrivate *priv;
394        GnomeVFSResult result;
395
396        g_return_val_if_fail (ctx->info->type == GNOME_VFS_FILE_TYPE_REGULAR, FALSE);
397
398        model = ctx->model;
399        priv = model->priv;
400
401        result = gnome_vfs_get_file_info_uri (ctx->uri,
402                                              ctx->info,
403                                              GNOME_VFS_FILE_INFO_GET_MIME_TYPE);
404
405        if (result != GNOME_VFS_OK) {
406                g_warning ("Error while obtaining file informations.\n");
407                return FALSE;
408        }
409       
410        if(g_ascii_strncasecmp(ctx->info->mime_type, "image/", 6) == 0) {
411                CImage *img;
412                GQuark id;
413
414                img = cimage_new_uri (ctx->uri);                       
415                id = cimage_get_unique_id (img);
416               
417                /* add image infos to internal lists */
418                g_hash_table_insert (priv->id_image_mapping,
419                                     GINT_TO_POINTER (id),
420                                     img);
421                g_signal_emit_by_name (G_OBJECT (model),
422                                       "image-added", id);
423        }
424
425        loading_context_free (ctx);
426
427        return FALSE;
428}
429
430
431static LoadingContext*
432prepare_context (EogCollectionModel *model, const gchar *text_uri)
433{
434        LoadingContext *ctx;
435        GnomeVFSFileInfo *info;
436        GnomeVFSResult result;
437
438        ctx = g_new0 (LoadingContext, 1);
439        ctx->uri = gnome_vfs_uri_new (text_uri);
440
441#ifdef COLLECTION_DEBUG
442        g_message ("Prepare context for URI: %s", text_uri);
443#endif
444
445        info = gnome_vfs_file_info_new ();
446        result = gnome_vfs_get_file_info_uri (ctx->uri, info,
447                                              GNOME_VFS_FILE_INFO_DEFAULT |
448                                              GNOME_VFS_FILE_INFO_FOLLOW_LINKS);
449        if (result != GNOME_VFS_OK) {
450                loading_context_free (ctx);
451                return NULL;
452        }
453
454        ctx->info = info;
455        ctx->handle = 0;
456        ctx->model = model;
457
458        return ctx;
459}
460
461void
462eog_collection_model_set_uri (EogCollectionModel *model,
463                              const gchar *text_uri)
464{
465        EogCollectionModelPrivate *priv;
466        LoadingContext *ctx;
467
468        g_return_if_fail (model != NULL);
469        g_return_if_fail (EOG_IS_COLLECTION_MODEL (model));
470        g_return_if_fail (text_uri != NULL);
471       
472        priv = model->priv;
473
474        ctx = prepare_context (model, text_uri);
475       
476        if (ctx != NULL) {
477                if (ctx->info->type == GNOME_VFS_FILE_TYPE_DIRECTORY)
478                        gtk_idle_add ((GtkFunction) real_dir_loading, ctx);
479                else if (ctx->info->type == GNOME_VFS_FILE_TYPE_REGULAR)
480                        gtk_idle_add ((GtkFunction) real_file_loading, ctx);
481                else {
482                        loading_context_free (ctx);
483                        g_warning (_("Can't handle URI: %s"), text_uri);
484                        return;
485                }
486        } else {
487                g_warning (_("Can't handle URI: %s"), text_uri);
488                return;
489        }       
490       
491        if (priv->base_uri == NULL) {
492                priv->base_uri = g_strdup (gnome_vfs_uri_get_path (ctx->uri));
493        }
494        else {
495                g_free (priv->base_uri);
496                priv->base_uri = g_strdup("multiple");
497        }
498        g_signal_emit_by_name (G_OBJECT (model), "base-uri-changed");
499}
500
501void
502eog_collection_model_set_uri_list (EogCollectionModel *model,
503                                   GList *uri_list)
504{
505        GList *node;
506        LoadingContext *ctx;
507        gchar *text_uri;
508
509        g_return_if_fail (model != NULL);
510        g_return_if_fail (EOG_IS_COLLECTION_MODEL (model));
511
512        node = uri_list;
513
514        while (node != NULL) {
515                text_uri = (gchar*) node->data;
516                ctx = prepare_context (model, text_uri);
517
518                if (ctx != NULL) {
519                        if (ctx->info->type == GNOME_VFS_FILE_TYPE_DIRECTORY)
520                                gtk_idle_add ((GtkFunction) real_dir_loading, ctx);
521                        else if (ctx->info->type == GNOME_VFS_FILE_TYPE_REGULAR)
522                                gtk_idle_add ((GtkFunction) real_file_loading, ctx);
523                        else {
524                                loading_context_free (ctx);
525                                g_warning (_("Can't handle URI: %s"), text_uri);
526                                return;
527                        }
528                } else {
529                        g_warning (_("Can't handle URI: %s"), text_uri);
530                        return;
531                }       
532
533                node = node->next;
534        }
535       
536        if (model->priv->base_uri != NULL)
537                g_free (model->priv->base_uri);
538        model->priv->base_uri = g_strdup ("multiple");
539        g_signal_emit_by_name (G_OBJECT (model), "base-uri-changed");
540}
541
542gint
543eog_collection_model_get_length (EogCollectionModel *model)
544{
545        g_return_val_if_fail (model != NULL, 0);
546        g_return_val_if_fail (EOG_IS_COLLECTION_MODEL (model), 0);
547
548        return g_hash_table_size (model->priv->id_image_mapping);
549}
550
551
552CImage*
553eog_collection_model_get_image (EogCollectionModel *model,
554                                GQuark id)
555{
556        g_return_val_if_fail (model != NULL, NULL);
557        g_return_val_if_fail (EOG_IS_COLLECTION_MODEL (model), NULL);
558
559        return CIMAGE (g_hash_table_lookup (model->priv->id_image_mapping,
560                                            GINT_TO_POINTER (id)));
561}
562
563gchar*
564eog_collection_model_get_uri (EogCollectionModel *model,
565                              GQuark id)
566{
567        CImage *img;
568        GnomeVFSURI *uri;
569        char *txt_uri;
570
571        g_return_val_if_fail (model != NULL, NULL);
572        g_return_val_if_fail (EOG_IS_COLLECTION_MODEL (model), NULL);
573
574        img = eog_collection_model_get_image (model, id);
575        if (img == NULL) return NULL;
576       
577        uri = cimage_get_uri (img);
578        txt_uri = gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_NONE);
579        gnome_vfs_uri_unref (uri);
580       
581        return txt_uri;
582}
583
584static gboolean
585add_image_to_selection_list (EogCollectionModel *model, CImage *img, gpointer data)
586{
587        EogCollectionModelPrivate *priv;
588
589        priv = model->priv;
590
591        priv->selected_images = g_slist_append (priv->selected_images, img);
592
593        return TRUE;
594}
595
596static void
597select_all_images (EogCollectionModel *model)
598{
599        EogCollectionModelPrivate *priv;
600
601        priv = model->priv;
602
603        if (priv->selected_images != NULL) {
604                g_slist_free (priv->selected_images);
605                priv->selected_images = NULL;
606        }
607
608        eog_collection_model_foreach (model, add_image_to_selection_list, NULL);
609
610        g_signal_emit_by_name (G_OBJECT (model), "selected-all");
611}
612
613static void
614unselect_all_images (EogCollectionModel *model)
615{
616        EogCollectionModelPrivate *priv;
617        GSList *node;
618        CImage *img;
619        gboolean single_change_signal;
620
621        priv = model->priv;
622
623        single_change_signal = (eog_collection_model_get_selected_length (model) <
624                                (eog_collection_model_get_length (model) / 2));
625
626        for (node = priv->selected_images; node; node = node->next) {
627                g_return_if_fail (IS_CIMAGE (node->data));
628
629                img = CIMAGE (node->data);
630
631                cimage_set_select_status (img, FALSE);
632
633                if (single_change_signal) {
634                        g_signal_emit_by_name (G_OBJECT (model), "selection-changed",
635                                               cimage_get_unique_id (img));
636                }
637        }
638
639        if (priv->selected_images)
640                g_slist_free (priv->selected_images);
641        priv->selected_images = NULL;
642
643        if (!single_change_signal) {
644                g_signal_emit_by_name (G_OBJECT (model), "selected-none");
645        }
646}
647
648void
649eog_collection_model_set_select_status (EogCollectionModel *model,
650                                        GQuark id, gboolean status)
651{
652        CImage *image;
653
654        g_return_if_fail (EOG_IS_COLLECTION_MODEL (model));
655
656        image = eog_collection_model_get_image (model, id);
657        if (status == cimage_is_selected (image))
658                return;
659
660        cimage_set_select_status (image, status);
661        if (status)
662                model->priv->selected_images = g_slist_append (
663                                        model->priv->selected_images, image);
664        else
665                model->priv->selected_images = g_slist_remove (
666                                        model->priv->selected_images, image);
667
668        g_signal_emit_by_name (G_OBJECT (model), "selection-changed",
669                               cimage_get_unique_id (image));
670}
671
672void
673eog_collection_model_set_select_status_all (EogCollectionModel *model,
674                                            gboolean status)
675{
676        g_return_if_fail (model != NULL);
677        g_return_if_fail (EOG_IS_COLLECTION_MODEL (model));
678
679        if (status)
680                select_all_images (model);
681        else
682                unselect_all_images (model);
683}
684
685
686void eog_collection_model_toggle_select_status (EogCollectionModel *model,
687                                                GQuark id)
688{
689        EogCollectionModelPrivate *priv;
690        CImage *image;
691
692        g_return_if_fail (model != NULL);
693        g_return_if_fail (EOG_IS_COLLECTION_MODEL (model));
694
695        priv = model->priv;
696
697        image = eog_collection_model_get_image (model, id);
698        cimage_toggle_select_status (image);
699        if (cimage_is_selected (image)) {
700                priv->selected_images =
701                        g_slist_append (priv->selected_images,
702                                        image);
703        } else {
704                priv->selected_images =
705                        g_slist_remove (priv->selected_images,
706                                        image);
707        }
708
709        g_signal_emit_by_name (G_OBJECT (model), "selection-changed",
710                               cimage_get_unique_id (image));
711}
712
713
714gchar*
715eog_collection_model_get_base_uri (EogCollectionModel *model)
716{
717        g_return_val_if_fail (model != NULL, NULL);
718        g_return_val_if_fail (EOG_IS_COLLECTION_MODEL (model), NULL);
719
720        if (!model->priv->base_uri)
721                return NULL;
722        else
723                return model->priv->base_uri;
724}
725
726
727gint
728eog_collection_model_get_selected_length (EogCollectionModel *model)
729{
730        g_return_val_if_fail (model != NULL, 0);
731        g_return_val_if_fail (EOG_IS_COLLECTION_MODEL (model), 0);
732       
733        return g_slist_length (model->priv->selected_images);
734}
735
736CImage*
737eog_collection_model_get_selected_image (EogCollectionModel *model)
738{
739        g_return_val_if_fail (model != NULL, 0);
740        g_return_val_if_fail (EOG_IS_COLLECTION_MODEL (model), 0);
741       
742        if (eog_collection_model_get_selected_length (model) == 1) {
743                return CIMAGE (model->priv->selected_images->data);
744        } else
745                return NULL;
746}
Note: See TracBrowser for help on using the repository browser.