2 * ROX-Filer, filer for the ROX desktop project
3 * Copyright (C) 2006, Thomas Leonard and others (see changelog for details).
5 * The collection widget provides an area for displaying a collection of
6 * objects (such as files). It allows the user to choose a selection of
7 * them and provides signals to allow popping up menus, detecting
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by the Free
12 * Software Foundation; either version 2 of the License, or (at your option)
15 * This program is distributed in the hope that it will be useful, but WITHOUT
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
20 * You should have received a copy of the GNU General Public License along with
21 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
22 * Place, Suite 330, Boston, MA 02111-1307 USA
30 #include <gdk/gdkkeysyms.h>
33 #include "collection.h"
37 #define MINIMUM_ITEMS 16
39 #define MAX_WINKS 5 /* Should be an odd number */
41 /* Macro to emit the "selection_changed" signal only if allowed */
42 #define EMIT_SELECTION_CHANGED(collection, time) \
43 if (!collection->block_selection_changed) \
44 g_signal_emit(collection, \
45 collection_signals[SELECTION_CHANGED], 0, time)
55 * void gain_selection(collection, time, user_data)
56 * We've gone from no selected items to having a selection.
57 * Time is the time of the event that caused the change, or
58 * GDK_CURRENT_TIME if not known.
60 * void lose_selection(collection, time, user_data)
61 * We've dropped to having no selected items.
62 * Time is the time of the event that caused the change, or
63 * GDK_CURRENT_TIME if not known.
65 * void selection_changed(collection, user_data)
66 * The set of selected items has changed.
67 * Time is the time of the event that caused the change, or
68 * GDK_CURRENT_TIME if not known.
78 static guint collection_signals
[LAST_SIGNAL
] = { 0 };
80 static guint32 current_event_time
= GDK_CURRENT_TIME
;
82 static GtkWidgetClass
*parent_class
= NULL
;
84 /* Static prototypes */
85 static void draw_one_item(Collection
*collection
,
88 static void collection_class_init(GObjectClass
*gclass
, gpointer data
);
89 static void collection_init(GTypeInstance
*object
, gpointer g_class
);
90 static void collection_destroy(GtkObject
*object
);
91 static void collection_finalize(GObject
*object
);
92 static void collection_realize(GtkWidget
*widget
);
93 static void collection_map(GtkWidget
*widget
);
94 static void collection_size_request(GtkWidget
*widget
,
95 GtkRequisition
*requisition
);
96 static void collection_size_allocate(GtkWidget
*widget
,
97 GtkAllocation
*allocation
);
98 static void collection_set_adjustment(Collection
*collection
,
100 static void collection_get_property(GObject
*object
,
104 static void collection_set_property(GObject
*object
,
108 static gint
collection_expose(GtkWidget
*widget
, GdkEventExpose
*event
);
109 static void default_draw_item(GtkWidget
*widget
,
110 CollectionItem
*data
,
113 static gboolean
default_test_point(Collection
*collection
,
114 int point_x
, int point_y
,
115 CollectionItem
*data
,
116 int width
, int height
,
118 static gint
collection_motion_notify(GtkWidget
*widget
,
119 GdkEventMotion
*event
);
120 static void add_lasso_box(Collection
*collection
);
121 static void abort_lasso(Collection
*collection
);
122 static void remove_lasso_box(Collection
*collection
);
123 static void draw_lasso_box(Collection
*collection
);
124 static void cancel_wink(Collection
*collection
);
125 static gint
collection_key_press(GtkWidget
*widget
, GdkEventKey
*event
);
126 static void get_visible_limits(Collection
*collection
, int *first
, int *last
);
127 static void scroll_to_show(Collection
*collection
, int item
);
128 static void collection_item_set_selected(Collection
*collection
,
132 static gint
collection_scroll_event(GtkWidget
*widget
, GdkEventScroll
*event
);
133 static int collection_get_rows(const Collection
*collection
);
134 static int collection_get_cols(const Collection
*collection
);
137 /* The number of rows, at least 1. */
138 static inline int collection_get_rows(const Collection
*collection
)
140 int rows
= (collection
->number_of_items
+ collection
->columns
- 1) /
145 /* The number of columns _actually_ displayed, at least 1. This
146 * function is required in vertical_order layout-based manipulation
147 * such as moving the cursor to detect the last column. */
148 static inline int collection_get_cols(const Collection
*collection
)
150 if (collection
->vertical_order
)
152 int rows
= collection_get_rows(collection
);
153 int cols
= (collection
->number_of_items
+ rows
- 1) / rows
;
157 return collection
->columns
;
160 static void draw_focus_at(Collection
*collection
, GdkRectangle
*area
)
165 widget
= GTK_WIDGET(collection
);
167 if (GTK_WIDGET_FLAGS(widget
) & GTK_HAS_FOCUS
)
168 state
= GTK_STATE_ACTIVE
;
170 state
= GTK_STATE_INSENSITIVE
;
172 gtk_paint_focus(widget
->style
,
179 collection
->item_width
,
183 static void draw_one_item(Collection
*collection
, int item
, GdkRectangle
*area
)
185 if (item
< collection
->number_of_items
)
187 collection
->draw_item((GtkWidget
*) collection
,
188 &collection
->items
[item
],
189 area
, collection
->cb_user_data
);
192 if (item
== collection
->cursor_item
)
193 draw_focus_at(collection
, area
);
196 GType
collection_get_type(void)
198 static GType my_type
= 0;
202 static const GTypeInfo info
=
204 sizeof(CollectionClass
),
205 NULL
, /* base_init */
206 NULL
, /* base_finalise */
207 (GClassInitFunc
) collection_class_init
,
208 NULL
, /* class_finalise */
209 NULL
, /* class_data */
215 my_type
= g_type_register_static(gtk_widget_get_type(),
216 "Collection", &info
, 0);
222 typedef void (*FinalizeFn
)(GObject
*object
);
224 static void collection_class_init(GObjectClass
*gclass
, gpointer data
)
226 CollectionClass
*collection_class
= (CollectionClass
*) gclass
;
227 GtkObjectClass
*object_class
= (GtkObjectClass
*) gclass
;
228 GtkWidgetClass
*widget_class
= (GtkWidgetClass
*) gclass
;
230 parent_class
= gtk_type_class(gtk_widget_get_type());
232 object_class
->destroy
= collection_destroy
;
233 G_OBJECT_CLASS(object_class
)->finalize
=
234 (FinalizeFn
) collection_finalize
;
236 widget_class
->realize
= collection_realize
;
237 widget_class
->expose_event
= collection_expose
;
238 widget_class
->size_request
= collection_size_request
;
239 widget_class
->size_allocate
= collection_size_allocate
;
241 widget_class
->key_press_event
= collection_key_press
;
243 widget_class
->motion_notify_event
= collection_motion_notify
;
244 widget_class
->map
= collection_map
;
245 widget_class
->scroll_event
= collection_scroll_event
;
247 gclass
->set_property
= collection_set_property
;
248 gclass
->get_property
= collection_get_property
;
250 collection_class
->gain_selection
= NULL
;
251 collection_class
->lose_selection
= NULL
;
252 collection_class
->selection_changed
= NULL
;
254 collection_signals
[GAIN_SELECTION
] = g_signal_new("gain_selection",
255 G_TYPE_FROM_CLASS(gclass
),
257 G_STRUCT_OFFSET(CollectionClass
,
260 g_cclosure_marshal_VOID__INT
,
264 collection_signals
[LOSE_SELECTION
] = g_signal_new("lose_selection",
265 G_TYPE_FROM_CLASS(gclass
),
267 G_STRUCT_OFFSET(CollectionClass
,
270 g_cclosure_marshal_VOID__INT
,
274 collection_signals
[SELECTION_CHANGED
] = g_signal_new(
276 G_TYPE_FROM_CLASS(gclass
),
278 G_STRUCT_OFFSET(CollectionClass
,
281 g_cclosure_marshal_VOID__INT
,
285 g_object_class_install_property(gclass
,
287 g_param_spec_object("vadjustment",
288 "Vertical Adjustment",
289 "The GtkAdjustment for the vertical position.",
291 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT
));
294 static void collection_init(GTypeInstance
*instance
, gpointer g_class
)
296 Collection
*object
= (Collection
*) instance
;
298 g_return_if_fail(object
!= NULL
);
299 g_return_if_fail(IS_COLLECTION(object
));
301 GTK_WIDGET_SET_FLAGS(GTK_WIDGET(object
), GTK_CAN_FOCUS
);
303 object
->number_of_items
= 0;
304 object
->number_selected
= 0;
305 object
->block_selection_changed
= 0;
307 object
->vertical_order
= FALSE
;
308 object
->item_width
= 64;
309 object
->item_height
= 64;
312 object
->items
= g_new(CollectionItem
, MINIMUM_ITEMS
);
313 object
->cursor_item
= -1;
314 object
->cursor_item_old
= -1;
315 object
->wink_item
= -1;
316 object
->wink_on_map
= -1;
317 object
->array_size
= MINIMUM_ITEMS
;
318 object
->draw_item
= default_draw_item
;
319 object
->test_point
= default_test_point
;
320 object
->free_item
= NULL
;
323 GtkWidget
* collection_new(void)
325 return GTK_WIDGET(gtk_widget_new(collection_get_type(), NULL
));
328 /* After this we are unusable, but our data (if any) is still hanging around.
329 * It will be freed later with finalize.
331 static void collection_destroy(GtkObject
*object
)
333 Collection
*collection
;
335 g_return_if_fail(object
!= NULL
);
336 g_return_if_fail(IS_COLLECTION(object
));
338 collection
= COLLECTION(object
);
340 collection_clear(collection
);
342 if (collection
->vadj
)
344 g_object_unref(G_OBJECT(collection
->vadj
));
345 collection
->vadj
= NULL
;
348 if (GTK_OBJECT_CLASS(parent_class
)->destroy
)
349 (*GTK_OBJECT_CLASS(parent_class
)->destroy
)(object
);
352 /* This is the last thing that happens to us. Free all data. */
353 static void collection_finalize(GObject
*object
)
355 Collection
*collection
;
357 collection
= COLLECTION(object
);
359 g_return_if_fail(collection
->number_of_items
== 0);
361 g_free(collection
->items
);
363 if (G_OBJECT_CLASS(parent_class
)->finalize
)
364 G_OBJECT_CLASS(parent_class
)->finalize(object
);
367 static void collection_map(GtkWidget
*widget
)
369 Collection
*collection
= COLLECTION(widget
);
371 if (GTK_WIDGET_CLASS(parent_class
)->map
)
372 (*GTK_WIDGET_CLASS(parent_class
)->map
)(widget
);
374 if (collection
->wink_on_map
>= 0)
376 collection_wink_item(collection
, collection
->wink_on_map
);
377 collection
->wink_on_map
= -1;
381 static void collection_realize(GtkWidget
*widget
)
383 Collection
*collection
;
384 GdkWindowAttr attributes
;
385 gint attributes_mask
;
386 GdkGCValues xor_values
;
389 g_return_if_fail(widget
!= NULL
);
390 g_return_if_fail(IS_COLLECTION(widget
));
391 g_return_if_fail(widget
->parent
!= NULL
);
393 GTK_WIDGET_SET_FLAGS(widget
, GTK_REALIZED
);
394 collection
= COLLECTION(widget
);
396 attributes
.x
= widget
->allocation
.x
;
397 attributes
.y
= widget
->allocation
.y
;
398 attributes
.width
= widget
->allocation
.width
;
399 attributes
.height
= widget
->allocation
.height
;
400 attributes
.wclass
= GDK_INPUT_OUTPUT
;
401 attributes
.window_type
= GDK_WINDOW_CHILD
;
402 attributes
.event_mask
= gtk_widget_get_events(widget
) |
404 GDK_BUTTON_PRESS_MASK
| GDK_BUTTON_RELEASE_MASK
|
405 GDK_BUTTON1_MOTION_MASK
| GDK_BUTTON2_MOTION_MASK
|
406 GDK_BUTTON3_MOTION_MASK
;
407 attributes
.visual
= gtk_widget_get_visual(widget
);
408 attributes
.colormap
= gtk_widget_get_colormap(widget
);
410 attributes_mask
= GDK_WA_X
| GDK_WA_Y
|
411 GDK_WA_VISUAL
| GDK_WA_COLORMAP
;
412 widget
->window
= gdk_window_new(gtk_widget_get_parent_window(widget
),
413 &attributes
, attributes_mask
);
415 widget
->style
= gtk_style_attach(widget
->style
, widget
->window
);
417 gdk_window_set_user_data(widget
->window
, widget
);
418 gdk_window_set_background(widget
->window
,
419 &widget
->style
->base
[GTK_STATE_NORMAL
]);
421 bg
= &widget
->style
->base
[GTK_STATE_NORMAL
];
422 fg
= &widget
->style
->text
[GTK_STATE_NORMAL
];
423 xor_values
.function
= GDK_XOR
;
424 xor_values
.foreground
.pixel
= fg
->pixel
^ bg
->pixel
;
425 collection
->xor_gc
= gdk_gc_new_with_values(widget
->window
,
431 static void collection_size_request(GtkWidget
*widget
,
432 GtkRequisition
*requisition
)
434 Collection
*collection
= COLLECTION(widget
);
437 /* We ask for the total size we need; our containing viewport
438 * will deal with scrolling.
440 requisition
->width
= MIN_WIDTH
;
441 rows
= collection_get_rows(collection
);
442 requisition
->height
= rows
* collection
->item_height
;
445 static gboolean
scroll_after_alloc(Collection
*collection
)
447 if (collection
->wink_item
!= -1)
448 scroll_to_show(collection
, collection
->wink_item
);
449 else if (collection
->cursor_item
!= -1)
450 scroll_to_show(collection
, collection
->cursor_item
);
451 g_object_unref(G_OBJECT(collection
));
456 static void collection_size_allocate(GtkWidget
*widget
,
457 GtkAllocation
*allocation
)
459 Collection
*collection
;
461 gboolean cursor_visible
= FALSE
;
463 g_return_if_fail(widget
!= NULL
);
464 g_return_if_fail(IS_COLLECTION(widget
));
465 g_return_if_fail(allocation
!= NULL
);
467 collection
= COLLECTION(widget
);
469 if (collection
->cursor_item
!= -1)
474 collection_item_to_rowcol(collection
, collection
->cursor_item
,
477 get_visible_limits(collection
, &first
, &last
);
479 cursor_visible
= crow
>= first
&& crow
<= last
;
482 old_columns
= collection
->columns
;
484 widget
->allocation
= *allocation
;
486 collection
->columns
= allocation
->width
/ collection
->item_width
;
487 if (collection
->columns
< 1)
488 collection
->columns
= 1;
490 if (GTK_WIDGET_REALIZED(widget
))
492 gdk_window_move_resize(widget
->window
,
493 allocation
->x
, allocation
->y
,
494 allocation
->width
, allocation
->height
);
497 scroll_to_show(collection
, collection
->cursor_item
);
500 if (old_columns
!= collection
->columns
)
502 /* Need to go around again... */
503 gtk_widget_queue_resize(widget
);
505 else if (collection
->wink_item
!= -1 || collection
->cursor_item
!= -1)
507 /* Viewport resets the adjustments after the alloc */
508 g_object_ref(G_OBJECT(collection
));
509 g_idle_add((GSourceFunc
) scroll_after_alloc
, collection
);
513 /* Return the area occupied by the item at (row, col) by filling
516 static void collection_get_item_area(Collection
*collection
,
521 area
->x
= col
* collection
->item_width
;
522 area
->y
= row
* collection
->item_height
;
524 area
->width
= collection
->item_width
;
525 area
->height
= collection
->item_height
;
526 if (col
== collection
->columns
- 1)
530 static gint
collection_expose(GtkWidget
*widget
, GdkEventExpose
*event
)
532 Collection
*collection
;
533 GdkRectangle item_area
;
536 int start_row
, last_row
;
537 int start_col
, last_col
;
540 g_return_val_if_fail(widget
!= NULL
, FALSE
);
541 g_return_val_if_fail(IS_COLLECTION(widget
), FALSE
);
542 g_return_val_if_fail(event
!= NULL
, FALSE
);
544 /* Note about 'detail' argument:
545 * - If set to "base", lighthouse theme will crash
546 * - If set to NULL, cleanice theme will crash
548 * !!! We don't need to clear the background. GTK does it for us.
550 gtk_paint_flat_box(widget->style, widget->window, GTK_STATE_NORMAL,
551 GTK_SHADOW_NONE, &event->area,
552 widget, "collection", 0, 0, -1, -1);
555 collection
= COLLECTION(widget
);
557 /* Calculate the ranges to plot */
558 start_row
= event
->area
.y
/ collection
->item_height
;
559 last_row
= (event
->area
.y
+ event
->area
.height
- 1)
560 / collection
->item_height
;
562 if (last_row
>= collection_get_rows(collection
))
563 last_row
= collection_get_rows(collection
) - 1;
565 start_col
= event
->area
.x
/ collection
->item_width
;
566 phys_last_col
= (event
->area
.x
+ event
->area
.width
- 1)
567 / collection
->item_width
;
569 /* The right-most column may be wider than the others.
570 * Therefore, to redraw the area after the last 'real' column
571 * we may have to draw the right-most column.
573 if (start_col
>= collection
->columns
)
574 start_col
= collection
->columns
- 1;
576 if (phys_last_col
>= collection
->columns
)
577 last_col
= collection
->columns
- 1;
579 last_col
= phys_last_col
;
582 for(row
= start_row
; row
<= last_row
; row
++)
583 for(col
= start_col
; col
<= last_col
; col
++)
585 item
= collection_rowcol_to_item(collection
, row
, col
);
586 if (item
== 0 || item
< collection
->number_of_items
) {
587 collection_get_item_area(collection
,
588 row
, col
, &item_area
);
589 draw_one_item(collection
, item
, &item_area
);
593 if (collection
->lasso_box
)
594 draw_lasso_box(collection
);
599 static void default_draw_item(GtkWidget
*widget
,
600 CollectionItem
*item
,
604 gdk_draw_arc(widget
->window
,
605 item
->selected
? widget
->style
->white_gc
606 : widget
->style
->black_gc
,
609 COLLECTION(widget
)->item_width
, area
->height
,
614 static gboolean
default_test_point(Collection
*collection
,
615 int point_x
, int point_y
,
616 CollectionItem
*item
,
617 int width
, int height
,
622 /* Convert to point in unit circle */
623 f_x
= ((float) point_x
/ width
) - 0.5;
624 f_y
= ((float) point_y
/ height
) - 0.5;
626 return (f_x
* f_x
) + (f_y
* f_y
) <= .25;
629 static void collection_set_property(GObject
*object
,
634 Collection
*collection
;
636 collection
= COLLECTION(object
);
640 case PROP_VADJUSTMENT
:
641 collection_set_adjustment(collection
,
642 g_value_get_object(value
));
645 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
,
651 static void collection_set_adjustment(Collection
*collection
,
655 g_return_if_fail(GTK_IS_ADJUSTMENT(vadj
));
657 vadj
= GTK_ADJUSTMENT(gtk_adjustment_new(0.0,
661 if (collection
->vadj
== vadj
)
664 if (collection
->vadj
)
665 g_object_unref(G_OBJECT(collection
->vadj
));
667 collection
->vadj
= vadj
;
668 g_object_ref(G_OBJECT(collection
->vadj
));
669 gtk_object_sink(GTK_OBJECT(collection
->vadj
));
672 static void collection_get_property(GObject
*object
,
677 Collection
*collection
;
679 collection
= COLLECTION(object
);
683 case PROP_VADJUSTMENT
:
684 g_value_set_object(value
, G_OBJECT(collection
->vadj
));
687 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
,
693 static void resize_arrays(Collection
*collection
, guint new_size
)
695 g_return_if_fail(collection
!= NULL
);
696 g_return_if_fail(IS_COLLECTION(collection
));
697 g_return_if_fail(new_size
>= collection
->number_of_items
);
699 collection
->items
= g_realloc(collection
->items
,
700 sizeof(CollectionItem
) * new_size
);
701 collection
->array_size
= new_size
;
704 static gint
collection_key_press(GtkWidget
*widget
, GdkEventKey
*event
)
706 Collection
*collection
;
710 g_return_val_if_fail(widget
!= NULL
, FALSE
);
711 g_return_val_if_fail(IS_COLLECTION(widget
), FALSE
);
712 g_return_val_if_fail(event
!= NULL
, FALSE
);
714 collection
= (Collection
*) widget
;
715 item
= collection
->cursor_item
;
718 if (event
->state
& (GDK_CONTROL_MASK
| GDK_SHIFT_MASK
))
720 if (key
== GDK_Left
|| key
== GDK_Right
|| \
721 key
== GDK_Up
|| key
== GDK_Down
)
729 collection_move_cursor(collection
, 0, -1);
732 collection_move_cursor(collection
, 0, 1);
735 collection_move_cursor(collection
, -1, 0);
738 collection_move_cursor(collection
, 1, 0);
741 collection_set_cursor_item(collection
, 0, TRUE
);
744 collection_set_cursor_item(collection
,
745 MAX((gint
) collection
->number_of_items
- 1, 0),
751 get_visible_limits(collection
, &first
, &last
);
752 collection_move_cursor(collection
, first
- last
- 1, 0);
758 get_visible_limits(collection
, &first
, &last
);
759 collection_move_cursor(collection
, last
- first
+ 1, 0);
769 /* Wheel mouse scrolling */
770 static gint
collection_scroll_event(GtkWidget
*widget
, GdkEventScroll
*event
)
772 Collection
*collection
;
775 g_return_val_if_fail(widget
!= NULL
, FALSE
);
776 g_return_val_if_fail(IS_COLLECTION(widget
), FALSE
);
777 g_return_val_if_fail(event
!= NULL
, FALSE
);
779 collection
= COLLECTION(widget
);
781 if (event
->direction
== GDK_SCROLL_UP
)
783 else if (event
->direction
== GDK_SCROLL_DOWN
)
790 int old_value
= collection
->vadj
->value
;
792 gboolean box
= collection
->lasso_box
;
793 int step
= collection
->vadj
->page_increment
/ 2;
795 new_value
= CLAMP(old_value
+ diff
* step
, 0.0,
796 collection
->vadj
->upper
797 - collection
->vadj
->page_size
);
798 diff
= new_value
- old_value
;
803 remove_lasso_box(collection
);
804 collection
->drag_box_y
[0] -= diff
;
806 gtk_adjustment_set_value(collection
->vadj
, new_value
);
808 add_lasso_box(collection
);
815 /* 'from' and 'to' are pixel positions. 'step' is the size of each item.
816 * Returns the index of the first item covered, and the number of items.
818 static void get_range(int from
, int to
, int step
, gint
*pos
, gint
*len
)
820 int margin
= MIN(step
/ 4, 40);
829 from
= (from
+ margin
) / step
; /* First item */
830 to
= (to
+ step
- margin
) / step
; /* Last item (inclusive) */
836 /* Fills in the area with a rectangle corresponding to the current
837 * size of the lasso box (units of items, not pixels).
839 * The box will only span valid columns, but the total number
840 * of items is not taken into account (rows or cols).
842 static void find_lasso_area(Collection
*collection
, GdkRectangle
*area
)
844 int cols
= collection
->columns
;
845 int dx
= collection
->drag_box_x
[0] - collection
->drag_box_x
[1];
846 int dy
= collection
->drag_box_y
[0] - collection
->drag_box_y
[1];
848 if (ABS(dx
) < 8 && ABS(dy
) < 8)
850 /* Didn't move far enough - ignore */
851 area
->x
= area
->y
= 0;
857 get_range(collection
->drag_box_x
[0], collection
->drag_box_x
[1],
858 collection
->item_width
, &area
->x
, &area
->width
);
862 else if (area
->x
+ area
->width
> cols
)
863 area
->width
= cols
- area
->x
;
865 get_range(collection
->drag_box_y
[0], collection
->drag_box_y
[1],
866 collection
->item_height
, &area
->y
, &area
->height
);
869 static void collection_process_area(Collection
*collection
,
875 int rows
= collection_get_rows(collection
);
876 int cols
= collection
->columns
;
877 guint32 stacked_time
;
879 gboolean changed
= FALSE
;
882 g_return_if_fail(fn
== GDK_SET
|| fn
== GDK_INVERT
);
884 old_selected
= collection
->number_selected
;
886 stacked_time
= current_event_time
;
887 current_event_time
= time
;
889 collection
->block_selection_changed
++;
891 for (y
= area
->y
; y
< area
->y
+ area
->height
&& y
< rows
; y
++)
892 for (x
= area
->x
; x
< area
->x
+ area
->width
&& x
< cols
; x
++)
894 item
= collection_rowcol_to_item(collection
, y
, x
);
895 if (item
< collection
->number_of_items
) {
896 if (fn
== GDK_INVERT
)
897 collection_item_set_selected(
899 !collection
-> items
[item
].selected
,
902 collection_item_set_selected(
903 collection
, item
, TRUE
, FALSE
);
909 if (collection
->number_selected
&& !old_selected
)
910 g_signal_emit(collection
,
911 collection_signals
[GAIN_SELECTION
], 0,
913 else if (!collection
->number_selected
&& old_selected
)
914 g_signal_emit(collection
,
915 collection_signals
[LOSE_SELECTION
], 0,
918 collection_unblock_selection_changed(collection
,
919 current_event_time
, changed
);
920 current_event_time
= stacked_time
;
923 static gint
collection_motion_notify(GtkWidget
*widget
,
924 GdkEventMotion
*event
)
926 Collection
*collection
;
929 g_return_val_if_fail(widget
!= NULL
, FALSE
);
930 g_return_val_if_fail(IS_COLLECTION(widget
), FALSE
);
931 g_return_val_if_fail(event
!= NULL
, FALSE
);
933 collection
= COLLECTION(widget
);
935 if (!collection
->lasso_box
)
938 if (event
->window
!= widget
->window
)
939 gdk_window_get_pointer(widget
->window
, &x
, &y
, NULL
);
946 remove_lasso_box(collection
);
947 collection
->drag_box_x
[1] = x
;
948 collection
->drag_box_y
[1] = y
;
949 add_lasso_box(collection
);
953 static void add_lasso_box(Collection
*collection
)
955 g_return_if_fail(collection
!= NULL
);
956 g_return_if_fail(IS_COLLECTION(collection
));
957 g_return_if_fail(collection
->lasso_box
== FALSE
);
959 collection
->lasso_box
= TRUE
;
960 draw_lasso_box(collection
);
963 static void draw_lasso_box(Collection
*collection
)
966 int x
, y
, width
, height
;
968 widget
= GTK_WIDGET(collection
);
970 x
= MIN(collection
->drag_box_x
[0], collection
->drag_box_x
[1]);
971 y
= MIN(collection
->drag_box_y
[0], collection
->drag_box_y
[1]);
972 width
= abs(collection
->drag_box_x
[1] - collection
->drag_box_x
[0]);
973 height
= abs(collection
->drag_box_y
[1] - collection
->drag_box_y
[0]);
975 /* XXX: A redraw bug sometimes leaves a one-pixel dot on the screen.
976 * As a quick hack, don't draw boxes that small for now...
979 gdk_draw_rectangle(widget
->window
, collection
->xor_gc
, FALSE
,
980 x
, y
, width
, height
);
983 static void abort_lasso(Collection
*collection
)
985 if (collection
->lasso_box
)
986 remove_lasso_box(collection
);
989 static void remove_lasso_box(Collection
*collection
)
991 g_return_if_fail(collection
!= NULL
);
992 g_return_if_fail(IS_COLLECTION(collection
));
993 g_return_if_fail(collection
->lasso_box
== TRUE
);
995 draw_lasso_box(collection
);
997 collection
->lasso_box
= FALSE
;
1002 /* Make sure that 'item' is fully visible (vertically), scrolling if not. */
1003 static void scroll_to_show(Collection
*collection
, int item
)
1005 int first
, last
, row
, col
;
1007 g_return_if_fail(collection
!= NULL
);
1008 g_return_if_fail(IS_COLLECTION(collection
));
1010 collection_item_to_rowcol(collection
, item
, &row
, &col
);
1011 get_visible_limits(collection
, &first
, &last
);
1015 gtk_adjustment_set_value(collection
->vadj
,
1016 row
* collection
->item_height
);
1018 else if (row
>= last
)
1020 GtkWidget
*widget
= (GtkWidget
*) collection
;
1023 if (GTK_WIDGET_REALIZED(widget
))
1025 height
= collection
->vadj
->page_size
;
1026 gtk_adjustment_set_value(collection
->vadj
,
1027 (row
+ 1) * collection
->item_height
- height
);
1032 /* Return the first and last rows which are [partly] visible. Does not
1033 * ensure that the rows actually exist (contain items).
1035 static void get_visible_limits(Collection
*collection
, int *first
, int *last
)
1037 GtkWidget
*widget
= (GtkWidget
*) collection
;
1038 gint scroll
= 0, height
;
1040 g_return_if_fail(collection
!= NULL
);
1041 g_return_if_fail(IS_COLLECTION(collection
));
1042 g_return_if_fail(first
!= NULL
&& last
!= NULL
);
1044 if (!GTK_WIDGET_REALIZED(widget
))
1051 scroll
= collection
->vadj
->value
;
1052 height
= collection
->vadj
->page_size
;
1054 *first
= MAX(scroll
/ collection
->item_height
, 0);
1055 *last
= (scroll
+ height
- 1) /collection
->item_height
;
1062 /* Cancel the current wink effect. */
1063 static void cancel_wink(Collection
*collection
)
1067 g_return_if_fail(collection
!= NULL
);
1068 g_return_if_fail(IS_COLLECTION(collection
));
1069 g_return_if_fail(collection
->wink_item
!= -1);
1071 item
= collection
->wink_item
;
1073 collection
->wink_item
= -1;
1074 g_source_remove(collection
->wink_timeout
);
1076 collection_draw_item(collection
, item
, TRUE
);
1079 /* Draw/undraw a box around collection->wink_item */
1080 static void invert_wink(Collection
*collection
)
1085 g_return_if_fail(collection
->wink_item
>= 0);
1087 if (!GTK_WIDGET_REALIZED(GTK_WIDGET(collection
)))
1090 collection_item_to_rowcol(collection
, collection
->wink_item
,
1092 collection_get_item_area(collection
, row
, col
, &area
);
1094 gdk_draw_rectangle(((GtkWidget
*) collection
)->window
,
1095 collection
->xor_gc
, FALSE
,
1097 collection
->item_width
- 1,
1101 static gboolean
wink_timeout(Collection
*collection
)
1105 g_return_val_if_fail(collection
!= NULL
, FALSE
);
1106 g_return_val_if_fail(IS_COLLECTION(collection
), FALSE
);
1107 g_return_val_if_fail(collection
->wink_item
!= -1, FALSE
);
1109 item
= collection
->wink_item
;
1111 if (collection
->winks_left
-- > 0)
1113 invert_wink(collection
);
1117 collection
->wink_item
= -1;
1119 collection_draw_item(collection
, item
, TRUE
);
1124 /* Change the selected state of an item.
1125 * Send GAIN/LOSE signals if 'signal' is TRUE.
1126 * Send SELECTION_CHANGED unless blocked.
1127 * Updates number_selected and redraws the item.
1129 static void collection_item_set_selected(Collection
*collection
,
1134 g_return_if_fail(collection
!= NULL
);
1135 g_return_if_fail(IS_COLLECTION(collection
));
1136 g_return_if_fail(item
>= 0 && item
< collection
->number_of_items
);
1138 if (collection
->items
[item
].selected
== selected
)
1141 collection
->items
[item
].selected
= selected
;
1142 collection_draw_item(collection
, item
, TRUE
);
1146 collection
->number_selected
++;
1147 if (signal
&& collection
->number_selected
== 1)
1148 g_signal_emit(collection
,
1149 collection_signals
[GAIN_SELECTION
], 0,
1150 current_event_time
);
1154 collection
->number_selected
--;
1155 if (signal
&& collection
->number_selected
== 0)
1156 g_signal_emit(collection
,
1157 collection_signals
[LOSE_SELECTION
], 0,
1158 current_event_time
);
1161 EMIT_SELECTION_CHANGED(collection
, current_event_time
);
1164 /* Functions for managing collections */
1166 /* Remove all objects from the collection */
1167 void collection_clear(Collection
*collection
)
1169 collection_delete_if(collection
, NULL
, NULL
);
1172 /* Inserts a new item at the end. The new item is unselected, and its
1173 * number is returned.
1175 gint
collection_insert(Collection
*collection
, gpointer data
, gpointer view
)
1179 /* g_return_val_if_fail(IS_COLLECTION(collection), -1); (slow) */
1181 item
= collection
->number_of_items
;
1183 if (item
>= collection
->array_size
)
1184 resize_arrays(collection
, item
+ (item
>> 1));
1186 collection
->items
[item
].data
= data
;
1187 collection
->items
[item
].view_data
= view
;
1188 collection
->items
[item
].selected
= FALSE
;
1190 collection
->number_of_items
++;
1192 gtk_widget_queue_resize(GTK_WIDGET(collection
));
1194 collection_draw_item(collection
, item
, FALSE
);
1199 void collection_unselect_item(Collection
*collection
, gint item
)
1201 collection_item_set_selected(collection
, item
, FALSE
, TRUE
);
1204 void collection_select_item(Collection
*collection
, gint item
)
1206 collection_item_set_selected(collection
, item
, TRUE
, TRUE
);
1209 void collection_toggle_item(Collection
*collection
, gint item
)
1211 collection_item_set_selected(collection
, item
,
1212 !collection
->items
[item
].selected
, TRUE
);
1215 /* Select all items in the collection */
1216 void collection_select_all(Collection
*collection
)
1221 g_return_if_fail(collection
!= NULL
);
1222 g_return_if_fail(IS_COLLECTION(collection
));
1224 widget
= GTK_WIDGET(collection
);
1226 if (collection
->number_selected
== collection
->number_of_items
)
1227 return; /* Nothing to do */
1229 while (collection
->number_selected
< collection
->number_of_items
)
1231 while (collection
->items
[item
].selected
)
1234 collection
->items
[item
].selected
= TRUE
;
1235 collection_draw_item(collection
, item
, TRUE
);
1238 collection
->number_selected
++;
1241 g_signal_emit(collection
, collection_signals
[GAIN_SELECTION
], 0,
1242 current_event_time
);
1243 EMIT_SELECTION_CHANGED(collection
, current_event_time
);
1246 /* Toggle all items in the collection */
1247 void collection_invert_selection(Collection
*collection
)
1251 g_return_if_fail(collection
!= NULL
);
1252 g_return_if_fail(IS_COLLECTION(collection
));
1254 if (collection
->number_selected
== 0)
1256 collection_select_all(collection
);
1259 else if (collection
->number_of_items
== collection
->number_selected
)
1261 collection_clear_selection(collection
);
1265 for (item
= 0; item
< collection
->number_of_items
; item
++)
1266 collection
->items
[item
].selected
=
1267 !collection
->items
[item
].selected
;
1269 collection
->number_selected
= collection
->number_of_items
-
1270 collection
->number_selected
;
1272 /* Have to redraw everything... */
1273 gtk_widget_queue_draw(GTK_WIDGET(collection
));
1275 EMIT_SELECTION_CHANGED(collection
, current_event_time
);
1278 /* Unselect all items except number item, which is selected (-1 to unselect
1281 void collection_clear_except(Collection
*collection
, gint item
)
1285 int end
; /* Selected items to end up with */
1287 g_return_if_fail(collection
!= NULL
);
1288 g_return_if_fail(IS_COLLECTION(collection
));
1289 g_return_if_fail(item
>= -1 && item
< collection
->number_of_items
);
1291 widget
= GTK_WIDGET(collection
);
1297 collection_select_item(collection
, item
);
1301 if (collection
->number_selected
== 0)
1304 while (collection
->number_selected
> end
)
1306 while (i
== item
|| !collection
->items
[i
].selected
)
1309 collection
->items
[i
].selected
= FALSE
;
1310 collection_draw_item(collection
, i
, TRUE
);
1313 collection
->number_selected
--;
1317 g_signal_emit(collection
, collection_signals
[LOSE_SELECTION
], 0,
1318 current_event_time
);
1319 EMIT_SELECTION_CHANGED(collection
, current_event_time
);
1322 /* Unselect all items in the collection */
1323 void collection_clear_selection(Collection
*collection
)
1325 g_return_if_fail(collection
!= NULL
);
1326 g_return_if_fail(IS_COLLECTION(collection
));
1328 collection_clear_except(collection
, -1);
1331 /* Force a redraw of the specified item, if it is visible */
1332 void collection_draw_item(Collection
*collection
, gint item
, gboolean blank
)
1338 g_return_if_fail(collection
!= NULL
);
1339 /* g_return_if_fail(IS_COLLECTION(collection)); (slow) */
1340 g_return_if_fail(item
>= 0 &&
1341 (item
== 0 || item
< collection
->number_of_items
));
1343 widget
= GTK_WIDGET(collection
);
1344 if (!GTK_WIDGET_REALIZED(widget
))
1347 collection_item_to_rowcol(collection
, item
, &row
, &col
);
1349 collection_get_item_area(collection
, row
, col
, &area
);
1351 gdk_window_invalidate_rect(widget
->window
, &area
, FALSE
);
1354 void collection_set_item_size(Collection
*collection
, int width
, int height
)
1358 g_return_if_fail(collection
!= NULL
);
1359 g_return_if_fail(IS_COLLECTION(collection
));
1360 g_return_if_fail(width
> 4 && height
> 4);
1362 if (collection
->item_width
== width
&&
1363 collection
->item_height
== height
)
1366 widget
= GTK_WIDGET(collection
);
1368 collection
->item_width
= width
;
1369 collection
->item_height
= height
;
1371 if (GTK_WIDGET_REALIZED(widget
))
1375 gdk_drawable_get_size(widget
->window
, &window_width
, NULL
);
1376 collection
->columns
= MAX(window_width
/ collection
->item_width
,
1378 if (collection
->cursor_item
!= -1)
1379 scroll_to_show(collection
, collection
->cursor_item
);
1380 gtk_widget_queue_draw(widget
);
1383 gtk_widget_queue_resize(GTK_WIDGET(collection
));
1386 static int (*cmp_callback
)(const void *a
, const void *b
) = NULL
;
1387 static int collection_cmp(const void *a
, const void *b
)
1389 return cmp_callback(((CollectionItem
*) a
)->data
,
1390 ((CollectionItem
*) b
)->data
);
1392 static int collection_rcmp(const void *a
, const void *b
)
1394 return -cmp_callback(((CollectionItem
*) a
)->data
,
1395 ((CollectionItem
*) b
)->data
);
1398 /* Cursor is positioned on item with the same data as before the sort.
1399 * Same for the wink item.
1401 void collection_qsort(Collection
*collection
,
1402 int (*compar
)(const void *, const void *),
1405 int cursor
, wink
, items
, wink_on_map
;
1406 gpointer cursor_data
= NULL
;
1407 gpointer wink_data
= NULL
;
1408 gpointer wink_on_map_data
= NULL
;
1409 CollectionItem
*array
;
1411 int mul
= order
== GTK_SORT_ASCENDING
? 1 : -1;
1413 g_return_if_fail(collection
!= NULL
);
1414 g_return_if_fail(IS_COLLECTION(collection
));
1415 g_return_if_fail(compar
!= NULL
);
1416 g_return_if_fail(cmp_callback
== NULL
);
1418 /* Check to see if it needs sorting (saves redrawing) */
1419 if (collection
->number_of_items
< 2)
1422 array
= collection
->items
;
1423 for (i
= 1; i
< collection
->number_of_items
; i
++)
1425 if (mul
* compar(array
[i
- 1].data
, array
[i
].data
) > 0)
1428 if (i
== collection
->number_of_items
)
1429 return; /* Already sorted */
1431 items
= collection
->number_of_items
;
1433 wink_on_map
= collection
->wink_on_map
;
1434 if (wink_on_map
>= 0 && wink_on_map
< items
)
1436 wink_on_map_data
= collection
->items
[wink_on_map
].data
;
1437 collection
->wink_on_map
= -1;
1442 wink
= collection
->wink_item
;
1443 if (wink
>= 0 && wink
< items
)
1445 wink_data
= collection
->items
[wink
].data
;
1446 collection
->wink_item
= -1;
1451 cursor
= collection
->cursor_item
;
1452 if (cursor
>= 0 && cursor
< items
)
1453 cursor_data
= collection
->items
[cursor
].data
;
1457 cmp_callback
= compar
;
1458 qsort(collection
->items
, items
, sizeof(collection
->items
[0]),
1459 order
== GTK_SORT_ASCENDING
? collection_cmp
1461 cmp_callback
= NULL
;
1463 if (cursor
> -1 || wink
> -1 || wink_on_map
> -1)
1467 for (item
= 0; item
< items
; item
++)
1469 if (collection
->items
[item
].data
== cursor_data
)
1470 collection_set_cursor_item(collection
, item
,
1472 if (collection
->items
[item
].data
== wink_on_map_data
)
1473 collection
->wink_on_map
= item
;
1474 if (collection
->items
[item
].data
== wink_data
)
1476 collection
->cursor_item_old
= item
;
1477 collection
->wink_item
= item
;
1478 scroll_to_show(collection
, item
);
1483 gtk_widget_queue_draw(GTK_WIDGET(collection
));
1486 /* Find an item in a sorted collection.
1487 * Returns the item number, or -1 if not found.
1489 int collection_find_item(Collection
*collection
, gpointer data
,
1490 int (*compar
)(const void *, const void *),
1494 int mul
= order
== GTK_SORT_ASCENDING
? 1 : -1;
1496 g_return_val_if_fail(collection
!= NULL
, -1);
1497 g_return_val_if_fail(IS_COLLECTION(collection
), -1);
1498 g_return_val_if_fail(compar
!= NULL
, -1);
1500 /* If item is here, then: lower <= i < upper */
1502 upper
= collection
->number_of_items
;
1504 while (lower
< upper
)
1508 i
= (lower
+ upper
) >> 1;
1510 cmp
= mul
* compar(collection
->items
[i
].data
, data
);
1523 /* Return the number of the item under the point (x,y), or -1 for none.
1524 * This may call your test_point callback. The point is relative to the
1525 * collection's origin.
1527 int collection_get_item(Collection
*collection
, int x
, int y
)
1533 g_return_val_if_fail(collection
!= NULL
, -1);
1535 col
= x
/ collection
->item_width
;
1536 row
= y
/ collection
->item_height
;
1538 if (col
>= collection
->columns
)
1539 col
= collection
->columns
- 1;
1541 if (col
< 0 || row
< 0)
1544 if (col
== collection
->columns
- 1)
1545 width
= collection
->item_width
<< 1;
1547 width
= collection
->item_width
;
1549 item
= collection_rowcol_to_item(collection
, row
, col
);
1550 if (item
>= collection
->number_of_items
)
1553 x
-= col
* collection
->item_width
;
1554 y
-= row
* collection
->item_height
;
1556 if (collection
->test_point(collection
, x
, y
,
1557 &collection
->items
[item
], width
, collection
->item_height
,
1558 collection
->cb_user_data
))
1564 /* Set the cursor/highlight over the given item. Passing -1
1565 * hides the cursor. As a special case, you may set the cursor item
1566 * to zero when there are no items.
1568 void collection_set_cursor_item(Collection
*collection
, gint item
,
1569 gboolean may_scroll
)
1573 g_return_if_fail(collection
!= NULL
);
1574 g_return_if_fail(IS_COLLECTION(collection
));
1575 g_return_if_fail(item
>= -1 &&
1576 (item
< collection
->number_of_items
|| item
== 0));
1578 old_item
= collection
->cursor_item
;
1580 if (old_item
== item
)
1583 collection
->cursor_item
= item
;
1586 collection_draw_item(collection
, old_item
, TRUE
);
1590 collection_draw_item(collection
, item
, TRUE
);
1592 scroll_to_show(collection
, item
);
1594 else if (old_item
!= -1)
1595 collection
->cursor_item_old
= old_item
;
1598 /* Briefly highlight an item to draw the user's attention to it.
1599 * -1 cancels the effect, as does deleting items, sorting the collection
1600 * or starting a new wink effect.
1601 * Otherwise, the effect will cancel itself after a short pause.
1603 void collection_wink_item(Collection
*collection
, gint item
)
1605 g_return_if_fail(collection
!= NULL
);
1606 g_return_if_fail(IS_COLLECTION(collection
));
1607 g_return_if_fail(item
>= -1 && item
< collection
->number_of_items
);
1609 if (collection
->wink_item
!= -1)
1610 cancel_wink(collection
);
1614 if (!GTK_WIDGET_MAPPED(GTK_WIDGET(collection
)))
1616 collection
->wink_on_map
= item
;
1620 collection
->cursor_item_old
= collection
->wink_item
= item
;
1621 collection
->winks_left
= MAX_WINKS
;
1623 collection
->wink_timeout
= g_timeout_add(70,
1624 (GSourceFunc
) wink_timeout
,
1626 scroll_to_show(collection
, item
);
1627 invert_wink(collection
);
1632 /* Call test(item, data) on each item in the collection.
1633 * Remove all items for which it returns TRUE. test() should
1634 * free the data before returning TRUE. The collection is in an
1635 * inconsistant state during this call (ie, when test() is called).
1637 * If test is NULL, remove all items.
1639 void collection_delete_if(Collection
*collection
,
1640 gboolean (*test
)(gpointer item
, gpointer data
),
1647 g_return_if_fail(collection
!= NULL
);
1648 g_return_if_fail(IS_COLLECTION(collection
));
1650 cursor
= collection
->cursor_item
;
1652 for (in
= 0; in
< collection
->number_of_items
; in
++)
1654 if (test
&& !test(collection
->items
[in
].data
, data
))
1657 if (collection
->items
[in
].selected
)
1659 collection
->items
[out
].selected
= TRUE
;
1663 collection
->items
[out
].selected
= FALSE
;
1665 collection
->items
[out
].data
=
1666 collection
->items
[in
].data
;
1667 collection
->items
[out
].view_data
=
1668 collection
->items
[in
].view_data
;
1674 if (collection
->free_item
)
1675 collection
->free_item(collection
,
1676 &collection
->items
[in
]);
1678 if (collection
->cursor_item
>= in
)
1685 collection
->cursor_item
= cursor
;
1687 if (collection
->wink_item
!= -1)
1689 collection
->wink_item
= -1;
1690 g_source_remove(collection
->wink_timeout
);
1693 collection
->number_of_items
= out
;
1694 if (collection
->number_selected
&& !selected
)
1696 /* We've lost all the selected items */
1697 g_signal_emit(collection
,
1698 collection_signals
[LOSE_SELECTION
], 0,
1699 current_event_time
);
1702 collection
->number_selected
= selected
;
1703 resize_arrays(collection
,
1704 MAX(collection
->number_of_items
, MINIMUM_ITEMS
));
1706 if (GTK_WIDGET_REALIZED(GTK_WIDGET(collection
)))
1707 gtk_widget_queue_draw(GTK_WIDGET(collection
));
1709 gtk_widget_queue_resize(GTK_WIDGET(collection
));
1713 /* Move the cursor by the given row and column offsets.
1714 * Moving by (0,0) can be used to simply make the cursor appear.
1716 void collection_move_cursor(Collection
*collection
, int drow
, int dcol
)
1719 int first
, last
, total_rows
, total_cols
;
1721 g_return_if_fail(collection
!= NULL
);
1722 g_return_if_fail(IS_COLLECTION(collection
));
1724 if (!collection
->number_of_items
)
1726 /* Show the cursor, even though there are no items */
1727 collection_set_cursor_item(collection
, 0, TRUE
);
1731 get_visible_limits(collection
, &first
, &last
);
1732 total_rows
= collection_get_rows(collection
);
1733 total_cols
= collection_get_cols(collection
);
1735 item
= collection
->cursor_item
;
1738 item
= MIN(collection
->cursor_item_old
,
1739 collection
->number_of_items
- 1);
1749 collection_item_to_rowcol(collection
, item
, &row
, &col
);
1752 if (collection
->vertical_order
)
1755 col
= MIN(col
, total_cols
- 1);
1760 else if (row
> last
)
1764 if (collection
->vertical_order
)
1766 if (row
>= total_rows
)
1775 row
= MIN(row
, total_rows
- 1);
1780 item
= collection_rowcol_to_item(collection
, row
, col
);
1782 item
= MAX(item
, 0);
1783 item
= MIN(item
, collection
->number_of_items
-1);
1785 collection_set_cursor_item(collection
, item
, TRUE
);
1788 /* Start a lasso box drag */
1789 void collection_lasso_box(Collection
*collection
, int x
, int y
)
1791 collection
->drag_box_x
[0] = x
;
1792 collection
->drag_box_y
[0] = y
;
1793 collection
->drag_box_x
[1] = x
;
1794 collection
->drag_box_y
[1] = y
;
1796 add_lasso_box(collection
);
1799 /* Remove the lasso box. Applies fn to each item inside the box.
1800 * fn may be GDK_INVERT, GDK_SET, GDK_NOOP or GDK_CLEAR.
1802 void collection_end_lasso(Collection
*collection
, GdkFunction fn
)
1804 if (fn
!= GDK_CLEAR
)
1806 GdkRectangle region
;
1808 find_lasso_area(collection
, ®ion
);
1810 collection_process_area(collection
, ®ion
, fn
,
1814 abort_lasso(collection
);
1817 /* Unblock the selection_changed signal, emitting the signal if the
1818 * block counter reaches zero and emit is TRUE.
1820 void collection_unblock_selection_changed(Collection
*collection
,
1824 g_return_if_fail(collection
!= NULL
);
1825 g_return_if_fail(IS_COLLECTION(collection
));
1826 g_return_if_fail(collection
->block_selection_changed
> 0);
1828 collection
->block_selection_changed
--;
1830 if (emit
&& !collection
->block_selection_changed
)
1831 g_signal_emit(collection
,
1832 collection_signals
[SELECTION_CHANGED
], 0, time
);
1835 /* Translate the item number to the (row, column) form */
1836 void collection_item_to_rowcol (const Collection
*collection
,
1837 int item
, int *row
, int *col
)
1839 if (!collection
->vertical_order
)
1841 *row
= item
/ collection
->columns
;
1842 *col
= item
% collection
->columns
;
1846 int rows
= collection_get_rows(collection
);
1854 /* Translate the (row, column) form to the item number.
1855 * May return a number >= collection->number_of_items.
1857 int collection_rowcol_to_item(const Collection
*collection
, int row
, int col
)
1859 if (!collection
->vertical_order
)
1860 return row
* collection
->columns
+ col
;
1863 int rows
= collection_get_rows(collection
);
1865 return collection
->number_of_items
;
1866 return row
+ col
* rows
;