1 /* -*- Mode: C; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 3 -*- */
2 
3 /*
4  * GImageView
5  * Copyright (C) 2001 Takuro Ashie
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  * $Id: gimv_zlist.c,v 1.5 2004/09/21 08:44:32 makeinu Exp $
22  */
23 
24 /*
25  *  These codes are mostly taken from Another X image viewer.
26  *
27  *  Another X image viewer Author:
28  *     David Ramboz <dramboz@users.sourceforge.net>
29  */
30 
31 #ifdef HAVE_CONFIG_H
32 #  include "config.h"
33 #endif
34 
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <gdk/gdkkeysyms.h>
38 
39 #include "gtk2-compat.h"
40 #include "gimv_zlist.h"
41 
42 #define bw(widget) ((gint) GTK_CONTAINER(widget)->border_width)
43 
44 #define CELL_COL_FROM_X(list, x) \
45    (GIMV_SCROLLED_VX (list, (x) - (list)->x_pad - bw (list)) / (list)->cell_width)
46 #define CELL_ROW_FROM_Y(list, y) \
47    (GIMV_SCROLLED_VY (list, (y) - (list)->y_pad - bw (list)) / (list)->cell_height)
48 
49 #define CELL_X_FROM_COL(list, col) \
50    (GIMV_SCROLLED_X (list, (col) * (list)->cell_width  + (list)->x_pad))
51 #define CELL_Y_FROM_ROW(list, row) \
52    (GIMV_SCROLLED_Y (list, (row) * (list)->cell_height + (list)->y_pad))
53 
54 #define LIST_WIDTH(list)  ((list)->columns * (list)->cell_width /* + (list)->cell_x_pad */)
55 #define LIST_HEIGHT(list) ((list)->rows * (list)->cell_height /* + (list)->cell_y_pad */)
56 
57 #define HIGHLIGHT_SIZE 2
58 
59 #ifdef USE_GTK2
60 #  ifdef GTK_DISABLE_DEPRECATED
61 #     include "gimv_marshal.h"
62 #  endif
63 #  define WIDGET_DRAW(widget) gtk_widget_queue_draw (widget)
64 #  define WIDGET_DRAW_AREA(widget, area) \
65       gtk_widget_queue_draw_area (widget, \
66                                   (area)->x, (area)->y, \
67                                   (area)->width, (area)->height)
68 #else /* USE_GTK2 */
69 #  define WIDGET_DRAW(widget) gtk_widget_draw (widget, NULL)
70 #  define WIDGET_DRAW_AREA(widget, area) gtk_widget_draw (widget, area)
71 #endif /* USE_GTK2 */
72 
73 static void gimv_zlist_class_init              (GimvZListClass *klass);
74 static void gimv_zlist_init                    (GimvZList *list);
75 
76 #ifdef USE_GTK2
77 static void  gimv_zlist_finalize               (GObject *object);
78 #else
79 static void  gimv_zlist_finalize               (GtkObject        *object);
80 #endif
81 static void  gimv_zlist_map                    (GtkWidget        *widget);
82 static void  gimv_zlist_unmap                  (GtkWidget        *widget);
83 static void  gimv_zlist_realize                (GtkWidget        *widget);
84 static void  gimv_zlist_unrealize              (GtkWidget        *widget);
85 static void  gimv_zlist_size_request           (GtkWidget        *widget,
86                                                 GtkRequisition   *requisition);
87 static void  gimv_zlist_size_allocate          (GtkWidget        *widget,
88                                                 GtkAllocation    *allocation);
89 static gint  gimv_zlist_expose                 (GtkWidget        *widget,
90                                                 GdkEventExpose   *event);
91 static void  gimv_zlist_update                 (GimvZList        *list);
92 static void  gimv_zlist_draw_horizontal_list   (GtkWidget        *widget,
93                                                 GdkRectangle     *area);
94 static void  gimv_zlist_draw_vertical_list     (GtkWidget        *widget,
95                                                 GdkRectangle     *area);
96 static void  gimv_zlist_draw_selection_region  (GimvZList        *list,
97                                                 GdkRectangle     *area);
98 static void  gimv_zlist_draw                   (GtkWidget        *widget,
99                                                 GdkRectangle     *area);
100 static void gimv_zlist_redraw_selection_region (GtkWidget        *list,
101                                                 gint              prev_x,
102                                                 gint              prev_y,
103                                                 gint              next_x,
104                                                 gint              next_y);
105 
106 static gint  gimv_zlist_button_press           (GtkWidget        *widget,
107                                                 GdkEventButton   *event);
108 static gint  gimv_zlist_button_release         (GtkWidget        *widget,
109                                                 GdkEventButton   *event);
110 static gint  gimv_zlist_motion_notify          (GtkWidget        *widget,
111                                                 GdkEventMotion   *event);
112 static gint  gimv_zlist_key_press              (GtkWidget        *widget,
113                                                 GdkEventKey      *event);
114 static gint  gimv_zlist_focus_in               (GtkWidget        *widget,
115                                                 GdkEventFocus    *event);
116 static gint  gimv_zlist_focus_out              (GtkWidget        *widget,
117                                                 GdkEventFocus    *event);
118 static gint  gimv_zlist_drag_motion            (GtkWidget        *widget,
119                                                 GdkDragContext   *context,
120                                                 gint              x,
121                                                 gint              y,
122                                                 guint             time);
123 static gint gimv_zlist_drag_drop               (GtkWidget        *widget,
124                                                 GdkDragContext   *context,
125                                                 gint              x,
126                                                 gint              y,
127                                                 guint             time);
128 static void gimv_zlist_drag_leave              (GtkWidget        *widget,
129                                                 GdkDragContext   *context,
130                                                 guint             time);
131 static void gimv_zlist_highlight               (GtkWidget        *widget);
132 static void gimv_zlist_unhighlight             (GtkWidget        *widget);
133 static gint gimv_zlist_focus                   (GtkContainer     *container,
134                                                 GtkDirectionType  dir);
135 static void gimv_zlist_cell_draw_focus         (GimvZList        *list,
136                                                 gint              index);
137 static void gimv_zlist_cell_draw_default       (GimvZList        *list,
138                                                 gint              index);
139 
140 
141 static void gimv_zlist_forall                  (GtkContainer     *container,
142                                                 gboolean          include_internals,
143                                                 GtkCallback       callback,
144                                                 gpointer          callback_data);
145 static void gimv_zlist_adjust_adjustments      (GimvScrolled     *scrolled);
146 static void gimv_zlist_cell_pos                (GimvZList        *list,
147                                                 gint              index,
148                                                 gint             *row,
149                                                 gint             *col);
150 static void gimv_zlist_cell_area               (GimvZList        *list,
151                                                 gint              index,
152                                                 GdkRectangle     *cell_area);
153 
154 enum {
155    CLEAR,
156    CELL_DRAW,
157    CELL_SIZE_REQUEST,
158    CELL_DRAW_FOCUS,
159    CELL_DRAW_DEFAULT,
160    CELL_SELECT,
161    CELL_UNSELECT,
162    LAST_SIGNAL
163 };
164 
165 static GtkWidgetClass     *parent_class                = NULL;
166 
167 static guint               gimv_zlist_signals [LAST_SIGNAL] = { 0 };
168 
169 
170 GtkType
gimv_zlist_get_type(void)171 gimv_zlist_get_type (void)
172 {
173    static GtkType type = 0;
174 
175 #ifdef USE_GTK2
176    if (!type) {
177       static const GTypeInfo gimv_zlist_type_info = {
178          sizeof (GimvZListClass),
179          NULL,               /* base_init */
180          NULL,               /* base_finalize */
181          (GClassInitFunc)    gimv_zlist_class_init,
182          NULL,               /* class_finalize */
183          NULL,               /* class_data */
184          sizeof (GimvZList),
185          0,                  /* n_preallocs */
186          (GInstanceInitFunc) gimv_zlist_init,
187       };
188 
189       type = g_type_register_static (GIMV_TYPE_SCROLLED,
190                                      "GimvZList",
191                                      &gimv_zlist_type_info,
192                                      0);
193    }
194 #else /* USE_GTK2 */
195    if (!type) {
196       static const GtkTypeInfo gimv_zlist_type_info = {
197          "GimvZList",
198          sizeof (GimvZList),
199          sizeof (GimvZListClass),
200          (GtkClassInitFunc) gimv_zlist_class_init,
201          (GtkObjectInitFunc) gimv_zlist_init,
202          /* reserved_1 */ NULL,
203          /* reserved_2 */ NULL,
204          (GtkClassInitFunc) NULL,
205       };
206 
207       type = gtk_type_unique (GIMV_TYPE_SCROLLED, &gimv_zlist_type_info);
208    }
209 #endif /* USE_GTK2 */
210 
211    return type;
212 }
213 
214 
215 static void
gimv_zlist_class_init(GimvZListClass * klass)216 gimv_zlist_class_init (GimvZListClass *klass)
217 {
218    GtkObjectClass *object_class;
219    GtkWidgetClass *widget_class;
220    GtkContainerClass *container_class;
221    GimvScrolledClass *scrolled_class;
222 
223    parent_class    = gtk_type_class (GIMV_TYPE_SCROLLED);
224 
225    object_class    = (GtkObjectClass*) klass;
226    widget_class    = (GtkWidgetClass*) klass;
227    container_class = (GtkContainerClass*) klass;
228    scrolled_class  = (GimvScrolledClass*) klass;
229 
230 #if (defined USE_GTK2) && (defined GTK_DISABLE_DEPRECATED)
231    gimv_zlist_signals [CLEAR] =
232       g_signal_new ("clear",
233                     G_TYPE_FROM_CLASS (object_class),
234                     G_SIGNAL_RUN_FIRST,
235                     G_STRUCT_OFFSET (GimvZListClass, clear),
236                     NULL, NULL,
237                     g_cclosure_marshal_VOID__VOID,
238                     G_TYPE_NONE, 0);
239 
240    gimv_zlist_signals [CELL_DRAW] =
241       g_signal_new ("cell_draw",
242                     G_TYPE_FROM_CLASS (object_class),
243                     G_SIGNAL_RUN_FIRST,
244                     G_STRUCT_OFFSET (GimvZListClass, cell_draw),
245                     NULL, NULL,
246                     gimv_marshal_VOID__POINTER_POINTER_POINTER,
247                     G_TYPE_NONE, 3, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER);
248 
249    gimv_zlist_signals [CELL_SIZE_REQUEST] =
250       g_signal_new ("cell_size_request",
251                     G_TYPE_FROM_CLASS (object_class),
252                     G_SIGNAL_RUN_FIRST,
253                     G_STRUCT_OFFSET (GimvZListClass, cell_size_request),
254                     NULL, NULL,
255                     gimv_marshal_VOID__POINTER_POINTER,
256                     G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
257 
258    gimv_zlist_signals [CELL_DRAW_FOCUS] =
259       g_signal_new ("cell_draw_focus",
260                     G_TYPE_FROM_CLASS (object_class),
261                     G_SIGNAL_RUN_FIRST,
262                     G_STRUCT_OFFSET (GimvZListClass, cell_draw_focus),
263                     NULL, NULL,
264                     gimv_marshal_VOID__POINTER_POINTER,
265                     G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
266 
267    gimv_zlist_signals [CELL_DRAW_DEFAULT] =
268       g_signal_new ("cell_draw_default",
269                     G_TYPE_FROM_CLASS (object_class),
270                     G_SIGNAL_RUN_FIRST,
271                     G_STRUCT_OFFSET (GimvZListClass, cell_draw_default),
272                     NULL, NULL,
273                     gimv_marshal_VOID__POINTER_POINTER,
274                     G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
275 
276    gimv_zlist_signals [CELL_SELECT] =
277       g_signal_new ("cell_select",
278                     G_TYPE_FROM_CLASS (object_class),
279                     G_SIGNAL_RUN_FIRST,
280                     G_STRUCT_OFFSET (GimvZListClass, cell_select),
281                     NULL, NULL,
282                     g_cclosure_marshal_VOID__INT,
283                     G_TYPE_NONE, 1, G_TYPE_INT);
284 
285    gimv_zlist_signals [CELL_UNSELECT] =
286       g_signal_new ("cell_unselect",
287                     G_TYPE_FROM_CLASS (object_class),
288                     G_SIGNAL_RUN_FIRST,
289                     G_STRUCT_OFFSET (GimvZListClass, cell_unselect),
290                     NULL, NULL,
291                     g_cclosure_marshal_VOID__INT,
292                     G_TYPE_NONE, 1, G_TYPE_INT);
293 #else /* (defined USE_GTK2) && (defined GTK_DISABLE_DEPRECATED) */
294    gimv_zlist_signals [CLEAR] =
295       gtk_signal_new ("clear",
296                       GTK_RUN_FIRST,
297                       GTK_CLASS_TYPE(object_class),
298                       GTK_SIGNAL_OFFSET(GimvZListClass, clear),
299                       gtk_marshal_NONE__NONE,
300                       GTK_TYPE_NONE, 0);
301 
302    gimv_zlist_signals [CELL_DRAW] =
303       gtk_signal_new ("cell_draw",
304                       GTK_RUN_FIRST,
305                       GTK_CLASS_TYPE(object_class),
306                       GTK_SIGNAL_OFFSET(GimvZListClass, cell_draw),
307                       gtk_marshal_NONE__POINTER_POINTER_POINTER,
308                       GTK_TYPE_NONE, 3,
309                       GTK_TYPE_POINTER, GTK_TYPE_POINTER, GTK_TYPE_POINTER);
310 
311    gimv_zlist_signals [CELL_SIZE_REQUEST] =
312       gtk_signal_new ("cell_size_request",
313                       GTK_RUN_FIRST,
314                       GTK_CLASS_TYPE(object_class),
315                       GTK_SIGNAL_OFFSET(GimvZListClass, cell_size_request),
316                       gtk_marshal_NONE__POINTER_POINTER,
317                       GTK_TYPE_NONE, 2,
318                       GTK_TYPE_POINTER, GTK_TYPE_POINTER);
319 
320    gimv_zlist_signals [CELL_DRAW_FOCUS] =
321       gtk_signal_new ("cell_draw_focus",
322                       GTK_RUN_FIRST,
323                       GTK_CLASS_TYPE(object_class),
324                       GTK_SIGNAL_OFFSET(GimvZListClass, cell_draw_focus),
325                       gtk_marshal_NONE__POINTER_POINTER,
326                       GTK_TYPE_NONE, 2, GTK_TYPE_POINTER, GTK_TYPE_POINTER);
327 
328    gimv_zlist_signals [CELL_DRAW_DEFAULT] =
329       gtk_signal_new ("cell_draw_default",
330                       GTK_RUN_FIRST,
331                       GTK_CLASS_TYPE(object_class),
332                       GTK_SIGNAL_OFFSET(GimvZListClass, cell_draw_default),
333                       gtk_marshal_NONE__POINTER_POINTER,
334                       GTK_TYPE_NONE, 2, GTK_TYPE_POINTER, GTK_TYPE_POINTER);
335 
336    gimv_zlist_signals [CELL_SELECT] =
337       gtk_signal_new ("cell_select",
338                       GTK_RUN_FIRST,
339                       GTK_CLASS_TYPE(object_class),
340                       GTK_SIGNAL_OFFSET(GimvZListClass, cell_select),
341                       gtk_marshal_NONE__INT,
342                       GTK_TYPE_NONE, 1, GTK_TYPE_INT);
343 
344    gimv_zlist_signals [CELL_UNSELECT] =
345       gtk_signal_new ("cell_unselect",
346                       GTK_RUN_FIRST,
347                       GTK_CLASS_TYPE(object_class),
348                       GTK_SIGNAL_OFFSET(GimvZListClass, cell_unselect),
349                       gtk_marshal_NONE__INT,
350                       GTK_TYPE_NONE, 1, GTK_TYPE_INT);
351 
352    gtk_object_class_add_signals (object_class, gimv_zlist_signals, LAST_SIGNAL);
353 #endif /* (defined USE_GTK2) && (defined GTK_DISABLE_DEPRECATED) */
354 
355    OBJECT_CLASS_SET_FINALIZE_FUNC (klass, gimv_zlist_finalize);
356 
357    widget_class->map                    = gimv_zlist_map;
358    widget_class->unmap                  = gimv_zlist_unmap;
359    widget_class->realize                = gimv_zlist_realize;
360    widget_class->unrealize              = gimv_zlist_unrealize;
361    widget_class->size_request           = gimv_zlist_size_request;
362    widget_class->size_allocate          = gimv_zlist_size_allocate;
363    widget_class->expose_event           = gimv_zlist_expose;
364 #ifndef USE_GTK2
365    widget_class->draw                   = gimv_zlist_draw;
366 #endif
367 
368    widget_class->button_press_event     = gimv_zlist_button_press;
369    widget_class->button_release_event   = gimv_zlist_button_release;
370    widget_class->motion_notify_event    = gimv_zlist_motion_notify;
371    widget_class->key_press_event        = gimv_zlist_key_press;
372    widget_class->focus_in_event         = gimv_zlist_focus_in;
373    widget_class->focus_out_event        = gimv_zlist_focus_out;
374    widget_class->drag_motion            = gimv_zlist_drag_motion;
375    widget_class->drag_drop              = gimv_zlist_drag_drop;
376    widget_class->drag_leave             = gimv_zlist_drag_leave;
377 
378    container_class->forall              = gimv_zlist_forall;
379    /*  container_class->focus               = gimv_zlist_focus; */
380    scrolled_class->adjust_adjustments   = gimv_zlist_adjust_adjustments;
381 
382    klass->cell_draw                     = NULL;
383    klass->cell_size_request             = NULL;
384    klass->cell_draw_focus               = NULL;
385    klass->cell_draw_default             = NULL;
386    klass->cell_select                   = NULL;
387    klass->cell_unselect                 = NULL;
388 }
389 
390 
391 static void
gimv_zlist_init(GimvZList * list)392 gimv_zlist_init (GimvZList *list)
393 {
394    GTK_WIDGET_UNSET_FLAGS (list, GTK_NO_WINDOW);
395    GTK_WIDGET_SET_FLAGS (list, GTK_CAN_FOCUS);
396 
397    list->flags            = 0;
398    list->cell_width       = 1;
399    list->cell_height      = 1;
400    list->rows             = 1;
401    list->columns          = 1;
402    list->cells            = NULL;
403    list->cell_count       = 0;
404    list->selection_mode   = GTK_SELECTION_SINGLE;
405    list->selection        = NULL;
406    list->focus            = -1;
407    list->anchor           = -1;
408    list->cell_x_pad       = 4;
409    list->cell_y_pad       = 4;
410    list->x_pad            = 0;
411    list->y_pad            = 0;
412    list->entered_cell     = NULL;
413 
414    list->region_select    = GIMV_ZLIST_REGION_SELECT_OFF;
415    list->region_line_gc   = NULL;
416    list->selection_mask   = NULL;
417 }
418 
419 
420 void
gimv_zlist_construct(GimvZList * list,int flags)421 gimv_zlist_construct (GimvZList *list, int flags)
422 {
423    g_return_if_fail (list);
424 
425    list->flags          = flags;
426    list->cells          = g_array_new (0, 0, sizeof (gpointer));
427 }
428 
429 
430 GtkWidget*
gimv_zlist_new(guint flags)431 gimv_zlist_new (guint flags)
432 {
433    GimvZList *list;
434 
435 #ifdef USE_GTK2
436    list = g_object_new (gimv_zlist_get_type (), NULL);
437 #else /* USE_GTK2 */
438    list = (GimvZList*) gtk_type_new (gimv_zlist_get_type());
439 #endif /* USE_GTK2 */
440    g_return_val_if_fail (list, NULL);
441 
442    gimv_zlist_construct (list, flags);
443 
444    return (GtkWidget*) list;
445 }
446 
447 
448 void
gimv_zlist_set_to_vertical(GimvZList * zlist)449 gimv_zlist_set_to_vertical (GimvZList *zlist)
450 {
451    g_return_if_fail (GIMV_IS_ZLIST (zlist));
452 
453    zlist->flags &= ~GIMV_ZLIST_HORIZONTAL;
454 
455    if (GTK_WIDGET_VISIBLE (zlist)) {
456       WIDGET_DRAW (GTK_WIDGET (zlist));
457    }
458 }
459 
460 
461 void
gimv_zlist_set_to_horizontal(GimvZList * zlist)462 gimv_zlist_set_to_horizontal (GimvZList *zlist)
463 {
464    g_return_if_fail (GIMV_IS_ZLIST (zlist));
465 
466    zlist->flags |= GIMV_ZLIST_HORIZONTAL;
467 
468    if (GTK_WIDGET_VISIBLE (zlist)) {
469       WIDGET_DRAW (GTK_WIDGET (zlist));
470    }
471 }
472 
473 
474 static void
475 #ifdef USE_GTK2
gimv_zlist_finalize(GObject * object)476 gimv_zlist_finalize (GObject *object)
477 #else  /* USE_GTK2 */
478 gimv_zlist_finalize (GtkObject *object)
479 #endif /* USE_GTK2 */
480 {
481    if (GIMV_ZLIST (object)->region_line_gc)
482       gdk_gc_destroy (GIMV_ZLIST (object)->region_line_gc);
483 
484    OBJECT_CLASS_FINALIZE_SUPER (parent_class, object);
485 }
486 
487 
488 static void
gimv_zlist_map(GtkWidget * widget)489 gimv_zlist_map (GtkWidget *widget)
490 {
491    GTK_WIDGET_SET_FLAGS(widget, GTK_MAPPED);
492    gdk_window_show (widget->window);
493    gimv_zlist_update(GIMV_ZLIST (widget));
494 }
495 
496 
497 static void
gimv_zlist_unmap(GtkWidget * widget)498 gimv_zlist_unmap (GtkWidget *widget)
499 {
500    GTK_WIDGET_UNSET_FLAGS(widget, GTK_MAPPED);
501    gdk_window_hide (widget->window);
502 }
503 
504 
505 static void
gimv_zlist_realize(GtkWidget * widget)506 gimv_zlist_realize (GtkWidget *widget)
507 {
508    GdkWindowAttr attributes;
509    gint attributes_mask;
510 
511    GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
512 
513    attributes.window_type   = GDK_WINDOW_CHILD;
514    attributes.x             = widget->allocation.x + bw (widget);
515    attributes.y             = widget->allocation.y + bw (widget);
516    attributes.width         = widget->allocation.width  - 2 * bw (widget);
517    attributes.height        = widget->allocation.height - 2 * bw (widget);
518    attributes.wclass        = GDK_INPUT_OUTPUT;
519    attributes.visual        = gtk_widget_get_visual (widget);
520    attributes.colormap      = gtk_widget_get_colormap (widget);
521    attributes.event_mask    = gtk_widget_get_events (widget);
522    attributes.event_mask    |= (GDK_EXPOSURE_MASK |
523                                 GDK_BUTTON_PRESS_MASK |
524                                 GDK_BUTTON_RELEASE_MASK |
525                                 GDK_POINTER_MOTION_MASK |
526                                 GDK_KEY_PRESS_MASK);
527    attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
528 
529    widget->window = gdk_window_new (gtk_widget_get_parent_window(widget),
530                                     &attributes, attributes_mask);
531    gdk_window_set_user_data (widget->window, widget);
532    widget->style = gtk_style_attach (widget->style, widget->window);
533 
534    gdk_window_set_background (widget->window,
535                               &widget->style->base [GTK_STATE_NORMAL]);
536 
537    gimv_scrolled_realize (GIMV_SCROLLED(widget));
538 }
539 
540 
541 static void
gimv_zlist_unrealize(GtkWidget * widget)542 gimv_zlist_unrealize (GtkWidget *widget)
543 {
544    gimv_scrolled_unrealize (GIMV_SCROLLED(widget));
545 
546    if (parent_class->unrealize)
547       (* parent_class->unrealize) (widget);
548 }
549 
550 
551 static void
gimv_zlist_size_request(GtkWidget * widget,GtkRequisition * requisition)552 gimv_zlist_size_request (GtkWidget *widget, GtkRequisition *requisition)
553 {
554    requisition->width = requisition->height = 50;
555 }
556 
557 
558 static void
gimv_zlist_size_allocate(GtkWidget * widget,GtkAllocation * allocation)559 gimv_zlist_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
560 {
561    if (allocation->x == widget->allocation.x
562        && allocation->y == widget->allocation.y
563        && allocation->width == widget->allocation.width
564        && allocation->height == widget->allocation.height)
565    {
566       return;
567    }
568 
569    widget->allocation = *allocation;
570 
571    if (GTK_WIDGET_REALIZED(widget))
572       gdk_window_move_resize (widget->window,
573                               allocation->x + bw (widget),
574                               allocation->y + bw (widget),
575                               allocation->width  - 2 * bw (widget),
576                               allocation->height - 2 * bw (widget));
577 
578    gimv_zlist_update (GIMV_ZLIST(widget));
579 }
580 
581 
582 static void
gimv_zlist_update(GimvZList * list)583 gimv_zlist_update (GimvZList *list)
584 {
585    GtkWidget *widget;
586    GtkAdjustment *adj;
587 
588    widget = GTK_WIDGET(list);
589 
590    if (list->flags & GIMV_ZLIST_HORIZONTAL) {
591       if (list->flags & GIMV_ZLIST_1)
592          list->cell_height = widget->allocation.height - 2 * bw (widget);
593 
594       list->rows    = MAX(1, (widget->allocation.height - 2 * bw (widget)) / list->cell_height);
595       list->columns = list->cell_count % list->rows ?
596          list->cell_count / list->rows + 1 :
597          list->cell_count / list->rows;
598 
599       list->y_pad = (widget->allocation.height - LIST_HEIGHT(list) - 2 * bw (widget)) / 2;
600       if (list->y_pad < 0) list->y_pad = 0;
601 
602       gimv_zlist_adjust_adjustments (GIMV_SCROLLED(list));
603       adj = GIMV_SCROLLED(widget)->h_adjustment;
604       if (adj && !GIMV_SCROLLED(widget)->freeze_count
605           && adj->value > adj->upper - adj->page_size)
606       {
607          adj->value = adj->upper - adj->page_size;
608 #ifdef USE_GTK2
609          g_signal_emit_by_name (G_OBJECT(adj), "value_changed", NULL);
610 #else /* USE_GTK2 */
611          gtk_signal_emit_by_name (GTK_OBJECT(adj), "value_changed", NULL);
612 #endif /* USE_GTK2 */
613       }
614 
615    } else {
616       if (list->flags & GIMV_ZLIST_1)
617          list->cell_width = widget->allocation.width - 2 * bw (widget);
618 
619       list->columns = MAX(1, (widget->allocation.width - 2 * bw (widget)) / list->cell_width);
620       list->rows    = list->cell_count % list->columns ?
621          list->cell_count / list->columns + 1 :
622          list->cell_count / list->columns;
623 
624       list->x_pad = (widget->allocation.width - LIST_WIDTH(list) - 2 * bw (widget)) / 2;
625       if (list->x_pad < 0) list->x_pad = 0;
626 
627       gimv_zlist_adjust_adjustments (GIMV_SCROLLED(widget));
628       adj = GIMV_SCROLLED(widget)->v_adjustment;
629       if (adj
630           && !GIMV_SCROLLED(widget)->freeze_count
631           && adj->value > adj->upper - adj->page_size)
632       {
633          adj->value = adj->upper - adj->page_size;
634 #ifdef USE_GTK2
635          g_signal_emit_by_name (G_OBJECT(adj), "value_changed", NULL);
636 #else /* USE_GTK2 */
637          gtk_signal_emit_by_name (GTK_OBJECT(adj), "value_changed", NULL);
638 #endif /* USE_GTK2 */
639       }
640    }
641 }
642 
643 
644 static gint
gimv_zlist_expose(GtkWidget * widget,GdkEventExpose * event)645 gimv_zlist_expose (GtkWidget *widget, GdkEventExpose *event)
646 {
647    if (GTK_WIDGET_DRAWABLE(widget) && event->window == widget->window)
648       gimv_zlist_draw (widget, &event->area);
649 
650    return FALSE; /* xxx */
651 }
652 
653 
654 gboolean
gimv_zlist_get_cell_area(GimvZList * list,gint index,GdkRectangle * area)655 gimv_zlist_get_cell_area (GimvZList *list, gint index, GdkRectangle *area)
656 {
657    gint col, row;
658 
659    g_return_val_if_fail (GIMV_IS_ZLIST (list), FALSE);
660    g_return_val_if_fail (area, FALSE);
661    g_return_val_if_fail (index >= 0 && index < list->cell_count, FALSE);
662 
663    if (list->flags & GIMV_ZLIST_HORIZONTAL){
664       col = index / list->rows;
665       row = index % list->rows;
666    } else {
667       col = index % list->columns;
668       row = index / list->columns;
669    }
670 
671    area->x = CELL_X_FROM_COL(list, col) + list->cell_x_pad;
672    area->y = CELL_Y_FROM_ROW(list, row) + list->cell_y_pad;
673    area->width  = list->cell_width - list->cell_x_pad;
674    area->height = list->cell_height - list->cell_y_pad;
675 
676    return TRUE;
677 }
678 
679 
680 static void
gimv_zlist_draw_horizontal_list(GtkWidget * widget,GdkRectangle * area)681 gimv_zlist_draw_horizontal_list (GtkWidget *widget, GdkRectangle *area)
682 {
683    GimvZList *list;
684    gpointer *cell;
685    GdkRectangle cell_area, intersect_area;
686    gint first_row, last_row;
687    gint first_column, last_column;
688    gint i, j, idx, c;
689 
690    list = GIMV_ZLIST (widget);
691 
692    first_column = CELL_COL_FROM_X(list, area->x);
693    first_column = CLAMP(first_column, 0, list->columns);
694    last_column  = CELL_COL_FROM_X(list, area->x + area->width) + 1;
695    last_column  = CLAMP(last_column, 0, list->columns);
696 
697    first_row    = CELL_ROW_FROM_Y(list, area->y);
698    first_row    = CLAMP(first_row, 0, list->rows);
699    last_row     = CELL_ROW_FROM_Y(list, area->y + area->height) + 1;
700    last_row     = CLAMP(last_row, 0, list->rows);
701 
702    /* clear the padding area (bottom & top) */
703    c = list->y_pad - area->y;
704    if (c > 0)
705       gdk_window_clear_area (widget->window,
706                              area->x, area->y,
707                              area->width, c);
708 
709    c = area->y + area->height - (LIST_HEIGHT(list) + list->y_pad);
710    if (c > 0)
711       gdk_window_clear_area (widget->window,
712                              area->x, area->y + area->height - c,
713                              area->width, c);
714 
715 
716    for (j = first_column; j < last_column; j++) {
717 
718       /* clear the cell vertical padding */
719       if (list->cell_x_pad)
720          gdk_window_clear_area (widget->window,
721                                 CELL_X_FROM_COL(list, j), area->y,
722                                 list->cell_x_pad, area->height);
723 
724       idx = j * list->rows + first_row;
725       for (i = first_row; i < last_row; i++, idx++) {
726 
727          /* clear the cell horizontal padding */
728          if (list->cell_y_pad)
729             gdk_window_clear_area (widget->window,
730                                    area->x, CELL_Y_FROM_ROW(list, i),
731                                    area->width, list->cell_y_pad);
732 
733          cell_area.x = CELL_X_FROM_COL(list, j) + list->cell_x_pad;
734          cell_area.y = CELL_Y_FROM_ROW(list, i) + list->cell_y_pad;
735          cell_area.width  = list->cell_width - list->cell_x_pad;
736          cell_area.height = list->cell_height - list->cell_y_pad;
737 
738          if (gdk_rectangle_intersect (area, &cell_area, &intersect_area)) {
739             if (idx < list->cell_count) {
740                cell = GIMV_ZLIST_CELL_FROM_INDEX (list, idx);
741 
742 #ifdef USE_GTK2
743                g_signal_emit (G_OBJECT(list), gimv_zlist_signals [CELL_DRAW], 0,
744                               cell, &cell_area, &intersect_area);
745 #else /* USE_GTK2 */
746                gtk_signal_emit (GTK_OBJECT(list), gimv_zlist_signals [CELL_DRAW],
747                                 cell, &cell_area, &intersect_area);
748 #endif /* USE_GTK2 */
749             } else {
750                gdk_window_clear_area (widget->window,
751                                       intersect_area.x, intersect_area.y,
752                                       intersect_area.width, intersect_area.height);
753             }
754          }
755       } /* rows loop */
756    } /* columns loop */
757 
758    /* clear the right of the list  XXX should hasppen */
759    c = area->x + area->width - CELL_X_FROM_COL(list, j);
760    if (c > 0)
761       gdk_window_clear_area (widget->window,
762                              CELL_X_FROM_COL(list, j), area->y,
763                              c, area->height);
764    /* clear the bottom of the list */
765    c = area->y + area->height - CELL_Y_FROM_ROW(list, last_row);
766    if (c > 0)
767       gdk_window_clear_area (widget->window,
768                              area->x, CELL_Y_FROM_ROW(list, last_row),
769                              area->width, c);
770 }
771 
772 
773 static void
gimv_zlist_draw_vertical_list(GtkWidget * widget,GdkRectangle * area)774 gimv_zlist_draw_vertical_list (GtkWidget *widget, GdkRectangle *area)
775 {
776    GimvZList *list;
777    gpointer *cell;
778    GdkRectangle cell_area, intersect_area;
779    gint first_row, last_row;
780    gint first_column, last_column;
781    gint i, j, idx, c;
782 
783    list = GIMV_ZLIST (widget);
784 
785    first_column = CELL_COL_FROM_X(list, area->x);
786    first_column = CLAMP(first_column, 0, list->columns);
787    last_column  = CELL_COL_FROM_X(list, area->x + area->width) + 1;
788    last_column  = CLAMP(last_column, 0, list->columns);
789 
790    first_row    = CELL_ROW_FROM_Y(list, area->y);
791    first_row    = CLAMP(first_row, 0, list->rows);
792    last_row     = CELL_ROW_FROM_Y(list, area->y + area->height) + 1;
793    last_row     = CLAMP(last_row, 0, list->rows);
794 
795    /* clear the padding area (right & left) */
796    c = list->x_pad - area->x;
797    if (c > 0)
798       gdk_window_clear_area (widget->window,
799                              area->x, area->y,
800                              c, area->height);
801 
802    c = area->x + area->width - (LIST_WIDTH(list) + list->x_pad);
803    if (c > 0)
804       gdk_window_clear_area (widget->window,
805                              area->x + area->width - c, area->y,
806                              c, area->height);
807 
808    for (i = first_row; i < last_row; i++) {
809 
810       /* clear the cell horizontal padding */
811       if (list->cell_y_pad)
812          gdk_window_clear_area (widget->window,
813                                 area->x, CELL_Y_FROM_ROW(list, i),
814                                 area->width, list->cell_y_pad);
815 
816       idx = i * list->columns + first_column;
817       for (j = first_column; j < last_column; j++, idx++) {
818 
819          /* clear the cell vertical padding */
820          if (list->cell_x_pad)
821             gdk_window_clear_area (widget->window,
822                                    CELL_X_FROM_COL(list, j), area->y,
823                                    list->cell_x_pad, area->height);
824 
825          cell_area.x = CELL_X_FROM_COL(list, j) + list->cell_x_pad;
826          cell_area.y = CELL_Y_FROM_ROW(list, i) + list->cell_y_pad;
827          cell_area.width  = list->cell_width - list->cell_x_pad;
828          cell_area.height = list->cell_height - list->cell_y_pad;
829 
830          if (gdk_rectangle_intersect (area, &cell_area, &intersect_area)) {
831             if (idx < list->cell_count) {
832                cell = GIMV_ZLIST_CELL_FROM_INDEX (list, idx);
833 
834 #ifdef USE_GTK2
835                g_signal_emit (G_OBJECT(list), gimv_zlist_signals [CELL_DRAW], 0,
836                               cell, &cell_area, &intersect_area);
837 #else /* USE_GTK2 */
838                gtk_signal_emit (GTK_OBJECT(list), gimv_zlist_signals [CELL_DRAW],
839                                 cell, &cell_area, &intersect_area);
840 #endif /* USE_GTK2 */
841             } else {
842                gdk_window_clear_area (widget->window,
843                                       intersect_area.x, intersect_area.y,
844                                       intersect_area.width, intersect_area.height);
845             }
846          }
847       } /* columns loop */
848    } /* rows loop */
849 
850    /* clear the right of the list  XXX should hasppen */
851    c = area->x + area->width - CELL_X_FROM_COL(list, last_column);
852    if (c > 0)
853       gdk_window_clear_area (widget->window,
854                              CELL_X_FROM_COL(list, last_column), area->y,
855                              c, area->height);
856    /* clear the bottom of the list */
857    c = area->y + area->height - CELL_Y_FROM_ROW(list, i);
858    if (c > 0)
859       gdk_window_clear_area (widget->window,
860                              area->x, CELL_Y_FROM_ROW(list, i),
861                              area->width, c);
862 }
863 
864 
865 static void
gimv_zlist_draw_selection_region(GimvZList * list,GdkRectangle * area)866 gimv_zlist_draw_selection_region (GimvZList *list, GdkRectangle *area)
867 {
868    GtkWidget *widget;
869    GimvScrolled *scrolled;
870    GtkAdjustment *hadj, *vadj;
871    GdkRectangle widget_area, region_area, draw_area;
872    gint ds_vx, ds_vy, de_vx, de_vy;
873    gchar dash[] = {2, 1};
874 
875    widget = GTK_WIDGET (list);
876    scrolled = GIMV_SCROLLED (list);
877 
878    hadj = scrolled->h_adjustment;
879    vadj = scrolled->v_adjustment;
880 
881    ds_vx = scrolled->drag_start_vx;
882    ds_vy = scrolled->drag_start_vy;
883    de_vx = GIMV_SCROLLED_VX (scrolled, scrolled->drag_motion_x);
884    de_vy = GIMV_SCROLLED_VY (scrolled, scrolled->drag_motion_y);
885 
886    region_area.x = GIMV_SCROLLED_X (list, MIN (ds_vx, de_vx));
887    region_area.y = GIMV_SCROLLED_Y (list, MIN (ds_vy, de_vy));
888    region_area.width  = abs (de_vx - ds_vx);
889    region_area.height = abs (de_vy - ds_vy);
890 
891    widget_area.x = widget_area.y = 0;
892    widget_area.width  = widget->allocation.width;
893    widget_area.height = widget->allocation.height;
894 
895    if (!gdk_rectangle_intersect (&widget_area, &region_area, &draw_area))
896       return;
897 
898    if (!list->region_line_gc) {
899       list->region_line_gc = gdk_gc_new (widget->window);
900       gdk_gc_copy (list->region_line_gc, widget->style->black_gc);
901       gdk_gc_set_line_attributes (list->region_line_gc, 1,
902                                   GDK_LINE_ON_OFF_DASH,
903                                   GDK_CAP_NOT_LAST,
904                                   GDK_JOIN_MITER);
905       gdk_gc_set_dashes (list->region_line_gc, 0, dash, 2);
906    }
907 
908    gdk_draw_rectangle (widget->window,
909                        list->region_line_gc,
910                        FALSE,
911                        draw_area.x, draw_area.y,
912                        draw_area.width, draw_area.height);
913 }
914 
915 
916 static void
gimv_zlist_draw(GtkWidget * widget,GdkRectangle * area)917 gimv_zlist_draw (GtkWidget *widget, GdkRectangle *area)
918 {
919    GimvZList *list;
920    GimvScrolled *scr;
921    GdkRectangle list_area;
922 
923    list = GIMV_ZLIST(widget);
924    scr = GIMV_SCROLLED(widget);
925 
926    if (!GTK_WIDGET_DRAWABLE(widget) || scr->freeze_count)
927       return;
928 
929    list_area.x = list_area.y = 0;
930    /* xxx */
931    list_area.width  = widget->allocation.width;
932    list_area.height = widget->allocation.height;
933 
934    if (!area)
935       area = &list_area;
936 
937    if (!list->cell_count) {
938       gdk_window_clear_area (widget->window,
939                              area->x, area->y,
940                              area->width, area->height);
941       return;
942    }
943 
944    if (list->flags & GIMV_ZLIST_HORIZONTAL) {
945       gimv_zlist_draw_horizontal_list (widget, area);
946    } else {
947       gimv_zlist_draw_vertical_list (widget, area);
948    }
949 
950    if (list->focus > -1)
951       gimv_zlist_cell_draw_focus (list, list->focus);
952 
953    if (list->region_select)
954       gimv_zlist_draw_selection_region (list, area);
955 
956    if (list->flags & GIMV_ZLIST_HIGHLIGHTED)
957       gimv_zlist_highlight (widget);
958 }
959 
960 
961 static void
gimv_zlist_redraw_selection_region(GtkWidget * widget,gint prev_x,gint prev_y,gint next_x,gint next_y)962 gimv_zlist_redraw_selection_region (GtkWidget *widget,
963                                     gint prev_x, gint prev_y,
964                                     gint next_x, gint next_y)
965 {
966    GimvScrolled *scrolled;
967    GdkRectangle widget_area, area, draw_area;
968    gint start_x, start_y;
969    gint low, high;
970 
971    scrolled = GIMV_SCROLLED (widget);
972 
973    start_x = GIMV_SCROLLED_X (scrolled, scrolled->drag_start_vx);
974    start_y = GIMV_SCROLLED_Y (scrolled, scrolled->drag_start_vy);
975 
976    widget_area.x = widget_area.y = 0;
977    widget_area.width  = widget->allocation.width;
978    widget_area.height = widget->allocation.height;
979 
980    /* horizontal line */
981    low  = MIN (start_x, prev_x);
982    high = MAX (start_x, prev_x);
983    area.x = low - 1;
984    area.y = start_y - 1;
985    area.width = high - area.x + 3;
986    area.height = 3;
987    if (gdk_rectangle_intersect (&widget_area, &area, &draw_area))
988       gimv_zlist_draw (widget, &draw_area);
989 
990    area.y = prev_y - 1;
991    if (gdk_rectangle_intersect (&widget_area, &area, &draw_area))
992       gimv_zlist_draw (widget, &draw_area);
993 
994    /* vertical line */
995    low  = MIN (start_y, prev_y);
996    high = MAX (start_y, prev_y);
997    area.x = start_x - 1;
998    area.y = low - 1;
999    area.width = 3;
1000    area.height = high - area.y + 3;
1001    if (gdk_rectangle_intersect (&widget_area, &area, &draw_area))
1002       gimv_zlist_draw (widget, &draw_area);
1003 
1004    area.x = prev_x - 1;
1005    if (gdk_rectangle_intersect (&widget_area, &area, &draw_area))
1006       gimv_zlist_draw (widget, &draw_area);
1007 }
1008 
1009 
1010 static gint
gimv_zlist_button_press(GtkWidget * widget,GdkEventButton * event)1011 gimv_zlist_button_press (GtkWidget *widget, GdkEventButton *event)
1012 {
1013    GimvZList *list;
1014    gpointer *cell;
1015    gint retval = FALSE, idx;
1016 
1017    list = GIMV_ZLIST(widget);
1018 
1019    /* grab focus */
1020    if (!GTK_WIDGET_HAS_FOCUS(list))
1021       gtk_widget_grab_focus (widget);
1022 
1023    /* call parent method */
1024    if (parent_class->button_press_event)
1025       retval = parent_class->button_press_event (widget, event);
1026 
1027    list->region_select = GIMV_ZLIST_REGION_SELECT_OFF;
1028 
1029    if (event->type != GDK_BUTTON_PRESS ||
1030        event->button != 1 ||
1031        event->window != widget->window)
1032    {
1033       return retval || FALSE;
1034    }
1035 
1036    /* get the selected cell's index */
1037    idx = gimv_zlist_cell_index_from_xy (list, event->x, event->y);
1038 
1039    /* set to region select mode */
1040    if (idx < 0) {
1041       if (event->state & GDK_CONTROL_MASK) {          /* toggle mode */
1042          list->region_select = GIMV_ZLIST_REGION_SELECT_TOGGLE;
1043          gimv_zlist_set_selection_mask (list, NULL);
1044       } else if (event->state & GDK_SHIFT_MASK) {   /* expand mode */
1045          list->region_select = GIMV_ZLIST_REGION_SELECT_EXPAND;
1046          gimv_zlist_set_selection_mask (list, NULL);
1047       } else {
1048          list->region_select = GIMV_ZLIST_REGION_SELECT_NORMAL;
1049          gimv_zlist_unselect_all (list);
1050       }
1051 
1052    } else {
1053       /* get the selected cell */
1054       cell = GIMV_ZLIST_CELL_FROM_INDEX (list, idx);
1055 
1056       /* set focus */
1057       if (list->focus != idx) {
1058          if (list->focus > -1)
1059             gimv_zlist_cell_draw_default (list, list->focus);
1060          list->focus = idx;
1061       }
1062 
1063       /* set to the DnD mode */
1064       if (list->flags & GIMV_ZLIST_USES_DND
1065           && g_list_find (list->selection, GUINT_TO_POINTER(idx)))
1066       {
1067          list->anchor = idx;
1068          gimv_zlist_cell_draw_focus (list, idx);
1069          return retval || FALSE;
1070       }
1071 
1072       /* set selection */
1073       switch (list->selection_mode) {
1074       case GTK_SELECTION_SINGLE:
1075 #ifndef USE_GTK2
1076       case GTK_SELECTION_MULTIPLE:
1077 #endif
1078          list->anchor = idx;
1079          gimv_zlist_cell_draw_focus (list, idx);
1080          break;
1081 
1082       case GTK_SELECTION_BROWSE:
1083          gimv_zlist_unselect_all (list);
1084          gimv_zlist_cell_select (list, idx);
1085          break;
1086 
1087       case GTK_SELECTION_EXTENDED:
1088          if (event->state & GDK_CONTROL_MASK) {
1089             list->anchor = idx;
1090             gimv_zlist_cell_toggle (list, idx);
1091          } else if (event->state & GDK_SHIFT_MASK) {
1092             gimv_zlist_extend_selection (list, idx);
1093          } else {
1094             list->anchor = idx;
1095 
1096             if (!g_list_find (list->selection, GUINT_TO_POINTER(idx))) {
1097                gimv_zlist_unselect_all (list);
1098                gimv_zlist_cell_select (list, idx);
1099             }
1100          }
1101          break;
1102 
1103       default:
1104          break;
1105       }
1106    }
1107 
1108    if (gdk_pointer_grab (widget->window, FALSE,
1109                          GDK_POINTER_MOTION_HINT_MASK |
1110                          GDK_BUTTON1_MOTION_MASK |
1111                          GDK_BUTTON_RELEASE_MASK,
1112                          NULL, NULL, event->time))
1113       return retval || FALSE;
1114 
1115    gtk_grab_add (widget);
1116 
1117    return retval || FALSE;
1118 }
1119 
1120 
1121 static gint
gimv_zlist_button_release(GtkWidget * widget,GdkEventButton * event)1122 gimv_zlist_button_release (GtkWidget *widget, GdkEventButton *event)
1123 {
1124    GimvZList *list;
1125    gpointer *cell;
1126    gint retval = FALSE, index;
1127 
1128    list = GIMV_ZLIST(widget);
1129 
1130    /* call parent class callback */
1131    if (parent_class->button_release_event)
1132       retval = parent_class->button_release_event (widget, event);
1133 
1134    if (GTK_WIDGET_HAS_GRAB(widget))
1135       gtk_grab_remove (widget);
1136 
1137    if (gdk_pointer_is_grabbed ())
1138       gdk_pointer_ungrab (event->time);
1139 
1140    index = gimv_zlist_cell_index_from_xy (list, event->x, event->y);
1141    if (index < 0) goto FUNC_END;
1142 
1143    cell = GIMV_ZLIST_CELL_FROM_INDEX (list, index);
1144 
1145    switch (list->selection_mode) {
1146    case GTK_SELECTION_SINGLE:
1147       if (list->anchor == index) {
1148          list->focus = index;
1149          gimv_zlist_unselect_all (list);
1150          gimv_zlist_cell_toggle (list, index);
1151       }
1152       list->anchor = index;
1153       break;
1154 
1155 #ifndef USE_GTK2
1156    case GTK_SELECTION_MULTIPLE:
1157       if (list->anchor == index) {
1158          list->focus = index;
1159          gimv_zlist_cell_toggle (list, index);
1160       }
1161       list->anchor = index;
1162       break;
1163 #endif
1164 
1165    case GTK_SELECTION_EXTENDED:
1166       if (event->state & GDK_CONTROL_MASK) {
1167       } else if (event->state & GDK_SHIFT_MASK) {
1168       } else {
1169          if (event->button == 1 && !list->region_select) {
1170             gimv_zlist_unselect_all (list);
1171             gimv_zlist_cell_select (list, index);
1172          }
1173       }
1174 
1175       if ((list->flags & GIMV_ZLIST_USES_DND)) {
1176          gint x, y;
1177 
1178          /* on a drag-drop event, the event->x & event->y always seem to be 0 */
1179          gdk_window_get_pointer (widget->window, &x, &y, NULL);
1180          index = gimv_zlist_cell_index_from_xy (list, x, y);
1181          if (index < 0) goto FUNC_END;
1182          /*
1183            if (list->anchor == index)
1184            gimv_zlist_extend_selection (list, list->focus);
1185          */
1186       }
1187 
1188       list->anchor = list->focus;
1189       break;
1190 
1191    default:
1192       break;
1193    }
1194 
1195 FUNC_END:
1196 
1197    /* unset region select */
1198    if (list->region_select)
1199       gimv_zlist_draw (widget, NULL);
1200    gimv_zlist_unset_selection_mask (list);
1201    list->region_select = GIMV_ZLIST_REGION_SELECT_OFF;
1202 
1203    return retval || FALSE;
1204 }
1205 
1206 
1207 static gint
gimv_zlist_motion_notify(GtkWidget * widget,GdkEventMotion * event)1208 gimv_zlist_motion_notify (GtkWidget *widget, GdkEventMotion *event)
1209 {
1210    GimvScrolled *scrolled;
1211    GimvZList *list;
1212    gpointer *cell;
1213    gint index, x, y, retval = FALSE;
1214 
1215    gint start_x, start_y, end_x, end_y, prev_x = 0, prev_y = 0;
1216    gint flags;
1217    gboolean pressed;
1218 
1219    list = GIMV_ZLIST(widget);
1220    scrolled = GIMV_SCROLLED (widget);
1221 
1222    flags = scrolled->autoscroll_flags;
1223    pressed = scrolled->pressed;
1224 
1225    index = gimv_zlist_cell_index_from_xy (list, x, y);
1226 
1227    if (list->region_select) {
1228       prev_x = scrolled->drag_motion_x;
1229       prev_y = scrolled->drag_motion_y;
1230    }
1231 
1232    /* call parent class callback */
1233    if (parent_class->motion_notify_event)
1234       retval = parent_class->motion_notify_event (widget, event);
1235 
1236    if (list->region_select) {
1237       if ((pressed && (flags & GIMV_SCROLLED_AUTO_SCROLL_MOTION))
1238           || (flags & GIMV_SCROLLED_AUTO_SCROLL_MOTION_ALL))
1239       {
1240          start_x = MIN (scrolled->drag_start_vx, GIMV_SCROLLED_VX (scrolled, event->x));
1241          start_y = MIN (scrolled->drag_start_vy, GIMV_SCROLLED_VY (scrolled, event->y));
1242          end_x   = MAX (scrolled->drag_start_vx, GIMV_SCROLLED_VX (scrolled, event->x));
1243          end_y   = MAX (scrolled->drag_start_vy, GIMV_SCROLLED_VY (scrolled, event->y));
1244 
1245          gimv_zlist_cell_select_by_pixel_region (list,
1246                                                  start_x, start_y,
1247                                                  end_x, end_y);
1248 
1249          gimv_zlist_redraw_selection_region (widget,
1250                                              prev_x, prev_y,
1251                                              event->x, event->y);
1252       }
1253    }
1254 
1255    if (list->flags & GIMV_ZLIST_USES_DND) return retval || FALSE;
1256 
1257    gdk_window_get_pointer (widget->window, &x, &y, NULL);
1258 
1259    index = gimv_zlist_cell_index_from_xy (list, x, y);
1260    if (index < 0) return retval || FALSE;
1261 
1262 
1263    cell = GIMV_ZLIST_CELL_FROM_INDEX (list, index);
1264 
1265    if (gdk_pointer_is_grabbed() && GTK_WIDGET_HAS_GRAB(widget)) {
1266 
1267       if (index == list->focus) return retval || FALSE;
1268 
1269       gimv_zlist_cell_draw_default (list, list->focus);
1270       list->focus = index;
1271 
1272       switch (list->selection_mode) {
1273       case GTK_SELECTION_SINGLE:
1274 #ifndef USE_GTK2
1275       case GTK_SELECTION_MULTIPLE:
1276 #endif
1277          gimv_zlist_cell_draw_focus (list, index);
1278          break;
1279 
1280       case GTK_SELECTION_BROWSE:
1281          gimv_zlist_unselect_all (list);
1282          gimv_zlist_cell_select (list, index);
1283          break;
1284 
1285 #if 0
1286       case GTK_SELECTION_EXTENDED:
1287          if (event->state & GDK_CONTROL_MASK) {
1288             list->anchor = index;
1289             gimv_zlist_cell_toggle (list, index);
1290          } else {
1291             gimv_zlist_extend_selection (list, index);
1292          }
1293          break;
1294 #endif
1295 
1296       default:
1297          break;
1298       }
1299    } else {
1300    }
1301 
1302    return retval || FALSE;
1303 }
1304 
1305 /*
1306  * GtkContainers doesn't seem to forward the focus movement to
1307  * containers which don't have child widgets
1308  *
1309  */
1310 
1311 static gint
gimv_zlist_key_press(GtkWidget * widget,GdkEventKey * event)1312 gimv_zlist_key_press (GtkWidget *widget, GdkEventKey *event)
1313 {
1314    gint direction = -1;
1315 
1316    g_return_val_if_fail (widget && event, FALSE);
1317 
1318    if (GTK_WIDGET_HAS_FOCUS (widget)) {
1319       switch (event->keyval) {
1320       case GDK_Up:
1321          direction = GTK_DIR_UP;
1322          break;
1323       case GDK_Down:
1324          direction = GTK_DIR_DOWN;
1325          break;
1326       case GDK_Left:
1327          direction = GTK_DIR_LEFT;
1328          break;
1329       case GDK_Right:
1330          direction = GTK_DIR_RIGHT;
1331          break;
1332       /*
1333       case GDK_Tab:
1334       case GDK_ISO_Left_Tab:
1335          if (event->state & GDK_SHIFT_MASK)
1336             direction = GTK_DIR_TAB_BACKWARD;
1337          else
1338             direction = GTK_DIR_TAB_FORWARD;
1339           break;
1340       */
1341       case GDK_Page_Up:
1342          gimv_scrolled_page_up (GIMV_SCROLLED (widget));
1343          break;
1344       case GDK_Page_Down:
1345          gimv_scrolled_page_down (GIMV_SCROLLED (widget));
1346          break;
1347       default:
1348          break;
1349       }
1350    }
1351 
1352    if (direction != -1) {
1353       gimv_zlist_focus (GTK_CONTAINER(widget), direction);
1354       return FALSE;
1355    }
1356 
1357    if (parent_class->key_press_event &&
1358        (* parent_class->key_press_event) (widget, event))
1359       return TRUE;
1360 
1361    return FALSE;
1362 }
1363 
1364 
1365 static gint
gimv_zlist_focus_in(GtkWidget * widget,GdkEventFocus * event)1366 gimv_zlist_focus_in (GtkWidget *widget, GdkEventFocus *event)
1367 {
1368    GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1369 
1370 #ifndef USE_GTK2
1371    gtk_widget_draw_focus (widget);
1372 #endif
1373 
1374    return FALSE;
1375 }
1376 
1377 
1378 static gint
gimv_zlist_focus_out(GtkWidget * widget,GdkEventFocus * event)1379 gimv_zlist_focus_out (GtkWidget *widget, GdkEventFocus *event)
1380 {
1381    GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
1382 
1383 #ifndef USE_GTK2
1384    gtk_widget_draw_default (widget);
1385 #endif
1386 
1387    return FALSE;
1388 }
1389 
1390 
1391 static gint
gimv_zlist_drag_motion(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint time)1392 gimv_zlist_drag_motion (GtkWidget      *widget,
1393                         GdkDragContext *context,
1394                         gint            x,
1395                         gint            y,
1396                         guint           time)
1397 {
1398    GimvZList *list;
1399    gint index, retval = FALSE;
1400 
1401    g_return_val_if_fail (widget, FALSE);
1402 
1403    if (parent_class->button_press_event)
1404       retval = parent_class->drag_motion (widget, context, x, y, time);
1405 
1406    list = GIMV_ZLIST(widget);
1407 
1408    if (!(list->flags & GIMV_ZLIST_USES_DND))
1409       return retval || FALSE;
1410 
1411    if (!(list->flags & GIMV_ZLIST_HIGHLIGHTED)) {
1412       list->flags |= GIMV_ZLIST_HIGHLIGHTED;
1413       gimv_zlist_highlight (widget);
1414    }
1415 
1416    index = gimv_zlist_cell_index_from_xy (GIMV_ZLIST(widget), x, y);
1417    if (index < 0)
1418       return retval || FALSE;
1419    /*
1420    if (g_list_find (ZLIST(widget)->selection, GUINT_TO_POINTER(index)))
1421       return TRUE;
1422    */
1423    return retval || FALSE;
1424 }
1425 
1426 
1427 static gint
gimv_zlist_drag_drop(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint time)1428 gimv_zlist_drag_drop (GtkWidget *widget,
1429                       GdkDragContext *context,
1430                       gint x,
1431                       gint y,
1432                       guint time)
1433 {
1434    GimvZList *list;
1435 
1436    list = GIMV_ZLIST(widget);
1437 
1438    if (!(list->flags & GIMV_ZLIST_USES_DND))
1439       return FALSE;
1440 
1441    return FALSE;
1442 }
1443 
1444 
1445 static void
gimv_zlist_drag_leave(GtkWidget * widget,GdkDragContext * context,guint time)1446 gimv_zlist_drag_leave (GtkWidget *widget,
1447                        GdkDragContext *context,
1448                        guint time)
1449 {
1450    GimvZList *list;
1451 
1452    if (parent_class->drag_leave)
1453       parent_class->drag_leave (widget, context, time);
1454 
1455    list = GIMV_ZLIST(widget);
1456    if (list->flags & GIMV_ZLIST_HIGHLIGHTED) {
1457       list->flags &= ~GIMV_ZLIST_HIGHLIGHTED;
1458       gimv_zlist_unhighlight (widget);
1459    }
1460 }
1461 
1462 static void
gimv_zlist_highlight(GtkWidget * widget)1463 gimv_zlist_highlight (GtkWidget *widget)
1464 {
1465 #ifdef USE_GTK2
1466    gtk_paint_shadow (widget->style,
1467                      widget->window,
1468                      GTK_STATE_NORMAL, GTK_SHADOW_OUT,
1469                      NULL, NULL, NULL,
1470                      0, 0,
1471                      widget->allocation.width - 2 * bw (widget),
1472                      widget->allocation.height - 2 * bw (widget));
1473 #else /* USE_GTK2 */
1474    gtk_draw_shadow (widget->style,
1475                     widget->window,
1476                     GTK_STATE_NORMAL, GTK_SHADOW_OUT,
1477                     0, 0,
1478                     widget->allocation.width - 2 * bw (widget),
1479                     widget->allocation.height - 2 * bw (widget));
1480 #endif /* USE_GTK2 */
1481 
1482    gdk_draw_rectangle (widget->window,
1483                        widget->style->black_gc,
1484                        FALSE,
1485                        0, 0,
1486                        widget->allocation.width - 2 * bw (widget) - 1,
1487                        widget->allocation.height - 2 * bw (widget) - 1);
1488 
1489 }
1490 
1491 
1492 static void
gimv_zlist_unhighlight(GtkWidget * widget)1493 gimv_zlist_unhighlight (GtkWidget *widget)
1494 {
1495    GdkRectangle area;
1496 
1497    area.x = 0; area.y = 0;
1498    area.width  = HIGHLIGHT_SIZE;
1499    area.height = widget->allocation.height - 2 * bw (widget);
1500    WIDGET_DRAW_AREA (widget, &area);
1501 
1502    area.width  = widget->allocation.width - 2 * bw (widget);
1503    area.height = HIGHLIGHT_SIZE;
1504    WIDGET_DRAW_AREA (widget, &area);
1505 
1506    area.y      = widget->allocation.height - 2 * bw (widget) - HIGHLIGHT_SIZE;
1507    WIDGET_DRAW_AREA (widget, &area);
1508 
1509    area.x      = widget->allocation.width - 2 * bw (widget) - HIGHLIGHT_SIZE;
1510    area.y      = 0;
1511    area.width  = HIGHLIGHT_SIZE;
1512    area.height = widget->allocation.height - 2 * bw (widget);
1513    WIDGET_DRAW_AREA (widget, &area);
1514 }
1515 
1516 
1517 static void
gimv_zlist_forall(GtkContainer * container,gboolean include_internals,GtkCallback callback,gpointer callback_data)1518 gimv_zlist_forall (GtkContainer *container,
1519                    gboolean include_internals,
1520                    GtkCallback callback,
1521                    gpointer callback_data)
1522 {
1523    /*
1524    GimvZList *list;
1525    gint i;
1526 
1527    list = ZLIST(container);
1528 
1529    if (include_internals)
1530    for (i = 0; i < list->cell_count; i++)
1531       (* callback) (GIMV_ZLIST_CELL_FROM_INDEX (list, i), callback_data);
1532    */
1533 }
1534 
1535 
1536 static gint
gimv_zlist_focus(GtkContainer * container,GtkDirectionType dir)1537 gimv_zlist_focus (GtkContainer *container, GtkDirectionType dir)
1538 {
1539    GimvZList *list;
1540    gint   focus;
1541 
1542    list = GIMV_ZLIST(container);
1543 
1544    if (list->focus < 0)
1545       return FALSE;
1546 
1547    focus = list->focus;
1548    switch (dir) {
1549    case GTK_DIR_LEFT:
1550       focus -= list->flags & GIMV_ZLIST_HORIZONTAL ? list->rows : 1;
1551       break;
1552    case GTK_DIR_RIGHT:
1553       focus += list->flags & GIMV_ZLIST_HORIZONTAL ? list->rows : 1;
1554       break;
1555    case GTK_DIR_UP:
1556    case GTK_DIR_TAB_BACKWARD:
1557       focus -= list->flags & GIMV_ZLIST_HORIZONTAL ? 1 : list->columns;
1558       break;
1559    case GTK_DIR_DOWN:
1560    case GTK_DIR_TAB_FORWARD:
1561       focus += list->flags & GIMV_ZLIST_HORIZONTAL ? 1 : list->columns;
1562       break;
1563    default:
1564       return FALSE;
1565       break;
1566    }
1567 
1568    if (focus < 0 || focus >= list->cell_count)
1569       return FALSE;
1570 
1571    gimv_zlist_cell_draw_default (list, list->focus);
1572    list->focus = focus;
1573    gimv_zlist_cell_draw_focus (list, focus);
1574    gimv_zlist_moveto (list, focus);
1575 
1576    return TRUE;
1577 }
1578 
1579 
1580 guint
gimv_zlist_add(GimvZList * list,gpointer cell)1581 gimv_zlist_add (GimvZList *list, gpointer cell)
1582 {
1583    g_return_val_if_fail (GIMV_IS_ZLIST (list), 0);
1584 
1585    return gimv_zlist_insert (list, list->cells->len, cell);
1586 }
1587 
1588 
1589 guint
gimv_zlist_insert(GimvZList * list,guint pos,gpointer cell)1590 gimv_zlist_insert (GimvZList *list, guint pos, gpointer cell)
1591 {
1592    GtkRequisition requisition = { 0 };
1593    gint adjust = FALSE;
1594 
1595    g_return_val_if_fail (GIMV_IS_ZLIST (list), 0);
1596 
1597    if (pos > list->cells->len)
1598       pos = list->cells->len;
1599    list->cells = g_array_insert_val (list->cells, pos, cell);
1600 
1601 #ifdef USE_GTK2
1602    g_signal_emit (G_OBJECT(list), gimv_zlist_signals [CELL_SIZE_REQUEST], 0,
1603                   cell, &requisition);
1604 #else /* USE_GTK2 */
1605    gtk_signal_emit (GTK_OBJECT(list), gimv_zlist_signals [CELL_SIZE_REQUEST],
1606                     cell, &requisition);
1607 #endif /* USE_GTK2 */
1608 
1609    if (list->flags & GIMV_ZLIST_HORIZONTAL) {
1610       if (list->cell_count && list->cell_count % list->rows == 0) {
1611          list->columns++;
1612          adjust = TRUE;
1613       }
1614    } else {
1615       if (list->cell_count && list->cell_count % list->columns == 0) {
1616          list->rows ++;
1617          adjust = TRUE;
1618       }
1619    }
1620 
1621    list->cell_count++;
1622 
1623    if (requisition.width  + list->cell_x_pad > list->cell_width ||
1624        requisition.height + list->cell_y_pad > list->cell_height)
1625    {
1626       list->cell_width
1627          = MAX(list->cell_width, requisition.width + list->cell_x_pad);
1628       list->cell_height
1629          = MAX(list->cell_height, requisition.height + list->cell_y_pad);
1630 
1631       gimv_zlist_update (list);
1632       WIDGET_DRAW (GTK_WIDGET (list));
1633 
1634    } else {
1635 
1636       if (adjust)
1637          gimv_zlist_adjust_adjustments (GIMV_SCROLLED(list));
1638 
1639       gimv_zlist_update (list);
1640       gimv_zlist_draw_cell (list, list->cell_count - 1);
1641    }
1642 
1643    return pos;
1644 }
1645 
1646 
1647 void
gimv_zlist_remove(GimvZList * list,gpointer cell)1648 gimv_zlist_remove (GimvZList *list, gpointer cell)
1649 {
1650    GdkRectangle area;
1651    GList *item;
1652    gint index, *i;
1653 
1654    index = gimv_zlist_cell_index (list, cell);
1655    if (index == -1)
1656       return;
1657 
1658    list->cells = g_array_remove_index (list->cells, index);
1659    list->cell_count --;
1660 
1661    list->selection = g_list_remove (list->selection, GUINT_TO_POINTER(index));
1662    item = list->selection;
1663    while (item) {
1664       i = (guint *) &item->data;
1665       if (*i > index)
1666          *i -= 1;
1667       item = item->next;
1668    }
1669 
1670    if (list->focus == index)
1671       list->focus = -1;
1672    else if (list->focus > index)
1673       list->focus --;
1674 
1675    if (list->anchor == index)
1676       list->anchor = -1;
1677    else if (list->anchor > index)
1678       list->anchor --;
1679 
1680    if ((list->flags & GIMV_ZLIST_HORIZONTAL) && list->cell_count % list->rows == 0) {
1681       list->columns --;
1682       gimv_zlist_adjust_adjustments (GIMV_SCROLLED(list));
1683    } else if (!(list->flags & GIMV_ZLIST_HORIZONTAL) && list->cell_count % list->columns == 0) {
1684       list->rows --;
1685       gimv_zlist_adjust_adjustments (GIMV_SCROLLED(list));
1686    }
1687 
1688    gimv_zlist_cell_area (list, index, &area);
1689 
1690    /* using gdk_window_copy_area will be faster but harder too */
1691    area.width  = GTK_WIDGET(list)->allocation.width  - area.x;
1692    area.height = GTK_WIDGET(list)->allocation.height - area.y;
1693 
1694    gimv_zlist_update (list);
1695    gimv_zlist_draw (GTK_WIDGET(list), &area);
1696 
1697    if (list->flags & GIMV_ZLIST_HORIZONTAL) {
1698       area.width -= list->cell_width;
1699       area.height = area.y;
1700       area.x     += list->cell_width;
1701       area.y      = 0;
1702    } else {
1703       area.width  = area.x;
1704       area.height -= list->cell_height;
1705       area.x      = 0;
1706       area.y      += list->cell_height;
1707    }
1708 
1709    WIDGET_DRAW_AREA (GTK_WIDGET (list), &area);
1710 }
1711 
1712 
1713 static void
gimv_zlist_adjust_adjustments(GimvScrolled * scrolled)1714 gimv_zlist_adjust_adjustments (GimvScrolled *scrolled)
1715 {
1716    GimvZList *list;
1717    GtkWidget *widget;
1718    GtkAdjustment *adj;
1719 
1720    list = GIMV_ZLIST(scrolled);
1721    widget = GTK_WIDGET(scrolled);
1722 
1723    if (!GTK_WIDGET_DRAWABLE(widget) || scrolled->freeze_count)
1724       return;
1725 
1726    if (scrolled->h_adjustment) {
1727       adj = scrolled->h_adjustment;
1728 
1729       adj->page_size      = widget->allocation.width - 2 * bw (widget);
1730       adj->page_increment = adj->page_size;
1731       adj->step_increment = list->cell_width;
1732       adj->lower          = 0;
1733       adj->upper          = LIST_WIDTH(list) + 2 * list->x_pad + list->cell_x_pad;
1734 
1735 #ifdef USE_GTK2
1736       g_signal_emit_by_name (G_OBJECT(adj), "changed");
1737 #else /* USE_GTK2 */
1738       gtk_signal_emit_by_name (GTK_OBJECT(adj), "changed");
1739 #endif /* USE_GTK2 */
1740    }
1741 
1742    if (scrolled->v_adjustment) {
1743       adj = scrolled->v_adjustment;
1744 
1745       adj->page_size      = widget->allocation.height - 2 * bw (widget);
1746       adj->page_increment = adj->page_size;
1747       adj->step_increment = list->cell_height;
1748       adj->lower          = 0;
1749       adj->upper          = LIST_HEIGHT(list) + 2 * list->y_pad + list->cell_y_pad;
1750 
1751 #ifdef USE_GTK2
1752       g_signal_emit_by_name (G_OBJECT(adj), "changed");
1753 #else /* USE_GTK2 */
1754       gtk_signal_emit_by_name (GTK_OBJECT(adj), "changed");
1755 #endif /* USE_GTK2 */
1756    }
1757 }
1758 
1759 
1760 void
gimv_zlist_clear(GimvZList * list)1761 gimv_zlist_clear (GimvZList *list)
1762 {
1763    GimvScrolled *scrolled;
1764 
1765    g_return_if_fail (list);
1766 
1767    gimv_zlist_unselect_all (list);
1768 
1769 #ifdef USE_GTK2
1770    g_signal_emit (G_OBJECT(list), gimv_zlist_signals [CLEAR], 0);
1771 #else /* USE_GTK2 */
1772    gtk_signal_emit (GTK_OBJECT(list), gimv_zlist_signals [CLEAR]);
1773 #endif /* USE_GTK2 */
1774 
1775    g_array_set_size (list->cells, 0);
1776    list->cell_count     = 0;
1777    list->focus          = -1;
1778    list->anchor         = -1;
1779    list->entered_cell   = NULL;
1780 
1781    if (list->flags & GIMV_ZLIST_HORIZONTAL)
1782       list->columns = 1;
1783    else
1784       list->rows = 1;
1785 
1786    scrolled = GIMV_SCROLLED(list);
1787    gimv_zlist_adjust_adjustments (GIMV_SCROLLED(list));
1788    scrolled->h_adjustment->value = 0;
1789    scrolled->v_adjustment->value = 0;
1790 #ifdef USE_GTK2
1791    g_signal_emit_by_name (G_OBJECT(scrolled->h_adjustment), "value_changed");
1792    g_signal_emit_by_name (G_OBJECT(scrolled->v_adjustment), "value_changed");
1793 #else /* USE_GTK2 */
1794    gtk_signal_emit_by_name (GTK_OBJECT(scrolled->h_adjustment), "value_changed");
1795    gtk_signal_emit_by_name (GTK_OBJECT(scrolled->v_adjustment), "value_changed");
1796 #endif /* USE_GTK2 */
1797 
1798    gimv_zlist_draw (GTK_WIDGET(list), NULL);
1799 }
1800 
1801 
1802 void
gimv_zlist_set_cell_padding(GimvZList * list,gint x_pad,gint y_pad)1803 gimv_zlist_set_cell_padding (GimvZList *list, gint x_pad, gint y_pad)
1804 {
1805    g_return_if_fail (list);
1806    g_return_if_fail (x_pad >= 0 && y_pad >= 0);
1807 
1808    list->cell_width  += x_pad - list->cell_x_pad;
1809    list->cell_height += y_pad - list->cell_y_pad;
1810 
1811    list->cell_x_pad = x_pad;
1812    list->cell_y_pad = y_pad;
1813 
1814    gimv_zlist_update (list);
1815    WIDGET_DRAW (GTK_WIDGET (list));
1816 }
1817 
1818 
1819 void
gimv_zlist_set_cell_size(GimvZList * list,gint width,gint height)1820 gimv_zlist_set_cell_size (GimvZList *list, gint width, gint height)
1821 {
1822    g_return_if_fail (GIMV_IS_ZLIST (list));
1823 
1824    if (width > 0)
1825       list->cell_width  = width  + list->cell_x_pad;
1826 
1827    if (height > 0)
1828       list->cell_height = height + list->cell_y_pad;
1829 
1830    gimv_zlist_update (list);
1831    gimv_zlist_draw (GTK_WIDGET(list), NULL);
1832 }
1833 
1834 
1835 void
gimv_zlist_set_selection_mode(GimvZList * list,GtkSelectionMode mode)1836 gimv_zlist_set_selection_mode (GimvZList *list, GtkSelectionMode mode)
1837 {
1838    g_return_if_fail (GIMV_IS_ZLIST (list));
1839 
1840    list->selection_mode = mode;
1841    gimv_zlist_unselect_all (list);
1842    WIDGET_DRAW (GTK_WIDGET (list));
1843 }
1844 
1845 
1846 gint
gimv_zlist_cell_index_from_xy(GimvZList * list,gint x,gint y)1847 gimv_zlist_cell_index_from_xy (GimvZList *list, gint x, gint y)
1848 {
1849    GimvScrolled *scr;
1850    gint row, column, cell_x, cell_y, index;
1851 
1852    scr = GIMV_SCROLLED(list);
1853 
1854    if (!list->cell_count || x < list->x_pad || y < list->y_pad)
1855       return -1;
1856 
1857    row    = CELL_ROW_FROM_Y(list, y);
1858    column = CELL_COL_FROM_X(list, x);
1859 
1860    if (row >= list->rows || column >= list->columns)
1861       return -1;
1862 
1863    cell_x = CELL_X_FROM_COL(list, column);
1864    if (x < list->cell_x_pad * 2 + cell_x)
1865       return -1;
1866 
1867    cell_y = CELL_Y_FROM_ROW(list, row);
1868    if (y < list->cell_y_pad * 2 + cell_y)
1869       return -1;
1870 
1871    index = list->flags & GIMV_ZLIST_HORIZONTAL ? column * list->rows + row : row * list->columns + column;
1872 
1873    return index < list->cell_count ? index : -1;
1874 }
1875 
1876 
1877 void
gimv_zlist_set_1(GimvZList * list,gint one)1878 gimv_zlist_set_1 (GimvZList *list, gint one)
1879 {
1880    g_return_if_fail (list);
1881 
1882    if (one)
1883       list->flags |= GIMV_ZLIST_1;
1884    else
1885       list->flags &= ~GIMV_ZLIST_1;
1886 
1887    gimv_zlist_update (list);
1888    WIDGET_DRAW (GTK_WIDGET (list));
1889 }
1890 
1891 
1892 gpointer
gimv_zlist_cell_from_xy(GimvZList * list,gint x,gint y)1893 gimv_zlist_cell_from_xy (GimvZList *list, gint x, gint y)
1894 {
1895    gint index;
1896 
1897    index = gimv_zlist_cell_index_from_xy (list, x, y);
1898    return index < 0 ? NULL : GIMV_ZLIST_CELL_FROM_INDEX (list, index);
1899 }
1900 
1901 
1902 static void
gimv_zlist_cell_pos(GimvZList * list,gint index,gint * row,gint * col)1903 gimv_zlist_cell_pos (GimvZList *list, gint index, gint *row, gint *col)
1904 {
1905    g_return_if_fail (list && index != -1 && row && col);
1906 
1907    if (list->flags & GIMV_ZLIST_HORIZONTAL) {
1908       *row = index % list->rows;
1909       *col = index / list->rows;
1910    } else {
1911       *row = index / list->columns;
1912       *col = index % list->columns;
1913    }
1914 }
1915 
1916 
1917 static void
gimv_zlist_cell_area(GimvZList * list,gint index,GdkRectangle * cell_area)1918 gimv_zlist_cell_area (GimvZList *list, gint index, GdkRectangle *cell_area)
1919 {
1920    gint row, col;
1921    g_return_if_fail (list && index != -1 && cell_area);
1922 
1923    gimv_zlist_cell_pos (list, index, &row, &col);
1924 
1925    cell_area->x      = CELL_X_FROM_COL(list, col) + list->cell_x_pad;
1926    cell_area->y      = CELL_Y_FROM_ROW(list, row) + list->cell_y_pad;
1927    cell_area->width  = list->cell_width - list->cell_x_pad;
1928    cell_area->height = list->cell_height - list->cell_y_pad;
1929 
1930 }
1931 
1932 
1933 void
gimv_zlist_draw_cell(GimvZList * list,gint index)1934 gimv_zlist_draw_cell (GimvZList *list, gint index)
1935 {
1936    GtkWidget *cell;
1937    GdkRectangle cell_area;
1938 
1939    g_return_if_fail (list && index != -1);
1940 
1941    if (!GTK_WIDGET_DRAWABLE(list))
1942       return;
1943 
1944    gimv_zlist_cell_area (list, index, &cell_area);
1945    cell = GIMV_ZLIST_CELL_FROM_INDEX (list, index);
1946 
1947 #ifdef USE_GTK2
1948    g_signal_emit (G_OBJECT(list), gimv_zlist_signals [CELL_DRAW], 0,
1949                   cell, &cell_area, &cell_area);
1950 #else /* USE_GTK2 */
1951    gtk_signal_emit (GTK_OBJECT(list), gimv_zlist_signals [CELL_DRAW],
1952                     cell, &cell_area, &cell_area);
1953 #endif /* USE_GTK2 */
1954 
1955    if (index == list->focus)
1956       gimv_zlist_cell_draw_focus (list, index);
1957 }
1958 
1959 
1960 gint
gimv_zlist_cell_index(GimvZList * list,gpointer cell)1961 gimv_zlist_cell_index (GimvZList *list, gpointer cell)
1962 {
1963    gint i;
1964    g_return_val_if_fail (list && cell, -1);
1965 
1966    for (i = 0; i < list->cell_count; i++)
1967       if (GIMV_ZLIST_CELL_FROM_INDEX (list, i) == cell)
1968          return i;
1969 
1970    return -1;
1971 }
1972 
1973 
1974 gint
gimv_zlist_update_cell_size(GimvZList * list,gpointer cell)1975 gimv_zlist_update_cell_size (GimvZList *list, gpointer cell)
1976 {
1977    GtkRequisition requisition;
1978 
1979    g_return_val_if_fail (list && cell, FALSE);
1980 
1981    if (list->flags & GIMV_ZLIST_1)
1982       return FALSE;
1983 
1984 #ifdef USE_GTK2
1985    g_signal_emit (G_OBJECT(list), gimv_zlist_signals [CELL_SIZE_REQUEST], 0,
1986                   cell, &requisition);
1987 #else /* USE_GTK2 */
1988    gtk_signal_emit (GTK_OBJECT(list), gimv_zlist_signals [CELL_SIZE_REQUEST],
1989                     cell, &requisition);
1990 #endif /* USE_GTK2 */
1991 
1992    if (requisition.width  + list->cell_x_pad > list->cell_width ||
1993        requisition.height + list->cell_y_pad > list->cell_height) {
1994       list->cell_width  = MAX(list->cell_width,  requisition.width  + list->cell_x_pad);
1995       list->cell_height = MAX(list->cell_height, requisition.height + list->cell_y_pad);
1996 
1997       gimv_zlist_update (list);
1998       WIDGET_DRAW (GTK_WIDGET (list));
1999 
2000       return TRUE;
2001    }
2002 
2003    return FALSE;
2004 }
2005 
2006 
2007 /*
2008  *
2009  * Focus & Selection handling
2010  *
2011  */
2012 
2013 static void
gimv_zlist_cell_draw_focus(GimvZList * list,gint index)2014 gimv_zlist_cell_draw_focus (GimvZList *list, gint index)
2015 {
2016    GdkRectangle cell_area;
2017    g_return_if_fail (list && index != -1);
2018 
2019    gimv_zlist_cell_area (list, index, &cell_area);
2020 
2021 #ifdef USE_GTK2
2022    g_signal_emit (G_OBJECT(list), gimv_zlist_signals [CELL_DRAW_FOCUS], 0,
2023                   GIMV_ZLIST_CELL_FROM_INDEX (list, index), &cell_area);
2024 #else /* USE_GTK2 */
2025    gtk_signal_emit (GTK_OBJECT(list), gimv_zlist_signals [CELL_DRAW_FOCUS],
2026                     GIMV_ZLIST_CELL_FROM_INDEX (list, index), &cell_area);
2027 #endif /* USE_GTK2 */
2028 }
2029 
2030 
2031 static void
gimv_zlist_cell_draw_default(GimvZList * list,gint index)2032 gimv_zlist_cell_draw_default (GimvZList *list, gint index)
2033 {
2034    GdkRectangle cell_area;
2035 
2036    g_return_if_fail (list);
2037    if (index < 0) return;
2038    /* g_return_if_fail (index != -1); */
2039 
2040    gimv_zlist_cell_area (list, index, &cell_area);
2041 
2042 #ifdef USE_GTK2
2043    g_signal_emit (G_OBJECT(list), gimv_zlist_signals [CELL_DRAW_DEFAULT], 0,
2044                   GIMV_ZLIST_CELL_FROM_INDEX (list, index), &cell_area);
2045 #else /* USE_GTK2 */
2046    gtk_signal_emit (GTK_OBJECT(list), gimv_zlist_signals [CELL_DRAW_DEFAULT],
2047                     GIMV_ZLIST_CELL_FROM_INDEX (list, index), &cell_area);
2048 #endif /* USE_GTK2 */
2049 }
2050 
2051 
2052 void
gimv_zlist_cell_select(GimvZList * list,gint index)2053 gimv_zlist_cell_select (GimvZList *list, gint index)
2054 {
2055    GList *node;
2056 
2057    g_return_if_fail (GIMV_IS_ZLIST (list) && index != -1);
2058 
2059    node = g_list_find (list->selection, GUINT_TO_POINTER(index));
2060    if (!node) {
2061 #ifdef USE_GTK2
2062       g_signal_emit (G_OBJECT(list), gimv_zlist_signals [CELL_SELECT], 0, index);
2063 #else /* USE_GTK2 */
2064       gtk_signal_emit (GTK_OBJECT(list), gimv_zlist_signals [CELL_SELECT], index);
2065 #endif /* USE_GTK2 */
2066       list->selection = g_list_prepend (list->selection, GUINT_TO_POINTER(index));
2067       gimv_zlist_draw_cell (list, index);
2068    }
2069 }
2070 
2071 
2072 void
gimv_zlist_cell_unselect(GimvZList * list,gint index)2073 gimv_zlist_cell_unselect (GimvZList *list, gint index)
2074 {
2075    GList *node;
2076 
2077    g_return_if_fail (GIMV_IS_ZLIST (list) && index != -1);
2078 
2079    node = g_list_find (list->selection, GUINT_TO_POINTER(index));
2080    if (node) {
2081 #ifdef USE_GTK2
2082       g_signal_emit (G_OBJECT(list), gimv_zlist_signals [CELL_UNSELECT], 0, index);
2083 #else /* USE_GTK2 */
2084       gtk_signal_emit (GTK_OBJECT(list), gimv_zlist_signals [CELL_UNSELECT], index);
2085 #endif /* USE_GTK2 */
2086       list->selection = g_list_remove (list->selection, GUINT_TO_POINTER(index));
2087       gimv_zlist_draw_cell (list, index);
2088    }
2089 }
2090 
2091 
2092 void
gimv_zlist_cell_toggle(GimvZList * list,gint index)2093 gimv_zlist_cell_toggle (GimvZList *list, gint index)
2094 {
2095    g_return_if_fail (GIMV_IS_ZLIST (list) && index != -1);
2096 
2097    if (g_list_find (list->selection, GUINT_TO_POINTER(index)))
2098       gimv_zlist_cell_unselect (list, index);
2099    else
2100       gimv_zlist_cell_select (list, index);
2101 }
2102 
2103 
2104 void
gimv_zlist_unselect_all(GimvZList * list)2105 gimv_zlist_unselect_all (GimvZList *list)
2106 {
2107    GList *item;
2108 
2109    item = list->selection;
2110    while (item) {
2111 #ifdef USE_GTK2
2112       g_signal_emit (G_OBJECT(list), gimv_zlist_signals [CELL_UNSELECT], 0,
2113                      GPOINTER_TO_UINT(item->data));
2114 #else /* USE_GTK2 */
2115       gtk_signal_emit (GTK_OBJECT(list), gimv_zlist_signals [CELL_UNSELECT],
2116                        GPOINTER_TO_UINT(item->data));
2117 #endif /* USE_GTK2 */
2118       gimv_zlist_draw_cell (list, GPOINTER_TO_UINT(item->data));
2119 
2120       item = item->next;
2121    }
2122 
2123    g_list_free (list->selection);
2124    list->selection = NULL;
2125 }
2126 
2127 
2128 void
gimv_zlist_extend_selection(GimvZList * list,gint to)2129 gimv_zlist_extend_selection (GimvZList *list, gint to)
2130 {
2131    GList *item;
2132    gint i, s, e;
2133 
2134    g_return_if_fail (list && to != -1);
2135 
2136    if (list->anchor < to) {
2137       s = list->anchor;
2138       e = to;
2139    } else {
2140       s = to;
2141       e = list->anchor;
2142    }
2143 
2144    for (i = s; i <= e; i++)
2145       /* XXX the state of the cell should be cached in the cells array */
2146       if (!g_list_find (list->selection, GUINT_TO_POINTER(i)))
2147          gimv_zlist_cell_select (list, i);
2148       else if (i == list->focus)
2149          gimv_zlist_cell_draw_focus (list, i);
2150 
2151    item = list->selection;
2152    while (item) {
2153       i = GPOINTER_TO_UINT(item->data);
2154       item = item->next;
2155 
2156       if (i < s || i > e)
2157          gimv_zlist_cell_unselect (list, i);
2158    }
2159 }
2160 
2161 
2162 static void
select_each_cell_by_region(GimvZList * list,gint index,gboolean in_region)2163 select_each_cell_by_region (GimvZList *list, gint index, gboolean in_region)
2164 {
2165    gboolean select = FALSE, selected = FALSE;
2166    gboolean is_mask = FALSE;
2167 
2168    g_return_if_fail (index < list->cell_count);
2169 
2170    if (g_list_find (list->selection, GUINT_TO_POINTER (index)))
2171       selected = TRUE;
2172 
2173    if (g_list_find (list->selection_mask, GUINT_TO_POINTER (index)))
2174       is_mask = TRUE;
2175 
2176    switch (list->region_select) {
2177    case GIMV_ZLIST_REGION_SELECT_TOGGLE:
2178       if ((in_region && !is_mask) || (!in_region && is_mask))
2179          select = TRUE;
2180       break;
2181 
2182    case GIMV_ZLIST_REGION_SELECT_EXPAND:
2183       if (in_region || is_mask)
2184          select = TRUE;
2185       break;
2186 
2187    case GIMV_ZLIST_REGION_SELECT_NORMAL:
2188       if (in_region)
2189          select = TRUE;
2190       break;
2191    default:
2192       break;
2193    }
2194 
2195    if (select && !selected)
2196       gimv_zlist_cell_select (list, index);
2197    if (!select && selected)
2198       gimv_zlist_cell_unselect (list, index);
2199 }
2200 
2201 
2202 void
gimv_zlist_cell_select_by_region(GimvZList * list,guint start_col,guint start_row,guint end_col,guint end_row)2203 gimv_zlist_cell_select_by_region (GimvZList *list,
2204                                   guint start_col, guint start_row,
2205                                   guint end_col, guint end_row)
2206 {
2207    gint index = 0, i, j, cols, rows;
2208    guint col, row;
2209 
2210    g_return_if_fail (list);
2211    g_return_if_fail (GIMV_IS_ZLIST (list));
2212    g_return_if_fail (end_col >= start_col);
2213    g_return_if_fail (end_row >= start_row);
2214 
2215    if (list->flags & GIMV_ZLIST_HORIZONTAL) {
2216       cols = list->rows;
2217       rows = list->columns;
2218    } else {
2219       cols = list->columns;
2220       rows = list->rows;
2221    }
2222 
2223    for (i = 0; i < rows; i++) {
2224       for (j = 0; j < cols; j++) {
2225          gboolean in_region = FALSE;
2226 
2227          if (list->flags & GIMV_ZLIST_HORIZONTAL) {
2228             index = list->rows * i + j;
2229          } else {
2230             index = list->columns * i + j;
2231          }
2232          if (index >= list->cell_count) break;
2233 
2234          if (list->flags & GIMV_ZLIST_HORIZONTAL) {
2235             col = i;
2236             row = j;
2237          } else {
2238             col = j;
2239             row = i;
2240          }
2241 
2242          if (row >= start_row && row <= end_row && col >= start_col && col <= end_col)
2243             in_region = TRUE;
2244 
2245          select_each_cell_by_region (list, index, in_region);
2246       }
2247 
2248       if (index >= list->cell_count) break;
2249    }
2250 }
2251 
2252 
2253 void
gimv_zlist_cell_select_by_pixel_region(GimvZList * list,gint start_x,gint start_y,gint end_x,gint end_y)2254 gimv_zlist_cell_select_by_pixel_region (GimvZList *list,
2255                                         gint start_x, gint start_y,
2256                                         gint end_x, gint end_y)
2257 {
2258    gint start_col, start_row, end_col, end_row, cell_x, cell_y;
2259 
2260    g_return_if_fail (list);
2261    g_return_if_fail (GIMV_IS_ZLIST (list));
2262    g_return_if_fail (end_x >= start_x);
2263    g_return_if_fail (end_y >= start_y);
2264 
2265    start_x = start_x - list->x_pad - bw (list);
2266    if (start_x < 0) start_x = 0;
2267    start_y = start_y - list->y_pad - bw (list);
2268    if (start_y < 0) start_y = 0;
2269 
2270    end_x = end_x - list->x_pad - bw (list);
2271    if (end_x < 0) end_x = 0;
2272    end_y = end_y - list->y_pad - bw (list);
2273    if (end_y < 0) end_y = 0;
2274 
2275    start_col = start_x / list->cell_width;
2276    start_row = start_y / list->cell_height;
2277    end_col   = end_x   / list->cell_width;
2278    end_row   = end_y   / list->cell_height;
2279 
2280    /*
2281    cell_x = CELL_X_FROM_COL(list, start_col);
2282    if (start_x  < list->cell_x_pad * 2 + cell_x && start_col > 1)
2283       start_col--;
2284       cell_y = CELL_Y_FROM_ROW(list, start_row);
2285    if (start_y < list->cell_y_pad * 2 + cell_y && start_row > 1)
2286       start_row--;
2287    */
2288 
2289    cell_x = CELL_X_FROM_COL(list, end_col);
2290 
2291    if (GIMV_SCROLLED_X (GIMV_SCROLLED (list), end_x) < list->cell_x_pad * 2 + cell_x
2292        && end_col > 0 && end_col > start_col)
2293    {
2294       end_col--;
2295    }
2296 
2297    cell_y = CELL_Y_FROM_ROW(list, end_row);
2298 
2299    if (GIMV_SCROLLED_Y (GIMV_SCROLLED (list), end_y) < list->cell_y_pad * 2 + cell_y
2300        && end_row > 0 && end_row > start_row)
2301    {
2302       end_row--;
2303    }
2304 
2305    gimv_zlist_cell_select_by_region (list,
2306                                      start_col, start_row,
2307                                      end_col, end_row);
2308 }
2309 
2310 
2311 void
gimv_zlist_set_selection_mask(GimvZList * list,GList * mask_list)2312 gimv_zlist_set_selection_mask (GimvZList *list, GList *mask_list)
2313 {
2314    g_return_if_fail (list);
2315    g_return_if_fail (GIMV_IS_ZLIST (list));
2316 
2317    if (mask_list) {
2318       list->selection_mask = mask_list;
2319    } else {
2320       if (list->selection)
2321          list->selection_mask = g_list_copy (list->selection);
2322       else
2323          list->selection_mask = NULL;
2324    }
2325 }
2326 
2327 
2328 void
gimv_zlist_unset_selection_mask(GimvZList * list)2329 gimv_zlist_unset_selection_mask (GimvZList *list)
2330 {
2331    g_return_if_fail (list);
2332    g_return_if_fail (GIMV_IS_ZLIST (list));
2333 
2334    if (list->selection_mask)
2335       g_list_free (list->selection_mask);
2336    list->selection_mask = NULL;
2337 }
2338 
2339 
2340 void
gimv_zlist_moveto(GimvZList * list,gint index)2341 gimv_zlist_moveto (GimvZList *list, gint index)
2342 {
2343    GdkRectangle cell_area;
2344    GtkAdjustment *adj;
2345 
2346    g_return_if_fail (list && index != -1);
2347 
2348    if (!GTK_WIDGET_DRAWABLE(list) || GIMV_SCROLLED(list)->freeze_count)
2349       return;
2350 
2351    gimv_zlist_cell_area (list, index, &cell_area);
2352 
2353    if (list->flags & GIMV_ZLIST_HORIZONTAL) { /* horizontal list */
2354 
2355       adj = GIMV_SCROLLED(list)->h_adjustment;
2356 
2357       if (cell_area.x < 0) {
2358          adj->value += cell_area.x;
2359 #ifdef USE_GTK2
2360          g_signal_emit_by_name (G_OBJECT(adj), "value_changed");
2361 #else /* USE_GTK2 */
2362          gtk_signal_emit_by_name (GTK_OBJECT(adj), "value_changed");
2363 #endif /* USE_GTK2 */
2364       }
2365 
2366       if (cell_area.x + cell_area.width > GTK_WIDGET(list)->allocation.width) {
2367          adj->value += (cell_area.x - adj->page_size) + cell_area.width;
2368 #ifdef USE_GTK2
2369          g_signal_emit_by_name (G_OBJECT(adj), "value_changed");
2370 #else /* USE_GTK2 */
2371          gtk_signal_emit_by_name (GTK_OBJECT(adj), "value_changed");
2372 #endif /* USE_GTK2 */
2373       }
2374 
2375    } else { /* vertical list */
2376 
2377       adj = GIMV_SCROLLED(list)->v_adjustment;
2378 
2379       if (cell_area.y < 0) {
2380          adj->value += cell_area.y;
2381 #ifdef USE_GTK2
2382          g_signal_emit_by_name (G_OBJECT(adj), "value_changed");
2383 #else /* USE_GTK2 */
2384          gtk_signal_emit_by_name (GTK_OBJECT(adj), "value_changed");
2385 #endif /* USE_GTK2 */
2386       }
2387 
2388       if (cell_area.y + cell_area.height > GTK_WIDGET(list)->allocation.height) {
2389          adj->value += (cell_area.y - adj->page_size) + cell_area.height;
2390 #ifdef USE_GTK2
2391          g_signal_emit_by_name (G_OBJECT(adj), "value_changed");
2392 #else /* USE_GTK2 */
2393          gtk_signal_emit_by_name (GTK_OBJECT(adj), "value_changed");
2394 #endif /* USE_GTK2 */
2395       }
2396    }
2397 }
2398 
2399 
2400 void
gimv_zlist_cell_set_focus(GimvZList * list,gint index)2401 gimv_zlist_cell_set_focus (GimvZList *list, gint index)
2402 {
2403    g_return_if_fail (GIMV_IS_ZLIST (list));
2404    g_return_if_fail (index >= 0 && index < list->cell_count);
2405 
2406    list->focus = index;
2407    gimv_zlist_cell_draw_focus (list, list->focus);
2408 }
2409 
2410 
2411 void
gimv_zlist_cell_unset_focus(GimvZList * list)2412 gimv_zlist_cell_unset_focus (GimvZList *list)
2413 {
2414    gint focus;
2415 
2416    g_return_if_fail (GIMV_IS_ZLIST (list));
2417 
2418    focus = list->focus;
2419    list->focus = -1;
2420 
2421    if (focus >= 0 && focus < list->cell_count)
2422       gimv_zlist_cell_draw_default (list, focus);
2423 }
2424