r110: Added a option for using RISC OS mouse bindings to the options box, although
[rox-filer.git] / ROX-Filer / src / collection.c
blob3cd134f4fdd7cf90154912c4abd866d0a85a8254
1 /*
2 * $Id$
4 * Collection - a GTK+ widget
5 * Copyright (C) 1999, Thomas Leonard, <tal197@ecs.soton.ac.uk>.
7 * The collection widget provides an area for displaying a collection of
8 * objects (such as files). It allows the user to choose a selection of
9 * them and provides signals to allow popping up menus, detecting
10 * double-clicks etc.
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the Free
14 * Software Foundation; either version 2 of the License, or (at your option)
15 * any later version.
17 * This program is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
20 * more details.
22 * You should have received a copy of the GNU General Public License along with
23 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
24 * Place, Suite 330, Boston, MA 02111-1307 USA
27 #include <stdlib.h>
29 #include <gtk/gtk.h>
30 #include "collection.h"
32 #define MIN_WIDTH 80
33 #define MIN_HEIGHT 60
34 #define MINIMUM_ITEMS 16
36 enum
38 ARG_0,
39 ARG_VADJUSTMENT
42 /* Signals:
44 * void open_item(collection, item, item_number, user_data)
45 * User has double clicked on this item.
47 * void drag_selection(collection, motion_event, number_selected, user_data)
48 * User has tried to drag the selection.
50 * void show_menu(collection, button_event, item, user_data)
51 * User has right-clicked on the collection. 'item' is the number
52 * of the item clicked, or -1 if the click was over the background.
54 * void gain_selection(collection, time, user_data)
55 * We've gone from no selected items to having a selection.
56 * Time is the time of the event that caused the change, or
57 * GDK_CURRENT_TIME if not known.
59 * void lose_selection(collection, time, user_data)
60 * We've dropped to having no selected items.
61 * Time is the time of the event that caused the change, or
62 * GDK_CURRENT_TIME if not known.
64 enum
66 OPEN_ITEM,
67 DRAG_SELECTION,
68 SHOW_MENU,
69 GAIN_SELECTION,
70 LOSE_SELECTION,
71 LAST_SIGNAL
74 static guint collection_signals[LAST_SIGNAL] = { 0 };
76 static guint32 current_event_time = GDK_CURRENT_TIME;
78 static GtkWidgetClass *parent_class = NULL;
80 /* Static prototypes */
81 static void draw_one_item(Collection *collection,
82 int item,
83 GdkRectangle *area);
84 static void collection_class_init(CollectionClass *class);
85 static void collection_init(Collection *object);
86 static void collection_destroy(GtkObject *object);
87 static void collection_finalize(GtkObject *object);
88 static void collection_realize(GtkWidget *widget);
89 static gint collection_paint(Collection *collection,
90 GdkRectangle *area);
91 static void collection_size_request(GtkWidget *widget,
92 GtkRequisition *requisition);
93 static void collection_size_allocate(GtkWidget *widget,
94 GtkAllocation *allocation);
95 static void collection_set_adjustment(Collection *collection,
96 GtkAdjustment *vadj);
97 static void collection_set_arg( GtkObject *object,
98 GtkArg *arg,
99 guint arg_id);
100 static void collection_get_arg( GtkObject *object,
101 GtkArg *arg,
102 guint arg_id);
103 static void collection_adjustment(GtkAdjustment *adjustment,
104 Collection *collection);
105 static void collection_disconnect(GtkAdjustment *adjustment,
106 Collection *collection);
107 static void set_vadjustment(Collection *collection);
108 static void collection_draw(GtkWidget *widget, GdkRectangle *area);
109 static gint collection_expose(GtkWidget *widget, GdkEventExpose *event);
110 static void scroll_by(Collection *collection, gint diff);
111 static gint collection_button_press(GtkWidget *widget,
112 GdkEventButton *event);
113 static gint collection_button_release(GtkWidget *widget,
114 GdkEventButton *event);
115 static void default_draw_item(GtkWidget *widget,
116 CollectionItem *data,
117 GdkRectangle *area);
118 static gboolean default_test_point(Collection *collection,
119 int point_x, int point_y,
120 CollectionItem *data,
121 int width, int height);
122 static gint collection_motion_notify(GtkWidget *widget,
123 GdkEventMotion *event);
124 static void add_lasso_box(Collection *collection);
125 static void remove_lasso_box(Collection *collection);
126 static void draw_lasso_box(Collection *collection);
127 static int item_at_row_col(Collection *collection, int row, int col);
128 static void collection_clear_except(Collection *collection, gint exception);
129 static void cancel_wink(Collection *collection);
131 static void draw_one_item(Collection *collection, int item, GdkRectangle *area)
133 collection->draw_item((GtkWidget *) collection,
134 &collection->items[item],
135 area);
136 if (item == collection->wink_item)
137 gdk_draw_rectangle(((GtkWidget *) collection)->window,
138 ((GtkWidget *) collection)->style->black_gc,
139 FALSE,
140 area->x + 1, area->y + 1,
141 area->width - 3, area->height - 3);
142 if (item == collection->cursor_item)
143 gdk_draw_rectangle(((GtkWidget *) collection)->window,
144 ((GtkWidget *) collection)->style->black_gc,
145 FALSE,
146 area->x, area->y,
147 area->width - 1, area->height - 1);
151 GtkType collection_get_type(void)
153 static guint my_type = 0;
155 if (!my_type)
157 static const GtkTypeInfo my_info =
159 "Collection",
160 sizeof(Collection),
161 sizeof(CollectionClass),
162 (GtkClassInitFunc) collection_class_init,
163 (GtkObjectInitFunc) collection_init,
164 NULL, /* Reserved 1 */
165 NULL, /* Reserved 2 */
166 (GtkClassInitFunc) NULL /* base_class_init_func */
169 my_type = gtk_type_unique(gtk_widget_get_type(),
170 &my_info);
173 return my_type;
176 static void collection_class_init(CollectionClass *class)
178 GtkObjectClass *object_class;
179 GtkWidgetClass *widget_class;
181 object_class = (GtkObjectClass*) class;
182 widget_class = (GtkWidgetClass*) class;
184 parent_class = gtk_type_class(gtk_widget_get_type());
186 gtk_object_add_arg_type("Collection::vadjustment",
187 GTK_TYPE_ADJUSTMENT,
188 GTK_ARG_READWRITE | GTK_ARG_CONSTRUCT,
189 ARG_VADJUSTMENT);
191 object_class->destroy = collection_destroy;
192 object_class->finalize = collection_finalize;
194 widget_class->realize = collection_realize;
195 widget_class->draw = collection_draw;
196 widget_class->expose_event = collection_expose;
197 widget_class->size_request = collection_size_request;
198 widget_class->size_allocate = collection_size_allocate;
200 widget_class->button_press_event = collection_button_press;
201 widget_class->button_release_event = collection_button_release;
202 widget_class->motion_notify_event = collection_motion_notify;
203 object_class->set_arg = collection_set_arg;
204 object_class->get_arg = collection_get_arg;
206 class->open_item = NULL;
207 class->drag_selection = NULL;
208 class->show_menu = NULL;
209 class->gain_selection = NULL;
210 class->lose_selection = NULL;
212 collection_signals[OPEN_ITEM] = gtk_signal_new("open_item",
213 GTK_RUN_FIRST,
214 object_class->type,
215 GTK_SIGNAL_OFFSET(CollectionClass,
216 open_item),
217 gtk_marshal_NONE__POINTER_UINT,
218 GTK_TYPE_NONE, 2,
219 GTK_TYPE_POINTER,
220 GTK_TYPE_UINT);
221 collection_signals[DRAG_SELECTION] = gtk_signal_new("drag_selection",
222 GTK_RUN_FIRST,
223 object_class->type,
224 GTK_SIGNAL_OFFSET(CollectionClass,
225 drag_selection),
226 gtk_marshal_NONE__POINTER_UINT,
227 GTK_TYPE_NONE, 2,
228 GTK_TYPE_POINTER,
229 GTK_TYPE_UINT);
230 collection_signals[SHOW_MENU] = gtk_signal_new("show_menu",
231 GTK_RUN_FIRST,
232 object_class->type,
233 GTK_SIGNAL_OFFSET(CollectionClass,
234 show_menu),
235 gtk_marshal_NONE__POINTER_INT,
236 GTK_TYPE_NONE, 2,
237 GTK_TYPE_POINTER,
238 GTK_TYPE_INT);
239 collection_signals[GAIN_SELECTION] = gtk_signal_new("gain_selection",
240 GTK_RUN_FIRST,
241 object_class->type,
242 GTK_SIGNAL_OFFSET(CollectionClass,
243 gain_selection),
244 gtk_marshal_NONE__UINT,
245 GTK_TYPE_NONE, 1,
246 GTK_TYPE_UINT);
247 collection_signals[LOSE_SELECTION] = gtk_signal_new("lose_selection",
248 GTK_RUN_FIRST,
249 object_class->type,
250 GTK_SIGNAL_OFFSET(CollectionClass,
251 lose_selection),
252 gtk_marshal_NONE__UINT,
253 GTK_TYPE_NONE, 1,
254 GTK_TYPE_UINT);
256 gtk_object_class_add_signals(object_class,
257 collection_signals, LAST_SIGNAL);
260 static void collection_init(Collection *object)
262 object->panel = FALSE;
263 object->number_of_items = 0;
264 object->number_selected = 0;
265 object->columns = 1;
266 object->item_width = 64;
267 object->item_height = 64;
268 object->vadj = NULL;
269 object->paint_level = PAINT_OVERWRITE;
270 object->last_scroll = 0;
272 object->items = g_malloc(sizeof(CollectionItem) * MINIMUM_ITEMS);
273 object->cursor_item = -1;
274 object->wink_item = -1;
275 object->array_size = MINIMUM_ITEMS;
276 object->draw_item = default_draw_item;
277 object->test_point = default_test_point;
279 object->buttons_pressed = 0;
280 object->may_drag = FALSE;
282 return;
285 GtkWidget* collection_new(GtkAdjustment *vadj)
287 if (vadj)
288 g_return_val_if_fail(GTK_IS_ADJUSTMENT(vadj), NULL);
290 return GTK_WIDGET(gtk_widget_new(collection_get_type(),
291 "vadjustment", vadj,
292 NULL));
295 void collection_set_functions(Collection *collection,
296 CollectionDrawFunc draw_item,
297 CollectionTestFunc test_point)
299 GtkWidget *widget;
301 g_return_if_fail(collection != NULL);
302 g_return_if_fail(IS_COLLECTION(collection));
304 widget = GTK_WIDGET(collection);
306 if (!draw_item)
307 draw_item = default_draw_item;
308 if (!test_point)
309 test_point = default_test_point;
311 collection->draw_item = draw_item;
312 collection->test_point = test_point;
314 if (GTK_WIDGET_REALIZED(widget))
316 collection->paint_level = PAINT_CLEAR;
317 gtk_widget_queue_clear(widget);
321 /* After this we are unusable, but our data (if any) is still hanging around.
322 * It will be freed later with finalize.
324 static void collection_destroy(GtkObject *object)
326 Collection *collection;
328 g_return_if_fail(object != NULL);
329 g_return_if_fail(IS_COLLECTION(object));
331 collection = COLLECTION(object);
333 if (collection->wink_item != -1)
335 collection->wink_item = -1;
336 gtk_timeout_remove(collection->wink_timeout);
339 gtk_signal_disconnect_by_data(GTK_OBJECT(collection->vadj),
340 collection);
342 if (GTK_OBJECT_CLASS(parent_class)->destroy)
343 (*GTK_OBJECT_CLASS(parent_class)->destroy)(object);
346 /* This is the last thing that happens to us. Free all data. */
347 static void collection_finalize(GtkObject *object)
349 Collection *collection;
351 collection = COLLECTION(object);
353 if (collection->vadj)
355 gtk_object_unref(GTK_OBJECT(collection->vadj));
358 g_free(collection->items);
361 static void collection_realize(GtkWidget *widget)
363 Collection *collection;
364 GdkWindowAttr attributes;
365 gint attributes_mask;
366 GdkGCValues xor_values;
368 g_return_if_fail(widget != NULL);
369 g_return_if_fail(IS_COLLECTION(widget));
370 g_return_if_fail(widget->parent != NULL);
372 GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
373 collection = COLLECTION(widget);
375 attributes.x = widget->allocation.x;
376 attributes.y = widget->allocation.y;
377 attributes.width = widget->allocation.width;
378 attributes.height = widget->allocation.height;
379 attributes.wclass = GDK_INPUT_OUTPUT;
380 attributes.window_type = GDK_WINDOW_CHILD;
381 attributes.event_mask = gtk_widget_get_events(widget) |
382 GDK_EXPOSURE_MASK |
383 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
384 GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK;
385 attributes.visual = gtk_widget_get_visual(widget);
386 attributes.colormap = gtk_widget_get_colormap(widget);
388 attributes_mask = GDK_WA_X | GDK_WA_Y |
389 GDK_WA_VISUAL | GDK_WA_COLORMAP;
390 widget->window = gdk_window_new(widget->parent->window,
391 &attributes, attributes_mask);
393 widget->style = gtk_style_attach(widget->style, widget->window);
395 gdk_window_set_user_data(widget->window, widget);
397 gdk_window_set_background(widget->window,
398 &widget->style->base[GTK_STATE_INSENSITIVE]);
400 /* Try to stop everything flickering horribly */
401 gdk_window_set_static_gravities(widget->window, TRUE);
403 set_vadjustment(collection);
405 xor_values.function = GDK_XOR;
406 xor_values.foreground.red = 0xffff;
407 xor_values.foreground.green = 0xffff;
408 xor_values.foreground.blue = 0xffff;
409 gdk_color_alloc(gtk_widget_get_colormap(widget),
410 &xor_values.foreground);
411 collection->xor_gc = gdk_gc_new_with_values(widget->window,
412 &xor_values,
413 GDK_GC_FOREGROUND
414 | GDK_GC_FUNCTION);
417 static void collection_size_request(GtkWidget *widget,
418 GtkRequisition *requisition)
420 requisition->width = MIN_WIDTH;
421 requisition->height = MIN_HEIGHT;
424 static void collection_size_allocate(GtkWidget *widget,
425 GtkAllocation *allocation)
427 Collection *collection;
428 int old_columns;
430 g_return_if_fail(widget != NULL);
431 g_return_if_fail(IS_COLLECTION(widget));
432 g_return_if_fail(allocation != NULL);
434 collection = COLLECTION(widget);
436 old_columns = collection->columns;
437 widget->allocation = *allocation;
439 collection->columns = allocation->width / collection->item_width;
440 if (collection->columns < 1)
441 collection->columns = 1;
443 if (GTK_WIDGET_REALIZED(widget))
445 gdk_window_move_resize(widget->window,
446 allocation->x, allocation->y,
447 allocation->width, allocation->height);
449 if (old_columns != collection->columns)
451 collection->paint_level = PAINT_CLEAR;
452 gtk_widget_queue_clear(widget);
455 set_vadjustment(collection);
459 static gint collection_paint(Collection *collection,
460 GdkRectangle *area)
462 GdkRectangle whole, item_area;
463 GtkWidget *widget;
464 int row, col;
465 int item;
466 int scroll;
467 int start_row, last_row;
468 int start_col, last_col;
469 int phys_last_col;
470 GdkRectangle clip;
472 scroll = collection->vadj->value;
474 widget = GTK_WIDGET(collection);
476 if (collection->paint_level > PAINT_NORMAL || area == NULL)
478 guint width, height;
479 gdk_window_get_size(widget->window, &width, &height);
481 whole.x = 0;
482 whole.y = 0;
483 whole.width = width;
484 whole.height = height;
486 area = &whole;
488 if (collection->paint_level == PAINT_CLEAR
489 && !collection->lasso_box)
490 gdk_window_clear(widget->window);
492 collection->paint_level = PAINT_NORMAL;
495 /* Calculate the ranges to plot */
496 start_row = (area->y + scroll) / collection->item_height;
497 last_row = (area->y + area->height - 1 + scroll)
498 / collection->item_height;
499 row = start_row;
501 start_col = area->x / collection->item_width;
502 phys_last_col = (area->x + area->width - 1) / collection->item_width;
504 if (collection->lasso_box)
506 /* You can't be too careful with lasso boxes...
508 * clip gives the total area drawn over (this may be larger
509 * than the requested area). It's used to redraw the lasso
510 * box.
512 clip.x = start_col * collection->item_width;
513 clip.y = start_row * collection->item_height - scroll;
514 clip.width = (phys_last_col - start_col + 1)
515 * collection->item_width;
516 clip.height = (last_row - start_row + 1)
517 * collection->item_height;
519 gdk_window_clear_area(widget->window,
520 clip.x, clip.y, clip.width, clip.height);
523 if (start_col < collection->columns)
525 if (phys_last_col >= collection->columns)
526 last_col = collection->columns - 1;
527 else
528 last_col = phys_last_col;
530 col = start_col;
532 item = row * collection->columns + col;
533 item_area.width = collection->item_width;
534 item_area.height = collection->item_height;
536 while (item < collection->number_of_items && row <= last_row)
538 item_area.x = col * collection->item_width;
539 item_area.y = row * collection->item_height - scroll;
541 draw_one_item(collection, item, &item_area);
542 col++;
544 if (col > last_col)
546 col = start_col;
547 row++;
548 item = row * collection->columns + col;
550 else
551 item++;
555 if (collection->lasso_box)
557 gdk_gc_set_clip_rectangle(collection->xor_gc, &clip);
558 draw_lasso_box(collection);
559 gdk_gc_set_clip_rectangle(collection->xor_gc, NULL);
562 return FALSE;
565 static void default_draw_item( GtkWidget *widget,
566 CollectionItem *item,
567 GdkRectangle *area)
569 gdk_draw_arc(widget->window,
570 item->selected ? widget->style->white_gc
571 : widget->style->black_gc,
572 TRUE,
573 area->x, area->y,
574 area->width, area->height,
575 0, 360 * 64);
579 static gboolean default_test_point(Collection *collection,
580 int point_x, int point_y,
581 CollectionItem *item,
582 int width, int height)
584 float f_x, f_y;
586 /* Convert to point in unit circle */
587 f_x = ((float) point_x / width) - 0.5;
588 f_y = ((float) point_y / height) - 0.5;
590 return (f_x * f_x) + (f_y * f_y) <= .25;
593 static void collection_set_arg( GtkObject *object,
594 GtkArg *arg,
595 guint arg_id)
597 Collection *collection;
599 collection = COLLECTION(object);
601 switch (arg_id)
603 case ARG_VADJUSTMENT:
604 collection_set_adjustment(collection,
605 GTK_VALUE_POINTER(*arg));
606 break;
607 default:
608 break;
612 static void collection_set_adjustment( Collection *collection,
613 GtkAdjustment *vadj)
615 if (vadj)
616 g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
617 else
618 vadj = GTK_ADJUSTMENT(gtk_adjustment_new(0.0,
619 0.0, 0.0,
620 0.0, 0.0, 0.0));
621 if (collection->vadj && (collection->vadj != vadj))
623 gtk_signal_disconnect_by_data(GTK_OBJECT(collection->vadj),
624 collection);
625 gtk_object_unref(GTK_OBJECT(collection->vadj));
628 if (collection->vadj != vadj)
630 collection->vadj = vadj;
631 gtk_object_ref(GTK_OBJECT(collection->vadj));
632 gtk_object_sink(GTK_OBJECT(collection->vadj));
634 gtk_signal_connect(GTK_OBJECT(collection->vadj),
635 "changed",
636 (GtkSignalFunc) collection_adjustment,
637 collection);
638 gtk_signal_connect(GTK_OBJECT(collection->vadj),
639 "value_changed",
640 (GtkSignalFunc) collection_adjustment,
641 collection);
642 gtk_signal_connect(GTK_OBJECT(collection->vadj),
643 "disconnect",
644 (GtkSignalFunc) collection_disconnect,
645 collection);
646 collection_adjustment(vadj, collection);
650 static void collection_get_arg( GtkObject *object,
651 GtkArg *arg,
652 guint arg_id)
654 Collection *collection;
656 collection = COLLECTION(object);
658 switch (arg_id)
660 case ARG_VADJUSTMENT:
661 GTK_VALUE_POINTER(*arg) = collection->vadj;
662 break;
663 default:
664 arg->type = GTK_TYPE_INVALID;
665 break;
669 /* Something about the adjustment has changed */
670 static void collection_adjustment(GtkAdjustment *adjustment,
671 Collection *collection)
673 gint diff;
675 g_return_if_fail(adjustment != NULL);
676 g_return_if_fail(GTK_IS_ADJUSTMENT(adjustment));
677 g_return_if_fail(collection != NULL);
678 g_return_if_fail(IS_COLLECTION(collection));
680 diff = ((gint) adjustment->value) - collection->last_scroll;
682 if (diff)
684 collection->last_scroll = adjustment->value;
686 scroll_by(collection, diff);
690 static void collection_disconnect(GtkAdjustment *adjustment,
691 Collection *collection)
693 g_return_if_fail(adjustment != NULL);
694 g_return_if_fail(GTK_IS_ADJUSTMENT(adjustment));
695 g_return_if_fail(collection != NULL);
696 g_return_if_fail(IS_COLLECTION(collection));
698 collection_set_adjustment(collection, NULL);
701 static void set_vadjustment(Collection *collection)
703 GtkWidget *widget;
704 guint height;
705 int cols, rows;
707 widget = GTK_WIDGET(collection);
709 if (!GTK_WIDGET_REALIZED(widget))
710 return;
712 gdk_window_get_size(widget->window, NULL, &height);
713 cols = collection->columns;
714 rows = (collection->number_of_items + cols - 1) / cols;
716 collection->vadj->lower = 0.0;
717 collection->vadj->upper = collection->item_height * rows;
719 collection->vadj->step_increment =
720 MIN(collection->vadj->upper, collection->item_height / 4);
722 collection->vadj->page_increment =
723 MIN(collection->vadj->upper,
724 height - 5.0);
726 collection->vadj->page_size = MIN(collection->vadj->upper, height);
728 collection->vadj->value = MIN(collection->vadj->value,
729 collection->vadj->upper - collection->vadj->page_size);
731 collection->vadj->value = MAX(collection->vadj->value, 0.0);
733 gtk_signal_emit_by_name(GTK_OBJECT(collection->vadj), "changed");
736 static void collection_draw(GtkWidget *widget, GdkRectangle *area)
738 Collection *collection;
740 g_return_if_fail(widget != NULL);
741 g_return_if_fail(IS_COLLECTION(widget));
742 g_return_if_fail(area != NULL); /* Not actually used */
744 collection = COLLECTION(widget);
746 if (collection->paint_level > PAINT_NORMAL)
747 collection_paint(collection, area);
750 static gint collection_expose(GtkWidget *widget, GdkEventExpose *event)
752 g_return_val_if_fail(widget != NULL, FALSE);
753 g_return_val_if_fail(IS_COLLECTION(widget), FALSE);
754 g_return_val_if_fail(event != NULL, FALSE);
756 collection_paint(COLLECTION(widget), &event->area);
758 return FALSE;
761 /* Positive makes the contents go move up the screen */
762 static void scroll_by(Collection *collection, gint diff)
764 GtkWidget *widget;
765 guint width, height;
766 guint from_y, to_y;
767 guint amount;
768 GdkRectangle new_area;
770 widget = GTK_WIDGET(collection);
772 if (collection->lasso_box)
773 remove_lasso_box(collection);
775 gdk_window_get_size(widget->window, &width, &height);
776 new_area.x = 0;
777 new_area.width = width;
779 if (diff > 0)
781 amount = diff;
782 from_y = amount;
783 to_y = 0;
784 new_area.y = height - amount;
786 else
788 amount = -diff;
789 from_y = 0;
790 to_y = amount;
791 new_area.y = 0;
794 new_area.height = amount;
796 if (amount < height)
798 gdk_draw_pixmap(widget->window,
799 widget->style->white_gc,
800 widget->window,
802 from_y,
804 to_y,
805 width,
806 height - amount);
807 /* We have to redraw everything because any pending
808 * expose events now contain invalid areas.
809 * Don't need to clear the area first though...
811 if (collection->paint_level < PAINT_OVERWRITE)
812 collection->paint_level = PAINT_OVERWRITE;
814 else
815 collection->paint_level = PAINT_CLEAR;
817 gdk_window_clear_area(widget->window,
818 0, new_area.y,
819 width, new_area.height);
820 collection_paint(collection, NULL);
823 static void resize_arrays(Collection *collection, guint new_size)
825 g_return_if_fail(collection != NULL);
826 g_return_if_fail(IS_COLLECTION(collection));
827 g_return_if_fail(new_size >= collection->number_of_items);
829 collection->items = g_realloc(collection->items,
830 sizeof(CollectionItem) * new_size);
831 collection->array_size = new_size;
834 static gint collection_button_press(GtkWidget *widget,
835 GdkEventButton *event)
837 Collection *collection;
838 int row, col;
839 int item;
840 int action;
841 int scroll;
842 guint stacked_time;
844 g_return_val_if_fail(widget != NULL, FALSE);
845 g_return_val_if_fail(IS_COLLECTION(widget), FALSE);
846 g_return_val_if_fail(event != NULL, FALSE);
848 collection = COLLECTION(widget);
850 if (event->button > 3)
852 int diff;
854 /* Wheel mouse scrolling */
855 if (event->button == 4)
856 diff = -((signed int) collection->item_height) / 4;
857 else if (event->button == 5)
858 diff = collection->item_height / 4;
859 else
860 diff = 0;
862 if (diff)
864 int old_value = collection->vadj->value;
865 int new_value = 0;
866 gboolean box = collection->lasso_box;
868 new_value = CLAMP(old_value + diff, 0.0,
869 collection->vadj->upper
870 - collection->vadj->page_size);
871 diff = new_value - old_value;
872 if (diff)
874 if (box)
876 remove_lasso_box(collection);
877 collection->drag_box_y[0] -= diff;
879 collection->vadj->value = new_value;
880 gtk_signal_emit_by_name(
881 GTK_OBJECT(collection->vadj),
882 "changed");
883 if (box)
884 add_lasso_box(collection);
887 return FALSE;
890 scroll = collection->vadj->value;
892 if (event->type == GDK_BUTTON_PRESS && event->button < 3)
894 if (collection->buttons_pressed++ == 0)
895 gtk_grab_add(widget);
896 else
897 return FALSE; /* Ignore extra presses */
900 if (event->state & GDK_CONTROL_MASK)
901 action = 2;
902 else
903 action = event->button;
905 /* Ignore all clicks while we are dragging a lasso box */
906 if (collection->lasso_box)
907 return TRUE;
909 col = event->x / collection->item_width;
910 row = (event->y + scroll) / collection->item_height;
912 if (col < 0 || row < 0 || col >= collection->columns)
913 item = -1;
914 else
916 item = col + row * collection->columns;
917 if (item >= collection->number_of_items
919 !collection->test_point(collection,
920 event->x - col * collection->item_width,
921 event->y - row * collection->item_height
922 + scroll,
923 &collection->items[item],
924 collection->item_width,
925 collection->item_height))
927 item = -1;
931 collection->drag_box_x[0] = event->x;
932 collection->drag_box_y[0] = event->y;
934 stacked_time = current_event_time;
935 current_event_time = event->time;
937 if (event->button == 3)
939 gtk_signal_emit(GTK_OBJECT(collection),
940 collection_signals[SHOW_MENU],
941 event,
942 item);
944 else if (event->type == GDK_2BUTTON_PRESS && collection->panel)
946 /* Do nothing */
948 else if (event->type == GDK_2BUTTON_PRESS || collection->panel)
950 if (item >= 0)
952 if (collection->buttons_pressed)
954 gtk_grab_remove(widget);
955 collection->buttons_pressed = 0;
957 collection_unselect_item(collection, item);
958 gtk_signal_emit(GTK_OBJECT(collection),
959 collection_signals[OPEN_ITEM],
960 collection->items[item].data,
961 item);
964 else if (event->type == GDK_BUTTON_PRESS)
966 collection->may_drag = event->button < 3;
968 if (item >= 0)
970 if (action == 1)
972 if (!collection->items[item].selected)
974 collection_select_item(collection,
975 item);
976 collection_clear_except(collection,
977 item);
980 else
981 collection_toggle_item(collection, item);
983 else if (action == 1)
984 collection_clear_selection(collection);
987 current_event_time = stacked_time;
988 return FALSE;
991 static gint collection_button_release(GtkWidget *widget,
992 GdkEventButton *event)
994 Collection *collection;
995 int top, bottom;
996 int row, last_row;
997 int w, h;
998 int col, start_col, last_col;
999 int scroll;
1000 int item;
1001 guint stacked_time;
1003 g_return_val_if_fail(widget != NULL, FALSE);
1004 g_return_val_if_fail(IS_COLLECTION(widget), FALSE);
1005 g_return_val_if_fail(event != NULL, FALSE);
1007 collection = COLLECTION(widget);
1009 scroll = collection->vadj->value;
1011 if (event->button > 2)
1012 return FALSE;
1013 if (collection->buttons_pressed == 0)
1014 return FALSE;
1015 if (--collection->buttons_pressed == 0)
1016 gtk_grab_remove(widget);
1017 else
1018 return FALSE; /* Wait until ALL buttons are up */
1020 if (!collection->lasso_box)
1021 return FALSE;
1023 remove_lasso_box(collection);
1025 w = collection->item_width;
1026 h = collection->item_height;
1028 top = collection->drag_box_y[0] + scroll;
1029 bottom = collection->drag_box_y[1] + scroll;
1030 if (top > bottom)
1032 int tmp;
1033 tmp = top;
1034 top = bottom;
1035 bottom = tmp;
1037 top += h / 4;
1038 bottom -= h / 4;
1040 row = MAX(top / h, 0);
1041 last_row = bottom / h;
1043 top = collection->drag_box_x[0]; /* (left) */
1044 bottom = collection->drag_box_x[1];
1045 if (top > bottom)
1047 int tmp;
1048 tmp = top;
1049 top = bottom;
1050 bottom = tmp;
1052 top += w / 4;
1053 bottom -= w / 4;
1054 start_col = MAX(top / w, 0);
1055 last_col = bottom / w;
1056 if (last_col >= collection->columns)
1057 last_col = collection->columns - 1;
1059 stacked_time = current_event_time;
1060 current_event_time = event->time;
1062 while (row <= last_row)
1064 col = start_col;
1065 item = row * collection->columns + col;
1066 while (col <= last_col)
1068 if (item >= collection->number_of_items)
1070 current_event_time = stacked_time;
1071 return FALSE;
1073 collection_select_item(collection, item);
1074 col++;
1075 item++;
1077 row++;
1080 current_event_time = stacked_time;
1082 return FALSE;
1085 static gint collection_motion_notify(GtkWidget *widget,
1086 GdkEventMotion *event)
1088 Collection *collection;
1089 int x, y;
1090 guint stacked_time;
1092 g_return_val_if_fail(widget != NULL, FALSE);
1093 g_return_val_if_fail(IS_COLLECTION(widget), FALSE);
1094 g_return_val_if_fail(event != NULL, FALSE);
1096 collection = COLLECTION(widget);
1098 if (collection->buttons_pressed == 0)
1099 return FALSE;
1101 stacked_time = current_event_time;
1102 current_event_time = event->time;
1104 if (event->window != widget->window)
1105 gdk_window_get_pointer(widget->window, &x, &y, NULL);
1106 else
1108 x = event->x;
1109 y = event->y;
1112 if (collection->lasso_box)
1114 int new_value = 0, diff;
1115 int height;
1117 gdk_window_get_size(widget->window, NULL, &height);
1119 if (y < 0)
1121 int old_value = collection->vadj->value;
1123 new_value = MAX(old_value + y / 10, 0.0);
1124 diff = new_value - old_value;
1126 else if (y > height)
1128 int old_value = collection->vadj->value;
1130 new_value = MIN(old_value + (y - height) / 10,
1131 collection->vadj->upper
1132 - collection->vadj->page_size);
1133 diff = new_value - old_value;
1135 else
1136 diff = 0;
1138 remove_lasso_box(collection);
1139 collection->drag_box_x[1] = x;
1140 collection->drag_box_y[1] = y;
1142 if (diff)
1144 collection->drag_box_y[0] -= diff;
1145 collection->vadj->value = new_value;
1146 gtk_signal_emit_by_name(GTK_OBJECT(collection->vadj),
1147 "changed");
1149 add_lasso_box(collection);
1151 else if (collection->may_drag)
1153 int dx = x - collection->drag_box_x[0];
1154 int dy = y - collection->drag_box_y[0];
1156 if (abs(dx) > 9 || abs(dy) > 9)
1158 int row, col, item;
1159 int scroll = collection->vadj->value;
1161 collection->may_drag = FALSE;
1163 col = collection->drag_box_x[0]
1164 / collection->item_width;
1165 row = (collection->drag_box_y[0] + scroll)
1166 / collection->item_height;
1167 item = item_at_row_col(collection, row, col);
1169 if (item != -1 && collection->test_point(collection,
1170 collection->drag_box_x[0] -
1171 col * collection->item_width,
1172 collection->drag_box_y[0]
1173 - row * collection->item_height
1174 + scroll,
1175 &collection->items[item],
1176 collection->item_width,
1177 collection->item_height))
1179 collection->buttons_pressed = 0;
1180 gtk_grab_remove(widget);
1181 collection_select_item(collection, item);
1182 gtk_signal_emit(GTK_OBJECT(collection),
1183 collection_signals[DRAG_SELECTION],
1184 event,
1185 collection->number_selected);
1187 else
1189 collection->drag_box_x[1] = x;
1190 collection->drag_box_y[1] = y;
1191 add_lasso_box(collection);
1196 current_event_time = stacked_time;
1197 return FALSE;
1200 static void add_lasso_box(Collection *collection)
1202 g_return_if_fail(collection != NULL);
1203 g_return_if_fail(IS_COLLECTION(collection));
1204 g_return_if_fail(collection->lasso_box == FALSE);
1206 collection->lasso_box = TRUE;
1207 draw_lasso_box(collection);
1210 static void draw_lasso_box(Collection *collection)
1212 GtkWidget *widget;
1213 int x, y, width, height;
1215 widget = GTK_WIDGET(collection);
1217 x = MIN(collection->drag_box_x[0], collection->drag_box_x[1]);
1218 y = MIN(collection->drag_box_y[0], collection->drag_box_y[1]);
1219 width = abs(collection->drag_box_x[1] - collection->drag_box_x[0]);
1220 height = abs(collection->drag_box_y[1] - collection->drag_box_y[0]);
1222 gdk_draw_rectangle(widget->window, collection->xor_gc, FALSE,
1223 x, y, width, height);
1226 static void remove_lasso_box(Collection *collection)
1228 g_return_if_fail(collection != NULL);
1229 g_return_if_fail(IS_COLLECTION(collection));
1230 g_return_if_fail(collection->lasso_box == TRUE);
1232 draw_lasso_box(collection);
1234 collection->lasso_box = FALSE;
1236 return;
1239 /* Convert a row,col address to an item number, or -1 if none */
1240 static int item_at_row_col(Collection *collection, int row, int col)
1242 int item;
1244 if (row < 0 || col < 0 || col >= collection->columns)
1245 return -1;
1247 item = col + row * collection->columns;
1249 if (item >= collection->number_of_items)
1250 return -1;
1251 return item;
1254 /* Unselect all items except number item (-1 to unselect everything) */
1255 static void collection_clear_except(Collection *collection, gint exception)
1257 GtkWidget *widget;
1258 GdkRectangle area;
1259 int item = 0;
1260 int scroll;
1261 int end; /* Selected items to end up with */
1263 widget = GTK_WIDGET(collection);
1264 scroll = collection->vadj->value;
1266 end = exception >= 0 && exception < collection->number_of_items
1267 ? collection->items[exception].selected != 0 : 0;
1269 area.width = collection->item_width;
1270 area.height = collection->item_height;
1272 if (collection->number_selected == 0)
1273 return;
1275 while (collection->number_selected > end)
1277 while (item == exception || !collection->items[item].selected)
1278 item++;
1280 area.x = (item % collection->columns) * area.width;
1281 area.y = (item / collection->columns) * area.height
1282 - scroll;
1284 collection->items[item++].selected = FALSE;
1285 gdk_window_clear_area(widget->window,
1286 area.x, area.y, area.width, area.height);
1287 collection_paint(collection, &area);
1289 collection->number_selected--;
1292 if (end == 0)
1293 gtk_signal_emit(GTK_OBJECT(collection),
1294 collection_signals[LOSE_SELECTION],
1295 current_event_time);
1298 /* Cancel the current wink effect. */
1299 void cancel_wink(Collection *collection)
1301 gint item;
1303 g_return_if_fail(collection != NULL);
1304 g_return_if_fail(IS_COLLECTION(collection));
1305 g_return_if_fail(collection->wink_item != -1);
1307 item = collection->wink_item;
1309 collection->wink_item = -1;
1310 gtk_timeout_remove(collection->wink_timeout);
1312 collection_draw_item(collection, item, TRUE);
1315 gboolean cancel_wink_timeout(Collection *collection)
1317 gint item;
1319 g_return_val_if_fail(collection != NULL, FALSE);
1320 g_return_val_if_fail(IS_COLLECTION(collection), FALSE);
1321 g_return_val_if_fail(collection->wink_item != -1, FALSE);
1323 item = collection->wink_item;
1325 collection->wink_item = -1;
1327 collection_draw_item(collection, item, TRUE);
1329 return FALSE;
1332 /* Functions for managing collections */
1334 /* Remove all objects from the collection */
1335 void collection_clear(Collection *collection)
1337 int prev_selected;
1339 g_return_if_fail(IS_COLLECTION(collection));
1341 if (collection->number_of_items == 0)
1342 return;
1344 if (collection->wink_item != -1)
1346 collection->wink_item = -1;
1347 gtk_timeout_remove(collection->wink_timeout);
1350 collection_set_cursor_item(collection, -1);
1351 prev_selected = collection->number_selected;
1352 collection->number_of_items = collection->number_selected = 0;
1354 resize_arrays(collection, MINIMUM_ITEMS);
1356 collection->paint_level = PAINT_CLEAR;
1358 gtk_widget_queue_clear(GTK_WIDGET(collection));
1360 if (prev_selected && collection->number_selected == 0)
1361 gtk_signal_emit(GTK_OBJECT(collection),
1362 collection_signals[LOSE_SELECTION],
1363 current_event_time);
1366 /* Inserts a new item at the end. The new item is unselected, and its
1367 * number is returned.
1369 gint collection_insert(Collection *collection, gpointer data)
1371 int item;
1373 g_return_val_if_fail(IS_COLLECTION(collection), -1);
1375 item = collection->number_of_items;
1377 if (item >= collection->array_size)
1378 resize_arrays(collection, item + (item >> 1));
1380 collection->items[item].data = data;
1381 collection->items[item].selected = FALSE;
1383 collection->number_of_items++;
1385 if (GTK_WIDGET_REALIZED(GTK_WIDGET(collection)))
1387 set_vadjustment(collection);
1388 collection_draw_item(collection,
1389 collection->number_of_items - 1,
1390 FALSE);
1393 return item;
1396 /* Unselect an item by number */
1397 void collection_unselect_item(Collection *collection, gint item)
1399 g_return_if_fail(collection != NULL);
1400 g_return_if_fail(IS_COLLECTION(collection));
1401 g_return_if_fail(item >= 0 && item < collection->number_of_items);
1403 if (collection->items[item].selected)
1405 collection->items[item].selected = FALSE;
1406 collection_draw_item(collection, item, TRUE);
1408 if (--collection->number_selected == 0)
1409 gtk_signal_emit(GTK_OBJECT(collection),
1410 collection_signals[LOSE_SELECTION],
1411 current_event_time);
1415 /* Select an item by number */
1416 void collection_select_item(Collection *collection, gint item)
1418 g_return_if_fail(collection != NULL);
1419 g_return_if_fail(IS_COLLECTION(collection));
1420 g_return_if_fail(item >= 0 && item < collection->number_of_items);
1422 if (collection->items[item].selected)
1423 return; /* Already selected */
1425 collection->items[item].selected = TRUE;
1426 collection_draw_item(collection, item, TRUE);
1428 if (collection->number_selected++ == 0)
1429 gtk_signal_emit(GTK_OBJECT(collection),
1430 collection_signals[GAIN_SELECTION],
1431 current_event_time);
1434 /* Toggle the selected state of an item (by number) */
1435 void collection_toggle_item(Collection *collection, gint item)
1437 g_return_if_fail(collection != NULL);
1438 g_return_if_fail(IS_COLLECTION(collection));
1439 g_return_if_fail(item >= 0 && item < collection->number_of_items);
1441 if (collection->items[item].selected)
1443 collection->items[item].selected = FALSE;
1444 if (--collection->number_selected == 0)
1445 gtk_signal_emit(GTK_OBJECT(collection),
1446 collection_signals[LOSE_SELECTION],
1447 current_event_time);
1449 else
1451 collection->items[item].selected = TRUE;
1452 if (collection->number_selected++ == 0)
1453 gtk_signal_emit(GTK_OBJECT(collection),
1454 collection_signals[GAIN_SELECTION],
1455 current_event_time);
1457 collection_draw_item(collection, item, TRUE);
1460 /* Select all items in the collection */
1461 void collection_select_all(Collection *collection)
1463 GtkWidget *widget;
1464 GdkRectangle area;
1465 int item = 0;
1466 int scroll;
1468 g_return_if_fail(collection != NULL);
1469 g_return_if_fail(IS_COLLECTION(collection));
1471 widget = GTK_WIDGET(collection);
1472 scroll = collection->vadj->value;
1474 area.width = collection->item_width;
1475 area.height = collection->item_height;
1477 if (collection->number_selected == collection->number_of_items)
1478 return; /* Nothing to do */
1480 while (collection->number_selected < collection->number_of_items)
1482 while (collection->items[item].selected)
1483 item++;
1485 area.x = (item % collection->columns) * area.width;
1486 area.y = (item / collection->columns) * area.height
1487 - scroll;
1489 collection->items[item++].selected = TRUE;
1490 gdk_window_clear_area(widget->window,
1491 area.x, area.y, area.width, area.height);
1492 collection_paint(collection, &area);
1494 collection->number_selected++;
1497 gtk_signal_emit(GTK_OBJECT(collection),
1498 collection_signals[GAIN_SELECTION],
1499 current_event_time);
1502 /* Unselect all items in the collection */
1503 void collection_clear_selection(Collection *collection)
1505 g_return_if_fail(collection != NULL);
1506 g_return_if_fail(IS_COLLECTION(collection));
1508 collection_clear_except(collection, -1);
1511 /* Force a redraw of the specified item, if it is visible */
1512 void collection_draw_item(Collection *collection, gint item, gboolean blank)
1514 int height;
1515 GdkRectangle area;
1516 GtkWidget *widget;
1517 int row, col;
1518 int scroll;
1519 int area_y, area_height; /* NOT shorts! */
1521 g_return_if_fail(collection != NULL);
1522 g_return_if_fail(IS_COLLECTION(collection));
1523 g_return_if_fail(item >= 0 && item < collection->number_of_items);
1525 widget = GTK_WIDGET(collection);
1527 col = item % collection->columns;
1528 row = item / collection->columns;
1529 scroll = collection->vadj->value; /* (round to int) */
1531 area.x = col * collection->item_width;
1532 area_y = row * collection->item_height - scroll;
1533 area.width = collection->item_width;
1534 area_height = collection->item_height;
1536 if (area_y + area_height < 0)
1537 return;
1539 gdk_window_get_size(widget->window, NULL, &height);
1541 if (area_y > height)
1542 return;
1544 area.y = area_y;
1545 area.height = area_height;
1547 if (blank)
1548 gdk_window_clear_area(widget->window,
1549 area.x, area.y, area.width, area.height);
1551 draw_one_item(collection, item, &area);
1554 void collection_set_item_size(Collection *collection, int width, int height)
1556 GtkWidget *widget;
1558 g_return_if_fail(collection != NULL);
1559 g_return_if_fail(IS_COLLECTION(collection));
1560 g_return_if_fail(width > 4 && height > 4);
1562 widget = GTK_WIDGET(collection);
1564 collection->item_width = width;
1565 collection->item_height = height;
1567 if (GTK_WIDGET_REALIZED(widget))
1569 int window_width;
1571 collection->paint_level = PAINT_CLEAR;
1572 gdk_window_get_size(widget->window, &window_width, NULL);
1573 collection->columns = MAX(window_width / collection->item_width,
1576 set_vadjustment(collection);
1577 gtk_widget_queue_draw(widget);
1581 void collection_qsort(Collection *collection,
1582 int (*compar)(const void *, const void *))
1584 g_return_if_fail(collection != NULL);
1585 g_return_if_fail(IS_COLLECTION(collection));
1586 g_return_if_fail(compar != NULL);
1588 if (collection->wink_item != -1)
1590 collection->wink_item = -1;
1591 gtk_timeout_remove(collection->wink_timeout);
1594 qsort(collection->items, collection->number_of_items,
1595 sizeof(collection->items[0]), compar);
1597 collection->paint_level = PAINT_CLEAR;
1599 gtk_widget_queue_draw(GTK_WIDGET(collection));
1602 /* Find an item in an unsorted collection.
1603 * Returns the item number, or -1 if not found.
1605 int collection_find_item(Collection *collection, gpointer data,
1606 int (*compar)(const void *, const void *))
1608 int i;
1610 g_return_val_if_fail(collection != NULL, -1);
1611 g_return_val_if_fail(IS_COLLECTION(collection), -1);
1612 g_return_val_if_fail(compar != NULL, -1);
1614 for (i = 0; i < collection->number_of_items; i++)
1615 if (compar(&collection->items[i].data, &data) == 0)
1616 return i;
1618 return -1;
1621 /* The collection may be in either normal mode or panel mode.
1622 * In panel mode:
1623 * - a single click calls open_item
1624 * - items are never selected
1625 * - lasso boxes are disabled
1627 void collection_set_panel(Collection *collection, gboolean panel)
1629 g_return_if_fail(collection != NULL);
1630 g_return_if_fail(IS_COLLECTION(collection));
1632 collection->panel = panel == TRUE;
1634 if (collection->panel)
1636 collection_clear_selection(collection);
1637 if (collection->lasso_box)
1638 remove_lasso_box(collection);
1642 /* Return the number of the item under the point (x,y), or -1 for none.
1643 * This may call your test_point callback. The point is relative to the
1644 * collection's origin.
1646 int collection_get_item(Collection *collection, int x, int y)
1648 int scroll;
1649 int row, col;
1650 int item;
1652 g_return_val_if_fail(collection != NULL, -1);
1654 scroll = collection->vadj->value;
1655 col = x / collection->item_width;
1656 row = (y + scroll) / collection->item_height;
1658 if (col < 0 || row < 0 || col >= collection->columns)
1659 return -1;
1661 item = col + row * collection->columns;
1662 if (item >= collection->number_of_items
1664 !collection->test_point(collection,
1665 x - col * collection->item_width,
1666 y - row * collection->item_height
1667 + scroll,
1668 &collection->items[item],
1669 collection->item_width,
1670 collection->item_height))
1672 return -1;
1675 return item;
1678 /* Set the cursor/highlight over the given item. Passing -1
1679 * hides the cursor.
1681 void collection_set_cursor_item(Collection *collection, gint item)
1683 int old_item;
1685 g_return_if_fail(collection != NULL);
1686 g_return_if_fail(IS_COLLECTION(collection));
1687 g_return_if_fail(item >= -1 &&
1688 item < (int) collection->number_of_items);
1690 old_item = collection->cursor_item;
1692 if (old_item == item)
1693 return;
1695 collection->cursor_item = item;
1697 if (old_item != -1)
1698 collection_draw_item(collection, old_item, TRUE);
1699 if (item != -1)
1700 collection_draw_item(collection, item, TRUE);
1703 /* Briefly highlight an item to draw the user's attention to it.
1704 * -1 cancels the effect, as does deleting items, sorting the collection
1705 * or starting a new wink effect.
1706 * Otherwise, the effect will cancel itself after a short pause.
1707 * */
1708 void collection_wink_item(Collection *collection, gint item)
1710 g_return_if_fail(collection != NULL);
1711 g_return_if_fail(IS_COLLECTION(collection));
1712 g_return_if_fail(item >= -1 && item < collection->number_of_items);
1714 if (collection->wink_item != -1)
1715 cancel_wink(collection);
1716 if (item == -1)
1717 return;
1719 collection->wink_item = item;
1720 collection->wink_timeout = gtk_timeout_add(200,
1721 (GtkFunction) cancel_wink_timeout,
1722 collection);
1723 collection_draw_item(collection, item, TRUE);
1725 gdk_flush();
1728 /* Call test(item, data) on each item in the collection.
1729 * Remove all items for which it returns TRUE. test() should
1730 * free the data before returning TRUE. The collection is in an
1731 * inconsistant state during this call (ie, when test() is called).
1733 void collection_delete_if(Collection *collection,
1734 gboolean (*test)(gpointer item, gpointer data),
1735 gpointer data)
1737 int in, out = 0;
1738 int selected = 0;
1740 g_return_if_fail(collection != NULL);
1741 g_return_if_fail(IS_COLLECTION(collection));
1742 g_return_if_fail(test != NULL);
1744 for (in = 0; in < collection->number_of_items; in++)
1746 if (!test(collection->items[in].data, data))
1748 if (collection->items[in].selected)
1750 collection->items[out].selected = TRUE;
1751 selected++;
1753 else
1754 collection->items[out].selected = FALSE;
1756 collection->items[out++].data =
1757 collection->items[in].data;
1761 if (in != out)
1763 if (collection->wink_item != -1)
1765 collection->wink_item = -1;
1766 gtk_timeout_remove(collection->wink_timeout);
1769 collection->number_of_items = out;
1770 collection->number_selected = selected;
1771 resize_arrays(collection,
1772 MAX(collection->number_of_items, MINIMUM_ITEMS));
1774 collection->paint_level = PAINT_CLEAR;
1776 if (GTK_WIDGET_REALIZED(GTK_WIDGET(collection)))
1778 set_vadjustment(collection);
1779 gtk_widget_queue_draw(GTK_WIDGET(collection));