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

Revision 19173, 29.7 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/* Eog Of Gnome - view of the image collection
2 *
3 * Copyright (C) 2001 The Free Software Foundation
4 *
5 * Author: Jens Finke <jens@gnome.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 */
21#include <config.h>
22#include <glib/gmain.h>
23#include <libgnome/gnome-macros.h>
24
25#include "eog-wrap-list.h"
26#include "eog-collection-marshal.h"
27#include <math.h>
28
29#define COLLECTION_DEBUG 0
30
31
32/* Used to hold signal handler IDs for models */
33typedef enum {
34        MODEL_SIGNAL_IMAGE_CHANGED,
35        MODEL_SIGNAL_IMAGE_ADDED,
36        MODEL_SIGNAL_IMAGE_REMOVED,
37        MODEL_SIGNAL_SELECTION_CHANGED,
38        MODEL_SIGNAL_SELECTED_ALL,
39        MODEL_SIGNAL_SELECTED_NONE,
40        MODEL_SIGNAL_LAST
41} ModelSignal;
42
43enum {
44        RIGHT_CLICK,
45        DOUBLE_CLICK,
46        LAST_SIGNAL
47};
48
49static guint eog_wrap_list_signals [LAST_SIGNAL];
50
51enum {
52        GLOBAL_SIZE_CHANGED,
53        GLOBAL_SPACING_CHANGED,
54        GLOBAL_FACTORY_CHANGED,
55        GLOBAL_MODEL_CHANGED,
56        GLOBAL_LAYOUT_MODE_CHANGED,
57        GLOBAL_HINT_LAST
58};
59
60typedef struct {
61        ModelSignal  hint;
62        GQuark       id;
63} ItemUpdate;
64
65struct _EogWrapListPrivate {
66        /* List of all items currently in the view. */
67        GHashTable *item_table;
68
69        /* Sorted list of items */
70        GSList *view_order;
71       
72        /* Factory to use. */
73        EogItemFactory *factory;
74
75        /* Model to use. */
76        EogCollectionModel *model;
77
78        /* Signal connection IDs for the models */
79        int model_ids[MODEL_SIGNAL_LAST];
80       
81        /* Group which hold all the items. */
82        GnomeCanvasItem *item_group;
83
84        /* spacing between items */
85        guint row_spacing;
86        guint col_spacing;
87
88        /* size of items */
89        guint item_width;
90        guint item_height;
91
92        /* Number of items */
93        guint n_items;
94
95        /* Layout mode to use for row/col calculating. */
96        EogLayoutMode lm;
97
98        /* Number of rows. */
99        guint n_rows;
100
101        /* Number of columns. */
102        guint n_cols;
103
104        /* Id for the idle handler.*/
105        gint idle_handler_id;
106
107        /* Update hints */
108        gboolean global_update_hints [GLOBAL_HINT_LAST];
109
110        gboolean is_updating;
111
112        /* unique IDs to update */
113        GList *item_update_list;
114
115        /* last id of thumbnail the user clicked */
116        guint last_id_clicked;
117};
118
119static void eog_wrap_list_class_init (EogWrapListClass *class);
120static void eog_wrap_list_instance_init (EogWrapList *wlist);
121static void eog_wrap_list_dispose (GObject *object);
122static void eog_wrap_list_finalize (GObject *object);
123
124static void eog_wrap_list_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
125
126static void request_update (EogWrapList *wlist);
127static void do_update (EogWrapList *wlist);
128static gint handle_canvas_click (GnomeCanvas *canvas, GdkEventButton *event, gpointer data);
129
130GNOME_CLASS_BOILERPLATE (EogWrapList,
131                         eog_wrap_list,
132                         GnomeCanvas,
133                         GNOME_TYPE_CANVAS);
134
135/* Class initialization function for the abstract wrapped list view */
136static void
137eog_wrap_list_class_init (EogWrapListClass *class)
138{
139        GObjectClass *object_class;
140        GtkWidgetClass *widget_class;
141
142        object_class = (GObjectClass *) class;
143        widget_class = (GtkWidgetClass *) class;
144
145        object_class->dispose = eog_wrap_list_dispose;
146        object_class->finalize = eog_wrap_list_finalize;
147
148        widget_class->size_allocate = eog_wrap_list_size_allocate;
149
150        eog_wrap_list_signals [RIGHT_CLICK] =
151                g_signal_new ("right_click",
152                              G_TYPE_FROM_CLASS(object_class),
153                              G_SIGNAL_RUN_LAST,
154                              G_STRUCT_OFFSET (EogWrapListClass, right_click),
155                              NULL,
156                              NULL,
157                              eog_collection_marshal_BOOLEAN__INT_POINTER,
158                              G_TYPE_BOOLEAN,
159                              2,
160                              G_TYPE_INT,
161                              G_TYPE_POINTER);
162        eog_wrap_list_signals [DOUBLE_CLICK] =
163                g_signal_new ("double_click",
164                              G_TYPE_FROM_CLASS(object_class),
165                              G_SIGNAL_RUN_FIRST,
166                              G_STRUCT_OFFSET (EogWrapListClass, double_click),
167                              NULL,
168                              NULL,
169                              eog_collection_marshal_VOID__INT,
170                              G_TYPE_NONE,
171                              1,
172                              G_TYPE_INT);
173}
174
175/* object initialization function for the abstract wrapped list view */
176static void
177eog_wrap_list_instance_init (EogWrapList *wlist)
178{
179        EogWrapListPrivate *priv;
180
181        priv = g_new0 (EogWrapListPrivate, 1);
182        wlist->priv = priv;
183       
184        priv->item_table = NULL;
185        priv->view_order = NULL;
186        priv->model = NULL;
187        priv->factory = NULL;
188        priv->row_spacing = 0;
189        priv->col_spacing = 0;
190        priv->idle_handler_id = -1;
191        priv->is_updating = FALSE;
192        priv->n_items = 0;
193        priv->lm = EOG_LAYOUT_MODE_VERTICAL;
194        priv->n_rows = 0;
195        priv->n_cols = 0;
196        priv->last_id_clicked = -1;
197}
198
199/* Destroy handler for the abstract wrapped list view */
200static void
201eog_wrap_list_dispose (GObject *object)
202{
203        EogWrapList *wlist;
204        EogWrapListPrivate *priv;
205
206        g_return_if_fail (object != NULL);
207        g_return_if_fail (EOG_IS_WRAP_LIST (object));
208
209        wlist = EOG_WRAP_LIST (object);
210        priv = wlist->priv;
211
212        if (priv->model)
213                g_object_unref (G_OBJECT (priv->model));
214        priv->model = NULL;
215
216        if (priv->factory)
217                g_object_unref (G_OBJECT (priv->factory));
218        priv->factory = NULL;
219
220        /* FIXME: free the items and item array */
221       
222        GNOME_CALL_PARENT (G_OBJECT_CLASS, dispose, (object));
223}
224
225static void
226eog_wrap_list_finalize (GObject *object)
227{
228        EogWrapList *wlist;
229
230        g_return_if_fail (object != NULL);
231        g_return_if_fail (EOG_IS_WRAP_LIST (object));
232
233        wlist = EOG_WRAP_LIST (object);
234        if (wlist->priv)
235                g_free (wlist->priv);
236        wlist->priv = NULL;
237
238        if (G_OBJECT_CLASS (parent_class)->finalize)
239                (* G_OBJECT_CLASS (parent_class)->finalize) (object);
240}
241
242static void
243eog_wrap_list_construct (EogWrapList *wlist)
244{
245        wlist->priv->item_table = g_hash_table_new ((GHashFunc) g_direct_hash,
246                                                    (GCompareFunc) g_direct_equal);
247
248        gtk_widget_set_events (GTK_WIDGET (wlist), GDK_ALL_EVENTS_MASK);
249        g_signal_connect_after (G_OBJECT (wlist),
250                                "button-press-event",
251                                G_CALLBACK (handle_canvas_click),
252                                wlist);
253}
254
255
256GtkWidget*
257eog_wrap_list_new (void)
258{
259        GtkWidget *wlist;
260
261        wlist = gtk_widget_new (eog_wrap_list_get_type (), NULL);
262
263        eog_wrap_list_construct (EOG_WRAP_LIST (wlist));
264       
265        return wlist;
266}
267
268static GnomeCanvasItem*
269get_item_by_unique_id (EogWrapList *wlist,
270                       GQuark id)
271{
272        GnomeCanvasItem *item;
273
274        g_return_val_if_fail (EOG_IS_WRAP_LIST (wlist), NULL);
275       
276        item = g_hash_table_lookup (wlist->priv->item_table,
277                                    GINT_TO_POINTER (id));
278        if (!item)
279                g_warning ("Could not find item %i!", id);
280
281        return item;
282}
283
284static gint
285get_item_view_position (EogWrapList *wlist, GnomeCanvasItem *item)
286{
287        EogWrapListPrivate *priv;
288        double x1, y1, x2, y2;
289        gint row, col, n;
290       
291        priv = wlist->priv;
292        gnome_canvas_item_get_bounds (item, &x1, &y1, &x2, &y2);
293
294        col = x1 / (priv->item_width + priv->col_spacing);
295        row = y1 / (priv->item_height + priv->row_spacing);
296
297        n = col + priv->n_cols * row;
298
299        return n;
300}
301
302static gint
303handle_canvas_click (GnomeCanvas *canvas, GdkEventButton *event, gpointer data)
304{
305        EogWrapList *wlist;
306        EogCollectionModel *model;
307
308        g_return_val_if_fail (data != NULL, FALSE);
309        g_return_val_if_fail (EOG_IS_WRAP_LIST (data), FALSE);
310
311        wlist = EOG_WRAP_LIST (data);
312        model = wlist->priv->model;
313        if (model == NULL) return FALSE;
314
315        eog_collection_model_set_select_status_all (model, FALSE);
316        wlist->priv->last_id_clicked = -1;
317       
318        return TRUE;
319}
320
321static gint
322handle_item_event (GnomeCanvasItem *item, GdkEvent *event,  EogWrapList *wlist)
323{
324        EogWrapListPrivate *priv;
325        EogCollectionModel *model;
326        GQuark id;
327        gboolean ret_val;
328
329        g_return_val_if_fail (EOG_IS_WRAP_LIST (wlist), FALSE);
330
331        priv = wlist->priv;
332        model = priv->model;
333        if (model == NULL) return FALSE;
334
335        id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item), "ImageID"));
336
337        switch (event->type) {
338        case GDK_BUTTON_PRESS:
339                switch (event->button.button) {
340                case 3:
341                        /* Right click */
342                        eog_collection_model_set_select_status (model, id, TRUE);
343                        ret_val = FALSE;
344                        g_signal_emit (GTK_OBJECT (wlist),
345                                       eog_wrap_list_signals [RIGHT_CLICK], 0,
346                                       id, event, &ret_val);
347                        g_signal_stop_emission_by_name (G_OBJECT (item->canvas),
348                                                        "button-press-event");
349                        return ret_val;
350                case 2:
351                        /* Middle button */
352                        g_warning ("Implement D&D!");
353                        break;
354                case 1:
355                        /* Left click */
356                        if (event->button.state & GDK_SHIFT_MASK) {
357                                /* shift key pressed */
358                                if (wlist->priv->last_id_clicked == -1)
359                                        eog_collection_model_toggle_select_status (model, id);
360                                else {
361                                        GnomeCanvasItem *prev_item;
362                                        GSList *node = NULL;
363                                        gint prev_n, n;
364                                        gint i;
365                               
366                                        prev_item = get_item_by_unique_id (
367                                                wlist, priv->last_id_clicked);
368                                        prev_n = get_item_view_position (
369                                                        wlist, prev_item);
370                                        n = get_item_view_position(wlist, item);
371
372                                        /*
373                                         * assert that always prev_n <= n
374                                         * is valid
375                                         */
376                                        if (n < prev_n) {
377                                                gint tmp;
378                                                tmp = prev_n;
379                                                prev_n = n - 1;
380                                                n = tmp - 1;
381                                        }
382
383                                        /* init variables */
384                                        if (prev_n == -1) {
385                                                /*
386                                                 * happens if n == 0 and
387                                                 * prev_n > 0
388                                                 */
389                                                node = priv->view_order;
390                                                i = 0;
391                                        } else {
392                                                node = g_slist_nth (
393                                                        priv->view_order,
394                                                        prev_n);
395                                                node = node->next;
396                                                i = ++prev_n;
397                                        }
398                               
399                                        /*
400                                         * change select status for items in
401                                         * range (prev_n, n]
402                                         */
403                                        while (node && (i <= n)) {
404                                                GnomeCanvasItem *item;
405                                                gint id;
406                                       
407                                                item = GNOME_CANVAS_ITEM (
408                                                                node->data);
409                                                id = GPOINTER_TO_INT (
410                                                        g_object_get_data (
411                                                        G_OBJECT (item),
412                                                        "ImageID"));
413                                                eog_collection_model_toggle_select_status (model, id);
414
415                                                node = node->next;
416                                                i++;
417                                        }
418                                }
419                        } else if (event->button.state & GDK_CONTROL_MASK) {
420                                /* control key pressed */
421               
422                                /* add item with id to selection*/
423                                eog_collection_model_toggle_select_status (
424                                                                model, id);
425                        } else {
426                                /* clear selection */
427                                eog_collection_model_set_select_status_all (
428                                                                model, FALSE);
429
430                                /* select only item with id */
431                                eog_collection_model_toggle_select_status (
432                                                                model, id);
433                        }
434
435                        wlist->priv->last_id_clicked = id;
436
437                        /*
438                         * stop further event handling through
439                         * handle_canvas_click
440                         */
441                        g_signal_stop_emission_by_name (G_OBJECT (item->canvas),
442                                                        "button-press-event");
443                }
444
445                break;
446                       
447        case GDK_2BUTTON_PRESS:
448                /* stop further event handling through handle_canvas_click */
449                g_signal_stop_emission_by_name (G_OBJECT (item->canvas), "button-press-event");
450
451                g_signal_emit (G_OBJECT (wlist),
452                               eog_wrap_list_signals [DOUBLE_CLICK], 0,
453                               id);
454                break;
455               
456        default:
457                return FALSE;
458        }
459
460        return TRUE;
461}
462
463#if 0
464/* Size_request handler for the abstract wrapped list view */
465static void
466eog_wrap_list_size_request (GtkWidget *widget, GtkRequisition *requisition)
467{
468        EogWrapList *wlist;
469        EogWrapListPrivate *priv;
470        int border_width;
471
472        wlist = EOG_WRAP_LIST (widget);
473        priv = wlist->priv;
474
475        gtk_widget_size_request (GTK_WIDGET (wlist), requisition);
476
477        border_width = GTK_CONTAINER (widget)->border_width;
478
479        requisition->width += 2 * border_width;
480        requisition->height += 2 * border_width;
481}
482#endif
483
484/* Size_allocate handler for the abstract wrapped list view */
485static void
486eog_wrap_list_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
487{
488        EogWrapList *wlist;
489        EogWrapListPrivate *priv;
490
491        wlist = EOG_WRAP_LIST (widget);
492        priv = wlist->priv;
493
494        if (GTK_WIDGET_CLASS (parent_class)->size_allocate)
495                GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);
496
497        priv->global_update_hints[GLOBAL_SIZE_CHANGED] = TRUE;
498
499        request_update (wlist);
500}       
501
502/* Notifications from models */
503static void
504add_item_update (EogWrapList *wlist, EogCollectionModel *model, GQuark id, ModelSignal signal)
505{
506        EogWrapListPrivate *priv;
507        ItemUpdate *update;
508
509        g_return_if_fail (EOG_IS_COLLECTION_MODEL (model));
510        g_return_if_fail (EOG_IS_WRAP_LIST (wlist));
511
512        priv = wlist->priv;
513
514        update       = g_new0 (ItemUpdate, 1);
515        update->hint = signal;
516        update->id   = id;
517       
518        priv->item_update_list = g_list_prepend (priv->item_update_list, update);
519
520        request_update (wlist);
521}
522
523
524/* Handler for the interval_changed signal from models */
525static void
526model_image_changed (EogCollectionModel *model, GQuark id, gpointer data)
527{
528        EogWrapListPrivate *priv;
529
530        g_return_if_fail (EOG_IS_WRAP_LIST (data));
531
532#if COLLECTION_DEBUG
533        g_message ("model_interval_changed called");
534#endif
535        priv = EOG_WRAP_LIST (data)->priv;
536
537        if (priv->global_update_hints[GLOBAL_FACTORY_CHANGED] ||
538            priv->global_update_hints[GLOBAL_MODEL_CHANGED])
539        {
540                /* if any global changes have to be done already, we don't need
541                   to add any specific item changes */
542                return;
543        }
544        else {
545                add_item_update (EOG_WRAP_LIST (data), model, id, MODEL_SIGNAL_IMAGE_CHANGED);
546        }
547}
548
549/* Handler for the interval_added signal from models */
550static void
551model_image_added (EogCollectionModel *model, GQuark id, gpointer data)
552{
553        g_return_if_fail (EOG_IS_WRAP_LIST (data));
554
555#if COLLECTION_DEBUG
556        g_message ("model_interval_added called\n");
557#endif
558
559        add_item_update (EOG_WRAP_LIST (data), model, id, MODEL_SIGNAL_IMAGE_ADDED);
560}
561
562/* Handler for the interval_changed signal from models */
563static void
564model_image_removed (EogCollectionModel *model, GQuark id, gpointer data)
565{
566        g_return_if_fail (EOG_IS_WRAP_LIST (data));
567
568#if COLLECTION_DEBUG
569        g_message ("model_interval_removed called\n");
570#endif
571       
572        add_item_update (EOG_WRAP_LIST (data), model, id, MODEL_SIGNAL_IMAGE_REMOVED);
573}
574
575
576
577static void
578model_selection_changed (EogCollectionModel *model, GQuark id, gpointer data)
579{
580        g_return_if_fail (EOG_IS_WRAP_LIST (data));
581
582#if COLLECTION_DEBUG
583        g_message ("model_selection_changed called");
584#endif
585
586        add_item_update (EOG_WRAP_LIST (data), model, id, MODEL_SIGNAL_SELECTION_CHANGED);
587}
588
589static void
590model_selected_all (EogCollectionModel *model, gpointer data)
591{
592        g_return_if_fail (EOG_IS_WRAP_LIST (data));
593
594#if COLLECTION_DEBUG
595        g_message ("model_selected_all called");
596#endif
597
598        add_item_update (EOG_WRAP_LIST (data), model, 0, MODEL_SIGNAL_SELECTED_ALL);
599}
600
601static void
602model_selected_none (EogCollectionModel *model, gpointer data)
603{
604        g_return_if_fail (EOG_IS_WRAP_LIST (data));
605
606#if COLLECTION_DEBUG
607        g_message ("model_selected_none called");
608#endif
609
610        add_item_update (EOG_WRAP_LIST (data), model, 0, MODEL_SIGNAL_SELECTED_NONE);
611}
612
613/* Set model handler for the wrapped list view */
614void
615eog_wrap_list_set_model (EogWrapList *wlist, EogCollectionModel *model)
616{
617        EogWrapListPrivate *priv;
618        int i;
619
620        g_return_if_fail (wlist != NULL);
621        g_return_if_fail (EOG_IS_WRAP_LIST (wlist));
622
623        priv = wlist->priv;
624
625        if (priv->model) {
626                for (i = 0; i < MODEL_SIGNAL_LAST; i++) {
627                        g_signal_handler_disconnect (G_OBJECT (priv->model), priv->model_ids[i]);
628                        priv->model_ids[i] = 0;
629                }
630        }
631        priv->model = NULL;
632
633        if (model) {
634                priv->model = model;
635                g_object_ref (G_OBJECT (model));
636
637                priv->model_ids[MODEL_SIGNAL_IMAGE_CHANGED] = g_signal_connect (
638                        G_OBJECT (model), "image-changed",
639                        G_CALLBACK (model_image_changed),
640                        wlist);
641
642                priv->model_ids[MODEL_SIGNAL_IMAGE_ADDED] = g_signal_connect (
643                        G_OBJECT (model), "image-added",
644                        G_CALLBACK (model_image_added),
645                        wlist);
646
647                priv->model_ids[MODEL_SIGNAL_IMAGE_REMOVED] = g_signal_connect (
648                        G_OBJECT (model), "image-removed",
649                        G_CALLBACK (model_image_removed),
650                        wlist);
651
652                priv->model_ids[MODEL_SIGNAL_SELECTION_CHANGED] = g_signal_connect (
653                        G_OBJECT (model), "selection-changed",
654                        G_CALLBACK (model_selection_changed),
655                        wlist);
656
657                priv->model_ids[MODEL_SIGNAL_SELECTED_ALL] = g_signal_connect (
658                        G_OBJECT (model), "selected-all",
659                        G_CALLBACK (model_selected_all),
660                        wlist);
661
662                priv->model_ids[MODEL_SIGNAL_SELECTION_CHANGED] = g_signal_connect (
663                        G_OBJECT (model), "selected-none",
664                        G_CALLBACK (model_selected_none),
665                        wlist);
666        }
667
668        priv->global_update_hints[GLOBAL_MODEL_CHANGED] = TRUE;
669
670        request_update (wlist);
671}
672
673void
674eog_wrap_list_set_factory (EogWrapList *wlist, EogItemFactory *factory)
675{
676        EogWrapListPrivate *priv;
677
678        g_return_if_fail (wlist != NULL);
679        g_return_if_fail (EOG_IS_WRAP_LIST (wlist));
680
681        priv = wlist->priv;
682
683        if (priv->factory) {
684                g_object_unref (G_OBJECT (priv->factory));
685                priv->factory = NULL;
686        }
687
688        if (factory) {
689                priv->factory = factory;
690                g_object_ref (G_OBJECT (factory));
691                eog_item_factory_get_item_size (priv->factory,
692                                                &priv->item_width,
693                                                &priv->item_height);
694        }
695
696        priv->global_update_hints[GLOBAL_MODEL_CHANGED] = TRUE;
697
698        request_update (wlist);
699}
700
701void
702eog_wrap_list_set_layout_mode (EogWrapList *wlist, EogLayoutMode lm)
703{
704        EogWrapListPrivate *priv;
705
706        g_return_if_fail (wlist != NULL);
707        g_return_if_fail (EOG_IS_WRAP_LIST (wlist));
708
709        priv = wlist->priv;
710
711        if (lm == priv->lm) return;
712
713        priv->lm = lm;
714        priv->global_update_hints[GLOBAL_LAYOUT_MODE_CHANGED] = TRUE;
715
716        request_update (wlist);
717}
718
719void
720eog_wrap_list_set_background_color (EogWrapList *wlist, GdkColor *color)
721{
722       
723        g_return_if_fail (wlist != NULL);
724        g_return_if_fail (EOG_IS_WRAP_LIST (wlist));
725        g_return_if_fail (color != NULL);
726
727        /* try to alloc color */
728        if(gdk_colormap_alloc_color (gdk_colormap_get_system (), color, FALSE, TRUE))
729        {
730                GtkStyle *style;
731                style = gtk_style_copy (gtk_widget_get_style (GTK_WIDGET (wlist)));
732
733                /* set new style */
734                style->bg[GTK_STATE_NORMAL] = *color;
735                gtk_widget_set_style (GTK_WIDGET (wlist), style);
736        }
737}
738
739/**
740 * eog_wrap_list_set_row_spacing:
741 * @wlist: A wrapped list view.
742 * @spacing: Spacing between rows in pixels.
743 *
744 * Sets the spacing between the rows of a wrapped list view.
745 **/
746void
747eog_wrap_list_set_row_spacing (EogWrapList *wlist, guint spacing)
748{
749        EogWrapListPrivate *priv;
750
751        g_return_if_fail (wlist != NULL);
752        g_return_if_fail (EOG_IS_WRAP_LIST (wlist));
753
754        priv = wlist->priv;
755        priv->row_spacing = spacing;
756
757        priv->global_update_hints[GLOBAL_SPACING_CHANGED] = TRUE;
758
759        request_update (wlist);
760}
761
762/**
763 * eog_wrap_list_set_col_spacing:
764 * @wlist: A wrapped list view.
765 * @spacing: Spacing between columns in pixels.
766 *
767 * Sets the spacing between the columns of a wrapped list view.
768 **/
769void
770eog_wrap_list_set_col_spacing (EogWrapList *wlist, guint spacing)
771{
772        EogWrapListPrivate *priv;
773
774        g_return_if_fail (wlist != NULL);
775        g_return_if_fail (EOG_IS_WRAP_LIST (wlist));
776
777        priv = wlist->priv;
778        priv->col_spacing = spacing;
779
780        priv->global_update_hints[GLOBAL_SPACING_CHANGED] = TRUE;
781
782        request_update (wlist);
783}
784
785#if 0
786static
787void eog_wrap_list_clear (EogWrapList *wlist)
788{
789        EogWrapListPrivate *priv;
790
791        g_return_if_fail (wlist != NULL);
792        g_return_if_fail (EOG_IS_WRAP_LIST (wlist));
793
794        priv = wlist->priv;
795
796        gtk_object_destroy (GTK_OBJECT (priv->item_group));
797        priv->item_group =
798                gnome_canvas_item_new (gnome_canvas_root (GNOME_CANVAS (wlist)),       
799                                       gnome_canvas_group_get_type (),
800                                       "x", 0.0,
801                                       "y", 0.0,
802                                       NULL);
803                                                 
804        g_hash_table_destroy (priv->item_table);
805        priv->item_table = g_hash_table_new ((GHashFunc) g_direct_hash,
806                                             (GCompareFunc) g_direct_equal);
807}
808#endif
809
810static void
811request_update (EogWrapList *wlist)
812{
813        g_return_if_fail (wlist != NULL);
814        g_return_if_fail (EOG_IS_WRAP_LIST (wlist));
815
816#if COLLECTION_DEBUG
817        g_message ("request_update called.");
818#endif
819
820        if ((wlist->priv->idle_handler_id == -1) &&
821            (!wlist->priv->is_updating))
822                wlist->priv->idle_handler_id = gtk_idle_add (
823                                        (GtkFunction) do_update, wlist);
824}
825
826static gint
827compare_item_caption (const GnomeCanvasItem *item1, const GnomeCanvasItem *item2)
828{
829        gchar *cap1;
830        gchar *cap2;
831
832        cap1 = (gchar*) g_object_get_data (G_OBJECT (item1), "Caption");
833        cap2 = (gchar*) g_object_get_data (G_OBJECT (item2), "Caption");
834       
835        return g_ascii_strcasecmp (cap1, cap2);
836}
837
838static void
839do_item_changed_update (EogWrapList *wlist,
840                        GQuark id,
841                        gboolean *layout_check_needed,
842                        gboolean *item_rearrangement_needed)
843{
844        GnomeCanvasItem *item;
845
846        *layout_check_needed = FALSE;
847        *item_rearrangement_needed = FALSE;
848
849        g_return_if_fail (wlist != NULL);
850        g_return_if_fail (EOG_IS_WRAP_LIST (wlist));
851
852#if COLLECTION_DEBUG
853        g_message ("do_item_changed_update called - id:%i", id);
854#endif
855
856        if (wlist->priv->factory == NULL) return;
857
858        item = get_item_by_unique_id (wlist, id);
859
860        eog_item_factory_update_item (wlist->priv->factory,
861                                      wlist->priv->model,
862                                      item,
863                                      EOG_ITEM_UPDATE_IMAGE | EOG_ITEM_UPDATE_CAPTION);
864}
865
866static void
867do_item_removed_update (EogWrapList *wlist,
868                        GQuark id,
869                        gboolean *layout_check_needed,
870                        gboolean *item_rearrangement_needed)
871{
872        EogWrapListPrivate *priv;
873        GnomeCanvasItem *item;                 
874
875        g_return_if_fail (EOG_IS_WRAP_LIST (wlist));
876        g_return_if_fail (layout_check_needed != NULL);
877        g_return_if_fail (item_rearrangement_needed != NULL);
878
879        *layout_check_needed = FALSE;
880        *item_rearrangement_needed = FALSE;
881
882#if COLLECTION_DEBUG
883        g_message ("do_item_removed_update called - id:%i", id);
884#endif
885
886        priv = wlist->priv;
887
888        if (priv->factory == NULL) return;
889
890        item = get_item_by_unique_id (wlist, id);
891        g_hash_table_remove (priv->item_table,
892                             GINT_TO_POINTER (id));
893        priv->view_order = g_slist_remove (priv->view_order, item);
894        gtk_object_destroy (GTK_OBJECT (item));
895        priv->n_items--;
896
897        *item_rearrangement_needed = TRUE;
898        *layout_check_needed = TRUE;
899}
900
901static void
902do_item_added_update (EogWrapList *wlist,
903                      GQuark id,
904                      gboolean *layout_check_needed,
905                      gboolean *item_rearrangement_needed)
906{
907        EogWrapListPrivate *priv;
908        GnomeCanvasItem *item;
909        CImage *img;
910
911        *layout_check_needed = FALSE;
912        *item_rearrangement_needed = FALSE;
913
914        g_return_if_fail (wlist != NULL);
915        g_return_if_fail (EOG_IS_WRAP_LIST (wlist));
916
917#if COLLECTION_DEBUG
918        g_message ("do_item_added_update called - id:%i", id);
919#endif
920
921        priv = wlist->priv;
922
923        if (wlist->priv->factory == NULL) return;
924
925        if (priv->item_group == NULL)
926                priv->item_group =
927                        gnome_canvas_item_new (gnome_canvas_root (GNOME_CANVAS (wlist)),       
928                                               gnome_canvas_group_get_type (),
929                                               "x", 0.0,
930                                               "y", 0.0,
931                                               NULL);
932
933        item = eog_item_factory_create_item (priv->factory,
934                                             GNOME_CANVAS_GROUP (priv->item_group),
935                                             id);
936        eog_item_factory_update_item (priv->factory,
937                                      priv->model,
938                                      item, EOG_ITEM_UPDATE_ALL);
939        g_hash_table_insert (priv->item_table, GINT_TO_POINTER (id), item);
940        priv->n_items++;
941        g_signal_connect (G_OBJECT (item),
942                          "event",
943                          G_CALLBACK (handle_item_event),
944                          (gpointer) wlist);
945        g_object_set_data (G_OBJECT (item), "ImageID", GINT_TO_POINTER (id));
946
947        img = eog_collection_model_get_image (priv->model, id);
948        g_object_set_data (G_OBJECT (item),
949                           "Caption", cimage_get_caption (img));
950        priv->view_order = g_slist_insert_sorted (priv->view_order,
951                                                  item,
952                                                  (GCompareFunc) compare_item_caption);
953
954        *layout_check_needed = TRUE;
955        *item_rearrangement_needed = TRUE;
956}
957
958static void
959do_item_selection_changed_update (EogWrapList *wlist,
960                                  GQuark id,
961                                  gboolean *layout_check_needed,
962                                  gboolean *item_rearrangement_needed)
963{
964        EogWrapListPrivate *priv;
965        GnomeCanvasItem *item;
966
967        *layout_check_needed = FALSE;
968        *item_rearrangement_needed = FALSE;
969
970        g_return_if_fail (wlist != NULL);
971        g_return_if_fail (EOG_IS_WRAP_LIST (wlist));
972
973#if COLLECTION_DEBUG
974        g_message ("do_item_selection_changed - id:%i", id);
975#endif
976        priv = wlist->priv;
977
978        item = get_item_by_unique_id (wlist, id);
979
980        if (item != NULL) {
981                eog_item_factory_update_item (priv->factory,
982                                              priv->model,
983                                              item, EOG_ITEM_UPDATE_SELECTION_STATE);
984        }
985}
986
987static void
988item_selection_update_single (gpointer key, gpointer value, gpointer data)
989{
990        EogWrapListPrivate *priv;
991        GnomeCanvasItem *item;
992
993        priv = EOG_WRAP_LIST (data)->priv;
994        item = GNOME_CANVAS_ITEM (value);
995
996        eog_item_factory_update_item (priv->factory,
997                                      priv->model,
998                                      item, EOG_ITEM_UPDATE_SELECTION_STATE);
999}
1000
1001static void
1002do_item_selection_update_all (EogWrapList *wlist,
1003                              gboolean *layout_check_needed,
1004                              gboolean *item_rearrangement_needed)
1005{
1006        EogWrapListPrivate *priv;
1007
1008        *layout_check_needed = FALSE;
1009        *item_rearrangement_needed = FALSE;
1010
1011        g_return_if_fail (wlist != NULL);
1012        g_return_if_fail (EOG_IS_WRAP_LIST (wlist));
1013
1014#if COLLECTION_DEBUG
1015        g_message ("do_item_selection_update_all");
1016#endif
1017        priv = wlist->priv;
1018
1019        g_hash_table_foreach (priv->item_table, item_selection_update_single, wlist);
1020}
1021
1022
1023static gboolean
1024do_layout_check (EogWrapList *wlist)
1025{
1026        unsigned int n_rows_new = 0;
1027        unsigned int n_cols_new = 0;
1028        gint cw;
1029        gint ch;
1030       
1031        EogWrapListPrivate *priv;
1032
1033        priv = wlist->priv;
1034
1035#if COLLECTION_DEBUG
1036        g_message ("do_layout_check called");
1037#endif
1038
1039        /* get canvas width */
1040        cw = GTK_WIDGET (wlist)->allocation.width;
1041        ch = GTK_WIDGET (wlist)->allocation.height;
1042
1043        /* calculate new number of  columns/rows */
1044        switch (priv->lm) {
1045        case EOG_LAYOUT_MODE_VERTICAL:
1046                n_cols_new = cw / (priv->item_width + priv->col_spacing);
1047                if (n_cols_new == 0) n_cols_new = 1;
1048                n_rows_new = priv->n_items / n_cols_new;
1049                n_rows_new = priv->n_items % n_cols_new ? n_rows_new++ : n_rows_new;
1050                break;
1051
1052        case EOG_LAYOUT_MODE_HORIZONTAL:
1053                n_rows_new = ch / (priv->item_height + priv->row_spacing);
1054                if (n_rows_new == 0) n_rows_new = 1;
1055                n_cols_new = priv->n_items / n_rows_new;
1056                n_cols_new = priv->n_items % n_rows_new ? n_cols_new++ : n_cols_new;
1057                break;
1058
1059        case EOG_LAYOUT_MODE_RECTANGLE:
1060                n_rows_new = n_cols_new = sqrt (priv->n_items);
1061                if (n_rows_new * n_cols_new < priv->n_items) {
1062                        if ((n_rows_new+1) * n_cols_new < priv->n_items)
1063                                n_rows_new = n_cols_new = n_rows_new + 1;
1064                        else
1065                                n_rows_new = n_rows_new + 1;
1066                }
1067                break;
1068
1069        default:
1070                g_assert_not_reached ();
1071        }
1072
1073#if COLLECTION_DEBUG   
1074        g_print ("  ** canvas width: %i\n",cw);
1075        g_print ("  ** n_cols_new: %i\n", n_cols_new);
1076        g_print ("  ** n_rows_new: %i\n", n_rows_new);
1077#endif
1078
1079        if (n_cols_new == priv->n_cols && n_rows_new == priv->n_rows)
1080                return FALSE;
1081
1082        priv->n_cols = n_cols_new;
1083        priv->n_rows = n_rows_new;
1084
1085        return TRUE;
1086}
1087
1088static void
1089calculate_item_position (EogWrapList *wlist,
1090                         guint item_number,
1091                         double *world_x,
1092                         double *world_y)
1093{
1094        EogWrapListPrivate *priv;
1095        guint row;
1096        guint col;
1097
1098        priv = wlist->priv;
1099
1100        row = item_number / priv->n_cols;
1101        col = item_number % priv->n_cols;
1102               
1103        *world_x = col * (priv->item_width + priv->col_spacing);
1104        *world_y = row * (priv->item_height + priv->row_spacing);
1105}
1106
1107typedef struct {
1108        EogWrapList *wlist;
1109        gint n;
1110} RearrangeData;
1111
1112static void
1113rearrange_single_item (gpointer value, RearrangeData *data)
1114{
1115        GnomeCanvasItem *item;
1116        double x1, x2, y1, y2;
1117        double x3, y3;
1118        double xoff, yoff;
1119       
1120        item = (GnomeCanvasItem*) value;
1121       
1122        gnome_canvas_item_get_bounds (item, &x1, &y1, &x2, &y2);
1123       
1124        calculate_item_position (data->wlist, data->n, &x3, &y3);
1125       
1126        xoff = x3-x1;
1127        yoff = y3-y1;
1128               
1129        if (xoff || yoff)
1130                gnome_canvas_item_move (item, xoff, yoff);
1131       
1132        data->n++;
1133}
1134
1135static void
1136do_item_rearrangement (EogWrapList *wlist)
1137{
1138        EogWrapListPrivate *priv;
1139        RearrangeData data;
1140        double sr_width, sr_height;
1141
1142        data.wlist = wlist;
1143        data.n = 0;
1144
1145#if COLLECTION_DEBUG
1146        g_message ("do_item_rearrangement called");
1147#endif
1148
1149        priv = wlist->priv;
1150
1151        g_slist_foreach (priv->view_order,
1152                         (GFunc) rearrange_single_item,
1153                         &data);
1154
1155        /* set new canvas scroll region */
1156        sr_width =  priv->n_cols * (priv->item_width + priv->col_spacing) - priv->col_spacing;
1157        sr_height = priv->n_rows * (priv->item_height + priv->row_spacing) - priv->row_spacing;
1158        gnome_canvas_set_scroll_region (GNOME_CANVAS (wlist),
1159                                        0.0, 0.0,
1160                                        sr_width, sr_height);
1161
1162#if COLLECTION_DEBUG
1163        g_message ("do_item_rearrangement leaved");
1164#endif
1165}
1166
1167static void
1168do_update (EogWrapList *wlist)
1169{
1170        EogWrapListPrivate *priv;
1171        GList *item_update;
1172        gboolean layout_check_needed = FALSE;
1173        gboolean item_rearrangement_needed = FALSE;
1174
1175        g_return_if_fail (wlist != NULL);
1176        g_return_if_fail (EOG_IS_WRAP_LIST (wlist));
1177
1178        priv = wlist->priv;
1179
1180        /* remove idle function */
1181        priv->is_updating = TRUE;
1182        gtk_idle_remove (wlist->priv->idle_handler_id);
1183        priv->idle_handler_id = -1;
1184       
1185        /* handle global updates */
1186        if (priv->global_update_hints[GLOBAL_FACTORY_CHANGED]) {
1187                priv->global_update_hints[GLOBAL_FACTORY_CHANGED] = FALSE;
1188               
1189        } else if (priv->global_update_hints[GLOBAL_MODEL_CHANGED]) {
1190                priv->global_update_hints[GLOBAL_MODEL_CHANGED] = FALSE;
1191               
1192        } else if (priv->global_update_hints[GLOBAL_SPACING_CHANGED] ||
1193                   priv->global_update_hints[GLOBAL_SIZE_CHANGED]) {
1194                layout_check_needed = TRUE;
1195                item_rearrangement_needed = TRUE;
1196
1197                priv->global_update_hints[GLOBAL_SPACING_CHANGED] = FALSE;
1198                priv->global_update_hints[GLOBAL_SIZE_CHANGED] = FALSE;
1199        } else if (priv->global_update_hints[GLOBAL_LAYOUT_MODE_CHANGED]) {
1200                layout_check_needed = TRUE;
1201                priv->global_update_hints[GLOBAL_LAYOUT_MODE_CHANGED] = FALSE;
1202        }
1203
1204        item_update = priv->item_update_list;
1205       
1206        while (item_update) {
1207                ItemUpdate *update = (ItemUpdate*) item_update->data;
1208
1209                switch (update->hint) {
1210                case MODEL_SIGNAL_IMAGE_CHANGED:
1211                        do_item_changed_update (wlist,
1212                                                update->id,
1213                                                &layout_check_needed,
1214                                                &item_rearrangement_needed);
1215                        break;
1216
1217                case MODEL_SIGNAL_IMAGE_ADDED:
1218                        do_item_added_update (wlist,
1219                                              update->id,
1220                                              &layout_check_needed,
1221                                              &item_rearrangement_needed);
1222                        break;
1223
1224                case MODEL_SIGNAL_IMAGE_REMOVED:
1225                        do_item_removed_update (wlist,
1226                                                update->id,
1227                                                &layout_check_needed,
1228                                                &item_rearrangement_needed);
1229                        break;
1230                case MODEL_SIGNAL_SELECTION_CHANGED:
1231                        do_item_selection_changed_update (wlist,
1232                                                          update->id,
1233                                                          &layout_check_needed,
1234                                                          &item_rearrangement_needed);
1235                        break;
1236                case MODEL_SIGNAL_SELECTED_ALL:
1237                case MODEL_SIGNAL_SELECTED_NONE:
1238                        do_item_selection_update_all (wlist,
1239                                                      &layout_check_needed,
1240                                                      &item_rearrangement_needed);
1241                        break;
1242
1243                default:
1244                        g_assert_not_reached ();
1245                }
1246
1247                /* free id list */
1248                g_free (update);
1249
1250                item_update = g_list_next (item_update);
1251        }
1252       
1253        /* free update list */
1254        if (priv->item_update_list)
1255                g_list_free (priv->item_update_list);
1256        priv->item_update_list = NULL;
1257
1258        if (layout_check_needed && do_layout_check (wlist))
1259                item_rearrangement_needed = TRUE;
1260       
1261        if (item_rearrangement_needed) {
1262                do_item_rearrangement (wlist);
1263        }
1264
1265        priv->is_updating = FALSE;
1266
1267}
Note: See TracBrowser for help on using the repository browser.