Improve some sieve-related translations
[claws.git] / src / gtk / gtkcmclist.c
blobb32af24d64a8c05699999bcdcd96601a0ea507ce
1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball, Josh MacDonald,
3 * Copyright (C) 1997-1998 Jay Painter <jpaint@serv.net><jpaint@gimp.org>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
22 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
23 * file for a list of people on the GTK+ Team. See the ChangeLog
24 * files for a list of changes. These files are distributed with
25 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
28 #include <config.h>
30 #include <stdlib.h>
31 #include <string.h>
33 #include <gtk/gtk.h>
34 #include "claws-marshal.h"
35 #include "gtkcmclist.h"
36 #include <gdk/gdkkeysyms.h>
37 #include "utils.h"
38 #include "gtkutils.h"
40 /* length of button_actions array */
41 #define MAX_BUTTON 5
43 /* the number rows memchunk expands at a time */
44 #define CMCLIST_OPTIMUM_SIZE 64
46 /* the width of the column resize windows */
47 #define DRAG_WIDTH 6
49 /* minimum allowed width of a column */
50 #define COLUMN_MIN_WIDTH 5
52 /* this defigns the base grid spacing */
53 #define CELL_SPACING 1
55 /* added the horizontal space at the beginning and end of a row*/
56 #define COLUMN_INSET 3
58 /* used for auto-scrolling */
59 #define SCROLL_TIME 100
61 /* gives the top pixel of the given row in context of
62 * the clist's voffset */
63 #define ROW_TOP_YPIXEL(clist, row) (((clist)->row_height * (row)) + \
64 (((row) + 1) * CELL_SPACING) + \
65 (clist)->voffset)
67 /* returns the row index from a y pixel location in the
68 * context of the clist's voffset */
69 #define ROW_FROM_YPIXEL(clist, y) (((y) - (clist)->voffset) / \
70 ((clist)->row_height + CELL_SPACING))
72 /* gives the left pixel of the given column in context of
73 * the clist's hoffset */
74 #define COLUMN_LEFT_XPIXEL(clist, colnum) ((clist)->column[(colnum)].area.x + \
75 (clist)->hoffset)
77 static void gtk_cmclist_scrollable_init (GtkScrollableInterface *iface);
79 /* returns the column index from a x pixel location in the
80 * context of the clist's hoffset */
81 static inline gint
82 COLUMN_FROM_XPIXEL (GtkCMCList * clist,
83 gint x)
85 gint i, cx;
87 for (i = 0; i < clist->columns; i++)
88 if (clist->column[i].visible)
90 cx = clist->column[i].area.x + clist->hoffset;
92 if (x >= (cx - (COLUMN_INSET + CELL_SPACING)) &&
93 x <= (cx + clist->column[i].area.width + COLUMN_INSET))
94 return i;
97 /* no match */
98 return -1;
101 /* returns the top pixel of the given row in the context of
102 * the list height */
103 #define ROW_TOP(clist, row) (((clist)->row_height + CELL_SPACING) * (row))
105 /* returns the left pixel of the given column in the context of
106 * the list width */
107 #define COLUMN_LEFT(clist, colnum) ((clist)->column[(colnum)].area.x)
109 /* returns the total height of the list */
110 #define LIST_HEIGHT(clist) (((clist)->row_height * ((clist)->rows)) + \
111 (CELL_SPACING * ((clist)->rows + 1)))
114 /* returns the total width of the list */
115 static inline gint
116 LIST_WIDTH (GtkCMCList * clist)
118 gint last_column;
120 for (last_column = clist->columns - 1;
121 last_column >= 0 && !clist->column[last_column].visible; last_column--);
123 if (last_column >= 0)
124 return (clist->column[last_column].area.x +
125 clist->column[last_column].area.width +
126 COLUMN_INSET + CELL_SPACING);
127 return 0;
130 /* returns the GList item for the nth row */
131 #define ROW_ELEMENT(clist, row) (((row) == (clist)->rows - 1) ? \
132 (clist)->row_list_end : \
133 g_list_nth ((clist)->row_list, (row)))
136 /* redraw the list if it's not frozen */
137 #define CLIST_UNFROZEN(clist) (((GtkCMCList*) (clist))->freeze_count == 0)
138 #define CLIST_REFRESH(clist) G_STMT_START { \
139 if (CLIST_UNFROZEN (clist)) \
140 GTK_CMCLIST_GET_CLASS (clist)->refresh ((GtkCMCList*) (clist)); \
141 } G_STMT_END
144 /* Signals */
145 enum {
146 SELECT_ROW,
147 UNSELECT_ROW,
148 ROW_MOVE,
149 CLICK_COLUMN,
150 RESIZE_COLUMN,
151 TOGGLE_FOCUS_ROW,
152 SELECT_ALL,
153 UNSELECT_ALL,
154 UNDO_SELECTION,
155 START_SELECTION,
156 END_SELECTION,
157 TOGGLE_ADD_MODE,
158 EXTEND_SELECTION,
159 SCROLL_VERTICAL,
160 SCROLL_HORIZONTAL,
161 ABORT_COLUMN_RESIZE,
162 LAST_SIGNAL
165 enum {
166 SYNC_REMOVE,
167 SYNC_INSERT
170 enum {
171 ARG_0,
172 ARG_N_COLUMNS,
173 ARG_SHADOW_TYPE,
174 ARG_SELECTION_MODE,
175 ARG_ROW_HEIGHT,
176 ARG_TITLES_ACTIVE,
177 ARG_REORDERABLE,
178 ARG_USE_DRAG_ICONS,
179 ARG_SORT_TYPE,
180 ARG_HADJUSTMENT,
181 ARG_VADJUSTMENT,
182 ARG_HSCROLL_POLICY,
183 ARG_VSCROLL_POLICY
186 /* GtkCMCList Methods */
187 static void gtk_cmclist_class_init (GtkCMCListClass *klass);
188 static void gtk_cmclist_init (GtkCMCList *clist);
189 static GObject* gtk_cmclist_constructor (GType type,
190 guint n_construct_properties,
191 GObjectConstructParam *construct_params);
193 /* GtkObject Methods */
194 static void gtk_cmclist_destroy (GtkWidget *object);
195 static void gtk_cmclist_finalize (GObject *object);
196 static void gtk_cmclist_set_arg (GObject *object,
197 guint arg_id,
198 const GValue *value,
199 GParamSpec *spec);
200 static void gtk_cmclist_get_arg (GObject *object,
201 guint arg_id,
202 GValue *value,
203 GParamSpec *spec);
205 /* GtkWidget Methods */
206 static void gtk_cmclist_realize (GtkWidget *widget);
207 static void gtk_cmclist_unrealize (GtkWidget *widget);
208 static void gtk_cmclist_map (GtkWidget *widget);
209 static void gtk_cmclist_unmap (GtkWidget *widget);
210 static gint gtk_cmclist_draw (GtkWidget *widget,
211 cairo_t *event);
212 static gint gtk_cmclist_button_press (GtkWidget *widget,
213 GdkEventButton *event);
214 static gint gtk_cmclist_button_release (GtkWidget *widget,
215 GdkEventButton *event);
216 static gint gtk_cmclist_motion (GtkWidget *widget,
217 GdkEventMotion *event);
218 static void gtk_cmclist_get_preferred_height (GtkWidget *widget,
219 gint *minimal_height,
220 gint *natural_height);
221 static void gtk_cmclist_get_preferred_width (GtkWidget *widget,
222 gint *minimal_width,
223 gint *natural_width);
224 static void gtk_cmclist_size_request (GtkWidget *widget,
225 GtkRequisition *requisition);
226 static void gtk_cmclist_size_allocate (GtkWidget *widget,
227 GtkAllocation *allocation);
228 static void gtk_cmclist_undraw_focus (GtkWidget *widget);
229 static void gtk_cmclist_draw_focus (GtkWidget *widget);
230 static gint gtk_cmclist_focus_in (GtkWidget *widget,
231 GdkEventFocus *event);
232 static gint gtk_cmclist_focus_out (GtkWidget *widget,
233 GdkEventFocus *event);
234 static gint gtk_cmclist_focus (GtkWidget *widget,
235 GtkDirectionType direction);
236 static void gtk_cmclist_set_focus_child (GtkContainer *container,
237 GtkWidget *child);
238 static void gtk_cmclist_style_set (GtkWidget *widget,
239 GtkStyle *previous_style);
240 static void gtk_cmclist_drag_begin (GtkWidget *widget,
241 GdkDragContext *context);
242 static gint gtk_cmclist_drag_motion (GtkWidget *widget,
243 GdkDragContext *context,
244 gint x,
245 gint y,
246 guint time);
247 static void gtk_cmclist_drag_leave (GtkWidget *widget,
248 GdkDragContext *context,
249 guint time);
250 static void gtk_cmclist_drag_end (GtkWidget *widget,
251 GdkDragContext *context);
252 static gboolean gtk_cmclist_drag_drop (GtkWidget *widget,
253 GdkDragContext *context,
254 gint x,
255 gint y,
256 guint time);
257 static void gtk_cmclist_drag_data_get (GtkWidget *widget,
258 GdkDragContext *context,
259 GtkSelectionData *selection_data,
260 guint info,
261 guint time);
262 static void gtk_cmclist_drag_data_received (GtkWidget *widget,
263 GdkDragContext *context,
264 gint x,
265 gint y,
266 GtkSelectionData *selection_data,
267 guint info,
268 guint time);
270 /* GtkContainer Methods */
271 static void gtk_cmclist_forall (GtkContainer *container,
272 gboolean include_internals,
273 GtkCallback callback,
274 gpointer callback_data);
276 /* Selection */
277 static void toggle_row (GtkCMCList *clist,
278 gint row,
279 gint column,
280 GdkEvent *event);
281 static void real_select_row (GtkCMCList *clist,
282 gint row,
283 gint column,
284 GdkEvent *event);
285 static void real_unselect_row (GtkCMCList *clist,
286 gint row,
287 gint column,
288 GdkEvent *event);
289 static void update_extended_selection (GtkCMCList *clist,
290 gint row);
291 static GList *selection_find (GtkCMCList *clist,
292 gint row_number,
293 GList *row_list_element);
294 static void real_select_all (GtkCMCList *clist);
295 static void real_unselect_all (GtkCMCList *clist);
296 static void move_vertical (GtkCMCList *clist,
297 gint row,
298 gfloat align);
299 static void move_horizontal (GtkCMCList *clist,
300 gint diff);
301 static void real_undo_selection (GtkCMCList *clist);
302 static void fake_unselect_all (GtkCMCList *clist,
303 gint row);
304 static void fake_toggle_row (GtkCMCList *clist,
305 gint row);
306 static void resync_selection (GtkCMCList *clist,
307 GdkEvent *event);
308 static void sync_selection (GtkCMCList *clist,
309 gint row,
310 gint mode);
311 static void set_anchor (GtkCMCList *clist,
312 gboolean add_mode,
313 gint anchor,
314 gint undo_anchor);
315 static void start_selection (GtkCMCList *clist);
316 static void end_selection (GtkCMCList *clist);
317 static void toggle_add_mode (GtkCMCList *clist);
318 static void toggle_focus_row (GtkCMCList *clist);
319 static void extend_selection (GtkCMCList *clist,
320 GtkScrollType scroll_type,
321 gfloat position,
322 gboolean auto_start_selection);
323 static gint get_selection_info (GtkCMCList *clist,
324 gint x,
325 gint y,
326 gint *row,
327 gint *column);
329 /* Scrolling */
330 static void move_focus_row (GtkCMCList *clist,
331 GtkScrollType scroll_type,
332 gfloat position);
333 static void scroll_horizontal (GtkCMCList *clist,
334 GtkScrollType scroll_type,
335 gfloat position);
336 static void scroll_vertical (GtkCMCList *clist,
337 GtkScrollType scroll_type,
338 gfloat position);
339 static void move_horizontal (GtkCMCList *clist,
340 gint diff);
341 static void move_vertical (GtkCMCList *clist,
342 gint row,
343 gfloat align);
344 static gint horizontal_timeout (GtkCMCList *clist);
345 static gint vertical_timeout (GtkCMCList *clist);
346 static void remove_grab (GtkCMCList *clist);
349 /* Resize Columns */
350 static void draw_xor_line (GtkCMCList *clist);
351 static gint new_column_width (GtkCMCList *clist,
352 gint column,
353 gint *x);
354 static void column_auto_resize (GtkCMCList *clist,
355 GtkCMCListRow *clist_row,
356 gint column,
357 gint old_width);
358 static void real_resize_column (GtkCMCList *clist,
359 gint column,
360 gint width);
361 static void abort_column_resize (GtkCMCList *clist);
362 static void cell_size_request (GtkCMCList *clist,
363 GtkCMCListRow *clist_row,
364 gint column,
365 GtkRequisition *requisition);
367 /* Buttons */
368 static void column_button_create (GtkCMCList *clist,
369 gint column);
370 static void column_button_clicked (GtkWidget *widget,
371 gpointer data);
373 /* Adjustments */
374 static void adjust_adjustments (GtkCMCList *clist,
375 gboolean block_resize);
376 static void vadjustment_value_changed (GtkAdjustment *adjustment,
377 gpointer data);
378 static void hadjustment_value_changed (GtkAdjustment *adjustment,
379 gpointer data);
381 /* Drawing */
382 static void get_cell_style (GtkCMCList *clist,
383 GtkCMCListRow *clist_row,
384 gint state,
385 gint column,
386 GtkStyle **style);
387 static gint draw_cell_pixbuf (GdkWindow *window,
388 GdkRectangle *clip_rectangle,
389 cairo_t *cr,
390 GdkPixbuf *pixbuf,
391 gint x,
392 gint y,
393 gint width,
394 gint height);
395 static void draw_row (GtkCMCList *clist,
396 GdkRectangle *area,
397 gint row,
398 GtkCMCListRow *clist_row);
399 static void draw_rows (GtkCMCList *clist,
400 GdkRectangle *area);
401 static void clist_refresh (GtkCMCList *clist);
403 /* Size Allocation / Requisition */
404 static void size_allocate_title_buttons (GtkCMCList *clist);
405 static void size_allocate_columns (GtkCMCList *clist,
406 gboolean block_resize);
407 static gint list_requisition_width (GtkCMCList *clist);
409 /* Memory Allocation/Distruction Routines */
410 static GtkCMCListColumn *columns_new (GtkCMCList *clist);
411 static void column_title_new (GtkCMCList *clist,
412 gint column,
413 const gchar *title);
414 static void columns_delete (GtkCMCList *clist);
415 static GtkCMCListRow *row_new (GtkCMCList *clist);
416 static void row_delete (GtkCMCList *clist,
417 GtkCMCListRow *clist_row);
418 static void set_cell_contents (GtkCMCList *clist,
419 GtkCMCListRow *clist_row,
420 gint column,
421 GtkCMCellType type,
422 const gchar *text,
423 guint8 spacing,
424 GdkPixbuf *pixbuf);
425 static gint real_insert_row (GtkCMCList *clist,
426 gint row,
427 gchar *text[]);
428 static void real_remove_row (GtkCMCList *clist,
429 gint row);
430 static void real_clear (GtkCMCList *clist);
432 /* Sorting */
433 static gint default_compare (GtkCMCList *clist,
434 gconstpointer row1,
435 gconstpointer row2);
436 static void real_sort_list (GtkCMCList *clist);
437 static GList *gtk_cmclist_merge (GtkCMCList *clist,
438 GList *a,
439 GList *b);
440 static GList *gtk_cmclist_mergesort (GtkCMCList *clist,
441 GList *list,
442 gint num);
443 /* Misc */
444 static gboolean title_focus_in (GtkCMCList *clist,
445 gint dir);
446 static gboolean title_focus_move (GtkCMCList *clist,
447 gint dir);
449 static void real_row_move (GtkCMCList *clist,
450 gint source_row,
451 gint dest_row);
452 static gint column_title_passive_func (GtkWidget *widget,
453 GdkEvent *event,
454 gpointer data);
455 static void drag_dest_cell (GtkCMCList *clist,
456 gint x,
457 gint y,
458 GtkCMCListDestInfo *dest_info);
462 static guint clist_signals[LAST_SIGNAL] = {0};
464 static const GtkTargetEntry clist_target_table = { "gtk-clist-drag-reorder", 0, 0};
466 G_DEFINE_TYPE_WITH_CODE (GtkCMCList, gtk_cmclist, GTK_TYPE_CONTAINER,
467 G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE,
468 gtk_cmclist_scrollable_init))
470 static void
471 gtk_cmclist_class_init (GtkCMCListClass *klass)
473 GObjectClass *object_class = G_OBJECT_CLASS (klass);
475 GtkWidgetClass *widget_class;
476 GtkContainerClass *container_class;
477 GtkBindingSet *binding_set;
479 object_class->constructor = gtk_cmclist_constructor;
481 widget_class = (GtkWidgetClass *) klass;
482 container_class = (GtkContainerClass *) klass;
484 object_class->finalize = gtk_cmclist_finalize;
485 widget_class->destroy = gtk_cmclist_destroy;
486 object_class->set_property = gtk_cmclist_set_arg;
487 object_class->get_property = gtk_cmclist_get_arg;
490 widget_class->realize = gtk_cmclist_realize;
491 widget_class->unrealize = gtk_cmclist_unrealize;
492 widget_class->map = gtk_cmclist_map;
493 widget_class->unmap = gtk_cmclist_unmap;
494 widget_class->button_press_event = gtk_cmclist_button_press;
495 widget_class->button_release_event = gtk_cmclist_button_release;
496 widget_class->motion_notify_event = gtk_cmclist_motion;
497 widget_class->draw = gtk_cmclist_draw;
499 widget_class->get_preferred_width = gtk_cmclist_get_preferred_width;
500 widget_class->get_preferred_height = gtk_cmclist_get_preferred_height;
501 widget_class->size_allocate = gtk_cmclist_size_allocate;
502 widget_class->focus_in_event = gtk_cmclist_focus_in;
503 widget_class->focus_out_event = gtk_cmclist_focus_out;
504 widget_class->style_set = gtk_cmclist_style_set;
505 widget_class->drag_begin = gtk_cmclist_drag_begin;
506 widget_class->drag_end = gtk_cmclist_drag_end;
507 widget_class->drag_motion = gtk_cmclist_drag_motion;
508 widget_class->drag_leave = gtk_cmclist_drag_leave;
509 widget_class->drag_drop = gtk_cmclist_drag_drop;
510 widget_class->drag_data_get = gtk_cmclist_drag_data_get;
511 widget_class->drag_data_received = gtk_cmclist_drag_data_received;
512 widget_class->focus = gtk_cmclist_focus;
514 /* container_class->add = NULL; use the default GtkContainerClass warning */
515 /* container_class->remove=NULL; use the default GtkContainerClass warning */
517 container_class->forall = gtk_cmclist_forall;
518 container_class->set_focus_child = gtk_cmclist_set_focus_child;
520 klass->refresh = clist_refresh;
521 klass->select_row = real_select_row;
522 klass->unselect_row = real_unselect_row;
523 klass->row_move = real_row_move;
524 klass->undo_selection = real_undo_selection;
525 klass->resync_selection = resync_selection;
526 klass->selection_find = selection_find;
527 klass->click_column = NULL;
528 klass->resize_column = real_resize_column;
529 klass->draw_row = draw_row;
530 klass->insert_row = real_insert_row;
531 klass->remove_row = real_remove_row;
532 klass->clear = real_clear;
533 klass->sort_list = real_sort_list;
534 klass->select_all = real_select_all;
535 klass->unselect_all = real_unselect_all;
536 klass->fake_unselect_all = fake_unselect_all;
537 klass->scroll_horizontal = scroll_horizontal;
538 klass->scroll_vertical = scroll_vertical;
539 klass->extend_selection = extend_selection;
540 klass->toggle_focus_row = toggle_focus_row;
541 klass->toggle_add_mode = toggle_add_mode;
542 klass->start_selection = start_selection;
543 klass->end_selection = end_selection;
544 klass->abort_column_resize = abort_column_resize;
545 klass->set_cell_contents = set_cell_contents;
546 klass->cell_size_request = cell_size_request;
548 g_object_class_install_property (object_class,
549 ARG_N_COLUMNS,
550 g_param_spec_uint ("n-columns",
551 "N-Columns",
552 "N-Columns",
554 G_MAXINT,
556 G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
557 g_object_class_install_property (object_class,
558 ARG_SHADOW_TYPE,
559 g_param_spec_enum ("shadow-type",
560 "shadow-type",
561 "shadow-type",
562 GTK_TYPE_SHADOW_TYPE, 0,
563 G_PARAM_READWRITE));
564 g_object_class_install_property (object_class,
565 ARG_SELECTION_MODE,
566 g_param_spec_enum ("selection-mode",
567 "selection-mode",
568 "selection-mode",
569 GTK_TYPE_SELECTION_MODE, 0,
570 G_PARAM_READWRITE));
571 g_object_class_install_property (object_class,
572 ARG_ROW_HEIGHT,
573 g_param_spec_uint ("row-height",
574 "row-height",
575 "row-height",
577 G_MAXINT,
579 G_PARAM_READWRITE));
580 g_object_class_install_property (object_class,
581 ARG_REORDERABLE,
582 g_param_spec_boolean ("reorderable",
583 "reorderable",
584 "reorderable",
585 TRUE,
586 G_PARAM_READWRITE));
587 g_object_class_install_property (object_class,
588 ARG_TITLES_ACTIVE,
589 g_param_spec_boolean ("titles-active",
590 "titles-active",
591 "titles-active",
592 TRUE,
593 G_PARAM_READWRITE));
594 g_object_class_install_property (object_class,
595 ARG_USE_DRAG_ICONS,
596 g_param_spec_boolean ("use-drag-icons",
597 "use-drag-icons",
598 "use-drag-icons",
599 TRUE,
600 G_PARAM_READWRITE));
601 g_object_class_install_property (object_class,
602 ARG_SORT_TYPE,
603 g_param_spec_enum ("sort-type",
604 "sort-type",
605 "sort-type",
606 GTK_TYPE_SORT_TYPE, 0,
607 G_PARAM_READWRITE));
608 /* Scrollable interface properties */
609 g_object_class_override_property (object_class, ARG_HADJUSTMENT, "hadjustment");
610 g_object_class_override_property (object_class, ARG_VADJUSTMENT, "vadjustment");
611 g_object_class_override_property (object_class, ARG_HSCROLL_POLICY, "hscroll-policy");
612 g_object_class_override_property (object_class, ARG_VSCROLL_POLICY, "vscroll-policy");
614 clist_signals[SELECT_ROW] =
615 g_signal_new ("select_row",
616 G_TYPE_FROM_CLASS (object_class),
617 G_SIGNAL_RUN_FIRST,
618 G_STRUCT_OFFSET (GtkCMCListClass, select_row),
619 NULL, NULL,
620 claws_marshal_VOID__INT_INT_BOXED,
621 G_TYPE_NONE, 3,
622 G_TYPE_INT,
623 G_TYPE_INT,
624 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
625 clist_signals[UNSELECT_ROW] =
626 g_signal_new ("unselect_row",
627 G_TYPE_FROM_CLASS (object_class),
628 G_SIGNAL_RUN_FIRST,
629 G_STRUCT_OFFSET (GtkCMCListClass, unselect_row),
630 NULL, NULL,
631 claws_marshal_VOID__INT_INT_BOXED,
632 G_TYPE_NONE, 3,
633 G_TYPE_INT,
634 G_TYPE_INT,
635 GDK_TYPE_EVENT);
636 clist_signals[ROW_MOVE] =
637 g_signal_new ("row_move",
638 G_TYPE_FROM_CLASS (object_class),
639 G_SIGNAL_RUN_LAST,
640 G_STRUCT_OFFSET (GtkCMCListClass, row_move),
641 NULL, NULL,
642 claws_marshal_VOID__INT_INT,
643 G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
644 clist_signals[CLICK_COLUMN] =
645 g_signal_new ("click_column",
646 G_TYPE_FROM_CLASS (object_class),
647 G_SIGNAL_RUN_FIRST,
648 G_STRUCT_OFFSET (GtkCMCListClass, click_column),
649 NULL, NULL,
650 claws_marshal_VOID__INT,
651 G_TYPE_NONE, 1, G_TYPE_INT);
652 clist_signals[RESIZE_COLUMN] =
653 g_signal_new ("resize_column",
654 G_TYPE_FROM_CLASS (object_class),
655 G_SIGNAL_RUN_LAST,
656 G_STRUCT_OFFSET (GtkCMCListClass, resize_column),
657 NULL, NULL,
658 claws_marshal_VOID__INT_INT,
659 G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
661 clist_signals[TOGGLE_FOCUS_ROW] =
662 g_signal_new ("toggle_focus_row",
663 G_TYPE_FROM_CLASS (object_class),
664 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
665 G_STRUCT_OFFSET (GtkCMCListClass, toggle_focus_row),
666 NULL, NULL,
667 claws_marshal_VOID__VOID,
668 G_TYPE_NONE, 0);
669 clist_signals[SELECT_ALL] =
670 g_signal_new ("select_all",
671 G_TYPE_FROM_CLASS (object_class),
672 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
673 G_STRUCT_OFFSET (GtkCMCListClass, select_all),
674 NULL, NULL,
675 claws_marshal_VOID__VOID,
676 G_TYPE_NONE, 0);
677 clist_signals[UNSELECT_ALL] =
678 g_signal_new ("unselect_all",
679 G_TYPE_FROM_CLASS (object_class),
680 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
681 G_STRUCT_OFFSET (GtkCMCListClass, unselect_all),
682 NULL, NULL,
683 claws_marshal_VOID__VOID,
684 G_TYPE_NONE, 0);
685 clist_signals[UNDO_SELECTION] =
686 g_signal_new ("undo_selection",
687 G_TYPE_FROM_CLASS (object_class),
688 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
689 G_STRUCT_OFFSET (GtkCMCListClass, undo_selection),
690 NULL, NULL,
691 claws_marshal_VOID__VOID,
692 G_TYPE_NONE, 0);
693 clist_signals[START_SELECTION] =
694 g_signal_new ("start_selection",
695 G_TYPE_FROM_CLASS (object_class),
696 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
697 G_STRUCT_OFFSET (GtkCMCListClass, start_selection),
698 NULL, NULL,
699 claws_marshal_VOID__VOID,
700 G_TYPE_NONE, 0);
701 clist_signals[END_SELECTION] =
702 g_signal_new ("end_selection",
703 G_TYPE_FROM_CLASS (object_class),
704 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
705 G_STRUCT_OFFSET (GtkCMCListClass, end_selection),
706 NULL, NULL,
707 claws_marshal_VOID__VOID,
708 G_TYPE_NONE, 0);
709 clist_signals[TOGGLE_ADD_MODE] =
710 g_signal_new ("toggle_add_mode",
711 G_TYPE_FROM_CLASS (object_class),
712 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
713 G_STRUCT_OFFSET (GtkCMCListClass, toggle_add_mode),
714 NULL, NULL,
715 claws_marshal_VOID__VOID,
716 G_TYPE_NONE, 0);
717 clist_signals[EXTEND_SELECTION] =
718 g_signal_new ("extend_selection",
719 G_TYPE_FROM_CLASS (object_class),
720 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
721 G_STRUCT_OFFSET (GtkCMCListClass, extend_selection),
722 NULL, NULL,
723 claws_marshal_VOID__ENUM_FLOAT_BOOLEAN,
724 G_TYPE_NONE, 3, GTK_TYPE_SCROLL_TYPE, G_TYPE_FLOAT, G_TYPE_BOOLEAN);
725 clist_signals[SCROLL_VERTICAL] =
726 g_signal_new ("scroll_vertical",
727 G_TYPE_FROM_CLASS (object_class),
728 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
729 G_STRUCT_OFFSET (GtkCMCListClass, scroll_vertical),
730 NULL, NULL,
731 claws_marshal_VOID__ENUM_FLOAT,
732 G_TYPE_NONE, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_FLOAT);
733 clist_signals[SCROLL_HORIZONTAL] =
734 g_signal_new ("scroll_horizontal",
735 G_TYPE_FROM_CLASS (object_class),
736 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
737 G_STRUCT_OFFSET (GtkCMCListClass, scroll_horizontal),
738 NULL, NULL,
739 claws_marshal_VOID__ENUM_FLOAT,
740 G_TYPE_NONE, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_FLOAT);
741 clist_signals[ABORT_COLUMN_RESIZE] =
742 g_signal_new ("abort_column_resize",
743 G_TYPE_FROM_CLASS (object_class),
744 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
745 G_STRUCT_OFFSET (GtkCMCListClass, abort_column_resize),
746 NULL, NULL,
747 claws_marshal_VOID__VOID,
748 G_TYPE_NONE, 0);
750 binding_set = gtk_binding_set_by_class (klass);
751 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Up, 0,
752 "scroll_vertical", 2,
753 G_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
754 G_TYPE_FLOAT, 0.0);
755 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Up, 0,
756 "scroll_vertical", 2,
757 G_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
758 G_TYPE_FLOAT, 0.0);
759 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Down, 0,
760 "scroll_vertical", 2,
761 G_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
762 G_TYPE_FLOAT, 0.0);
763 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Down, 0,
764 "scroll_vertical", 2,
765 G_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
766 G_TYPE_FLOAT, 0.0);
767 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Up, 0,
768 "scroll_vertical", 2,
769 G_TYPE_ENUM, GTK_SCROLL_PAGE_BACKWARD,
770 G_TYPE_FLOAT, 0.0);
771 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Page_Up, 0,
772 "scroll_vertical", 2,
773 G_TYPE_ENUM, GTK_SCROLL_PAGE_BACKWARD,
774 G_TYPE_FLOAT, 0.0);
775 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Down, 0,
776 "scroll_vertical", 2,
777 G_TYPE_ENUM, GTK_SCROLL_PAGE_FORWARD,
778 G_TYPE_FLOAT, 0.0);
779 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Page_Down, 0,
780 "scroll_vertical", 2,
781 G_TYPE_ENUM, GTK_SCROLL_PAGE_FORWARD,
782 G_TYPE_FLOAT, 0.0);
783 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Home, GDK_CONTROL_MASK,
784 "scroll_vertical", 2,
785 G_TYPE_ENUM, GTK_SCROLL_JUMP,
786 G_TYPE_FLOAT, 0.0);
787 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Home, GDK_CONTROL_MASK,
788 "scroll_vertical", 2,
789 G_TYPE_ENUM, GTK_SCROLL_JUMP,
790 G_TYPE_FLOAT, 0.0);
791 gtk_binding_entry_add_signal (binding_set, GDK_KEY_End, GDK_CONTROL_MASK,
792 "scroll_vertical", 2,
793 G_TYPE_ENUM, GTK_SCROLL_JUMP,
794 G_TYPE_FLOAT, 1.0);
795 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_End, GDK_CONTROL_MASK,
796 "scroll_vertical", 2,
797 G_TYPE_ENUM, GTK_SCROLL_JUMP,
798 G_TYPE_FLOAT, 1.0);
800 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Up, GDK_SHIFT_MASK,
801 "extend_selection", 3,
802 G_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
803 G_TYPE_FLOAT, 0.0, G_TYPE_BOOLEAN, TRUE);
804 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Up, GDK_SHIFT_MASK,
805 "extend_selection", 3,
806 G_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
807 G_TYPE_FLOAT, 0.0, G_TYPE_BOOLEAN, TRUE);
808 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Down, GDK_SHIFT_MASK,
809 "extend_selection", 3,
810 G_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
811 G_TYPE_FLOAT, 0.0, G_TYPE_BOOLEAN, TRUE);
812 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Down, GDK_SHIFT_MASK,
813 "extend_selection", 3,
814 G_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
815 G_TYPE_FLOAT, 0.0, G_TYPE_BOOLEAN, TRUE);
816 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Up, GDK_SHIFT_MASK,
817 "extend_selection", 3,
818 G_TYPE_ENUM, GTK_SCROLL_PAGE_BACKWARD,
819 G_TYPE_FLOAT, 0.0, G_TYPE_BOOLEAN, TRUE);
820 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Page_Up, GDK_SHIFT_MASK,
821 "extend_selection", 3,
822 G_TYPE_ENUM, GTK_SCROLL_PAGE_BACKWARD,
823 G_TYPE_FLOAT, 0.0, G_TYPE_BOOLEAN, TRUE);
824 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Down, GDK_SHIFT_MASK,
825 "extend_selection", 3,
826 G_TYPE_ENUM, GTK_SCROLL_PAGE_FORWARD,
827 G_TYPE_FLOAT, 0.0, G_TYPE_BOOLEAN, TRUE);
828 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Page_Down, GDK_SHIFT_MASK,
829 "extend_selection", 3,
830 G_TYPE_ENUM, GTK_SCROLL_PAGE_FORWARD,
831 G_TYPE_FLOAT, 0.0, G_TYPE_BOOLEAN, TRUE);
832 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Home,
833 GDK_SHIFT_MASK | GDK_CONTROL_MASK,
834 "extend_selection", 3,
835 G_TYPE_ENUM, GTK_SCROLL_JUMP,
836 G_TYPE_FLOAT, 0.0, G_TYPE_BOOLEAN, TRUE);
837 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Home,
838 GDK_SHIFT_MASK | GDK_CONTROL_MASK,
839 "extend_selection", 3,
840 G_TYPE_ENUM, GTK_SCROLL_JUMP,
841 G_TYPE_FLOAT, 0.0, G_TYPE_BOOLEAN, TRUE);
842 gtk_binding_entry_add_signal (binding_set, GDK_KEY_End,
843 GDK_SHIFT_MASK | GDK_CONTROL_MASK,
844 "extend_selection", 3,
845 G_TYPE_ENUM, GTK_SCROLL_JUMP,
846 G_TYPE_FLOAT, 1.0, G_TYPE_BOOLEAN, TRUE);
847 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_End,
848 GDK_SHIFT_MASK | GDK_CONTROL_MASK,
849 "extend_selection", 3,
850 G_TYPE_ENUM, GTK_SCROLL_JUMP,
851 G_TYPE_FLOAT, 1.0, G_TYPE_BOOLEAN, TRUE);
854 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Left, 0,
855 "scroll_horizontal", 2,
856 G_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
857 G_TYPE_FLOAT, 0.0);
858 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Left, 0,
859 "scroll_horizontal", 2,
860 G_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
861 G_TYPE_FLOAT, 0.0);
863 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Right, 0,
864 "scroll_horizontal", 2,
865 G_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
866 G_TYPE_FLOAT, 0.0);
867 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Right, 0,
868 "scroll_horizontal", 2,
869 G_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
870 G_TYPE_FLOAT, 0.0);
872 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Home, 0,
873 "scroll_horizontal", 2,
874 G_TYPE_ENUM, GTK_SCROLL_JUMP,
875 G_TYPE_FLOAT, 0.0);
876 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Home, 0,
877 "scroll_horizontal", 2,
878 G_TYPE_ENUM, GTK_SCROLL_JUMP,
879 G_TYPE_FLOAT, 0.0);
881 gtk_binding_entry_add_signal (binding_set, GDK_KEY_End, 0,
882 "scroll_horizontal", 2,
883 G_TYPE_ENUM, GTK_SCROLL_JUMP,
884 G_TYPE_FLOAT, 1.0);
886 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_End, 0,
887 "scroll_horizontal", 2,
888 G_TYPE_ENUM, GTK_SCROLL_JUMP,
889 G_TYPE_FLOAT, 1.0);
891 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0,
892 "undo_selection", 0);
893 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0,
894 "abort_column_resize", 0);
895 gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, 0,
896 "toggle_focus_row", 0);
897 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Space, 0,
898 "toggle_focus_row", 0);
899 gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, GDK_CONTROL_MASK,
900 "toggle_add_mode", 0);
901 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Space, GDK_CONTROL_MASK,
902 "toggle_add_mode", 0);
903 gtk_binding_entry_add_signal (binding_set, GDK_KEY_slash, GDK_CONTROL_MASK,
904 "select_all", 0);
905 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Divide, GDK_CONTROL_MASK,
906 "select_all", 0);
907 gtk_binding_entry_add_signal (binding_set, '\\', GDK_CONTROL_MASK,
908 "unselect_all", 0);
909 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Shift_L,
910 GDK_RELEASE_MASK | GDK_SHIFT_MASK,
911 "end_selection", 0);
912 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Shift_R,
913 GDK_RELEASE_MASK | GDK_SHIFT_MASK,
914 "end_selection", 0);
915 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Shift_L,
916 GDK_RELEASE_MASK | GDK_SHIFT_MASK |
917 GDK_CONTROL_MASK,
918 "end_selection", 0);
919 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Shift_R,
920 GDK_RELEASE_MASK | GDK_SHIFT_MASK |
921 GDK_CONTROL_MASK,
922 "end_selection", 0);
925 static void
926 gtk_cmclist_set_arg (GObject *object,
927 guint arg_id,
928 const GValue *value,
929 GParamSpec *spec)
931 GtkCMCList *clist;
933 clist = GTK_CMCLIST (object);
935 switch (arg_id)
937 case ARG_N_COLUMNS: /* only set at construction time */
938 clist->columns = MAX (1, g_value_get_uint (value));
939 break;
940 case ARG_SHADOW_TYPE:
941 gtk_cmclist_set_shadow_type (clist, g_value_get_enum (value));
942 break;
943 case ARG_SELECTION_MODE:
944 gtk_cmclist_set_selection_mode (clist, g_value_get_enum (value));
945 break;
946 case ARG_ROW_HEIGHT:
947 gtk_cmclist_set_row_height (clist, g_value_get_uint (value));
948 break;
949 case ARG_REORDERABLE:
950 gtk_cmclist_set_reorderable (clist, g_value_get_boolean (value));
951 break;
952 case ARG_TITLES_ACTIVE:
953 if (g_value_get_boolean (value))
954 gtk_cmclist_column_titles_active (clist);
955 else
956 gtk_cmclist_column_titles_passive (clist);
957 break;
958 case ARG_USE_DRAG_ICONS:
959 gtk_cmclist_set_use_drag_icons (clist, g_value_get_boolean (value));
960 break;
961 case ARG_SORT_TYPE:
962 gtk_cmclist_set_sort_type (clist, g_value_get_enum (value));
963 break;
964 case ARG_HADJUSTMENT:
965 gtk_cmclist_set_hadjustment (clist, g_value_get_object (value));
966 break;
967 case ARG_VADJUSTMENT:
968 gtk_cmclist_set_vadjustment (clist, g_value_get_object (value));
969 break;
970 case ARG_HSCROLL_POLICY:
971 case ARG_VSCROLL_POLICY:
972 break;
976 static void
977 gtk_cmclist_get_arg (GObject *object,
978 guint arg_id,
979 GValue *value,
980 GParamSpec *spec)
982 GtkCMCList *clist;
984 clist = GTK_CMCLIST (object);
986 switch (arg_id)
988 guint i;
990 case ARG_N_COLUMNS:
991 g_value_set_uint(value, clist->columns);
992 break;
993 case ARG_SHADOW_TYPE:
994 g_value_set_enum(value, clist->shadow_type);
995 break;
996 case ARG_SELECTION_MODE:
997 g_value_set_enum(value, clist->selection_mode);
998 break;
999 case ARG_ROW_HEIGHT:
1000 g_value_set_uint(value, GTK_CMCLIST_ROW_HEIGHT_SET(clist) ? clist->row_height : 0);
1001 break;
1002 case ARG_REORDERABLE:
1003 g_value_set_boolean(value, GTK_CMCLIST_REORDERABLE (clist));
1004 break;
1005 case ARG_TITLES_ACTIVE:
1006 g_value_set_boolean(value, TRUE);
1007 for (i = 0; i < clist->columns; i++)
1008 if (clist->column[i].button &&
1009 !gtk_widget_get_sensitive (clist->column[i].button))
1011 g_value_set_boolean(value, FALSE);
1012 break;
1014 break;
1015 case ARG_USE_DRAG_ICONS:
1016 g_value_set_boolean(value, GTK_CMCLIST_USE_DRAG_ICONS (clist));
1017 break;
1018 case ARG_SORT_TYPE:
1019 g_value_set_enum(value, clist->sort_type);
1020 break;
1021 case ARG_HADJUSTMENT:
1022 g_value_set_object(value, gtk_cmclist_get_hadjustment(clist));
1023 break;
1024 case ARG_VADJUSTMENT:
1025 g_value_set_object(value, gtk_cmclist_get_vadjustment(clist));
1026 break;
1027 case ARG_HSCROLL_POLICY:
1028 case ARG_VSCROLL_POLICY:
1029 g_value_set_enum(value, GTK_SCROLL_NATURAL);
1030 break;
1031 default:
1032 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, arg_id, spec);
1033 break;
1037 static void
1038 gtk_cmclist_init (GtkCMCList *clist)
1040 clist->flags = 0;
1042 gtk_widget_set_has_window (GTK_WIDGET(clist), TRUE);
1043 gtk_widget_set_can_focus (GTK_WIDGET(clist), TRUE);
1044 GTK_CMCLIST_SET_FLAG (clist, CMCLIST_DRAW_DRAG_LINE);
1045 GTK_CMCLIST_SET_FLAG (clist, CMCLIST_USE_DRAG_ICONS);
1047 clist->freeze_count = 0;
1049 clist->rows = 0;
1050 clist->row_height = 0;
1051 clist->row_list = NULL;
1052 clist->row_list_end = NULL;
1054 clist->columns = 0;
1056 clist->title_window = NULL;
1057 clist->column_title_area.x = 0;
1058 clist->column_title_area.y = 0;
1059 clist->column_title_area.width = 1;
1060 clist->column_title_area.height = 1;
1062 clist->clist_window = NULL;
1063 clist->clist_window_width = 1;
1064 clist->clist_window_height = 1;
1066 clist->hoffset = 0;
1067 clist->voffset = 0;
1069 clist->shadow_type = GTK_SHADOW_IN;
1070 clist->vadjustment = NULL;
1071 clist->hadjustment = NULL;
1073 clist->button_actions[0] = GTK_CMBUTTON_SELECTS | GTK_CMBUTTON_DRAGS;
1074 clist->button_actions[1] = GTK_CMBUTTON_IGNORED;
1075 clist->button_actions[2] = GTK_CMBUTTON_IGNORED;
1076 clist->button_actions[3] = GTK_CMBUTTON_IGNORED;
1077 clist->button_actions[4] = GTK_CMBUTTON_IGNORED;
1079 clist->cursor_drag = NULL;
1080 clist->x_drag = 0;
1082 clist->selection_mode = GTK_SELECTION_SINGLE;
1083 clist->selection = NULL;
1084 clist->selection_end = NULL;
1085 clist->undo_selection = NULL;
1086 clist->undo_unselection = NULL;
1088 clist->focus_row = -1;
1089 clist->focus_header_column = -1;
1090 clist->undo_anchor = -1;
1092 clist->anchor = -1;
1093 clist->anchor_state = GTK_STATE_SELECTED;
1094 clist->drag_pos = -1;
1095 clist->htimer = 0;
1096 clist->vtimer = 0;
1098 clist->click_cell.row = -1;
1099 clist->click_cell.column = -1;
1101 clist->compare = default_compare;
1102 clist->sort_type = GTK_SORT_ASCENDING;
1103 clist->sort_column = 0;
1105 clist->drag_highlight_row = -1;
1108 /* Constructor */
1109 static GObject*
1110 gtk_cmclist_constructor (GType type,
1111 guint n_construct_properties,
1112 GObjectConstructParam *construct_properties)
1114 GObject *object = G_OBJECT_CLASS (gtk_cmclist_parent_class)->constructor (type,
1115 n_construct_properties,
1116 construct_properties);
1117 GtkCMCList *clist = GTK_CMCLIST (object);
1119 /* allocate memory for columns */
1120 clist->column = columns_new (clist);
1122 /* there needs to be at least one column button
1123 * because there is alot of code that will break if it
1124 * isn't there
1126 column_button_create (clist, 0);
1128 clist->draw_now = 1;
1130 return object;
1133 /* GTKCLIST PUBLIC INTERFACE
1134 * gtk_cmclist_new
1135 * gtk_cmclist_new_with_titles
1136 * gtk_cmclist_set_hadjustment
1137 * gtk_cmclist_set_vadjustment
1138 * gtk_cmclist_get_hadjustment
1139 * gtk_cmclist_get_vadjustment
1140 * gtk_cmclist_set_shadow_type
1141 * gtk_cmclist_set_selection_mode
1142 * gtk_cmclist_freeze
1143 * gtk_cmclist_thaw
1145 GtkWidget*
1146 gtk_cmclist_new (gint columns)
1148 return gtk_cmclist_new_with_titles (columns, NULL);
1151 GtkWidget*
1152 gtk_cmclist_new_with_titles (gint columns,
1153 gchar *titles[])
1155 GtkCMCList *clist;
1157 clist = g_object_new (GTK_TYPE_CMCLIST,
1158 "n_columns", columns,
1159 NULL);
1160 if (titles)
1162 guint i;
1164 for (i = 0; i < clist->columns; i++)
1165 gtk_cmclist_set_column_title (clist, i, titles[i]);
1166 gtk_cmclist_column_titles_show (clist);
1168 else
1169 gtk_cmclist_column_titles_hide (clist);
1171 return GTK_WIDGET (clist);
1174 void
1175 gtk_cmclist_set_hadjustment (GtkCMCList *clist,
1176 GtkAdjustment *adjustment)
1178 GtkAdjustment *old_adjustment;
1180 cm_return_if_fail (GTK_IS_CMCLIST (clist));
1181 if (adjustment)
1182 cm_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
1184 if (clist->hadjustment == adjustment)
1185 return;
1187 old_adjustment = clist->hadjustment;
1189 if (clist->hadjustment)
1191 g_signal_handlers_disconnect_matched(G_OBJECT (clist->hadjustment), G_SIGNAL_MATCH_DATA,
1192 0, 0, 0, 0, clist);
1194 g_object_unref (G_OBJECT (clist->hadjustment));
1197 clist->hadjustment = adjustment;
1199 if (clist->hadjustment)
1201 g_object_ref_sink (clist->hadjustment);
1202 g_signal_connect (G_OBJECT (clist->hadjustment), "value_changed",
1203 G_CALLBACK( hadjustment_value_changed),
1204 (gpointer) clist);
1207 if (!clist->hadjustment || !old_adjustment)
1208 gtk_widget_queue_resize (GTK_WIDGET (clist));
1211 GtkAdjustment *
1212 gtk_cmclist_get_hadjustment (GtkCMCList *clist)
1214 cm_return_val_if_fail (GTK_IS_CMCLIST (clist), NULL);
1216 return clist->hadjustment;
1219 void
1220 gtk_cmclist_set_vadjustment (GtkCMCList *clist,
1221 GtkAdjustment *adjustment)
1223 GtkAdjustment *old_adjustment;
1225 cm_return_if_fail (GTK_IS_CMCLIST (clist));
1226 if (adjustment)
1227 cm_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
1229 if (clist->vadjustment == adjustment)
1230 return;
1232 old_adjustment = clist->vadjustment;
1234 if (clist->vadjustment)
1236 g_signal_handlers_disconnect_matched(G_OBJECT (clist->vadjustment), G_SIGNAL_MATCH_DATA,
1237 0, 0, 0, 0, clist);
1238 g_object_unref (G_OBJECT (clist->vadjustment));
1241 clist->vadjustment = adjustment;
1243 if (clist->vadjustment)
1245 g_object_ref_sink (clist->vadjustment);
1247 g_signal_connect (G_OBJECT (clist->vadjustment), "value_changed",
1248 G_CALLBACK(vadjustment_value_changed),
1249 (gpointer) clist);
1252 if (!clist->vadjustment || !old_adjustment)
1253 gtk_widget_queue_resize (GTK_WIDGET (clist));
1256 GtkAdjustment *
1257 gtk_cmclist_get_vadjustment (GtkCMCList *clist)
1259 cm_return_val_if_fail (GTK_IS_CMCLIST (clist), NULL);
1261 return clist->vadjustment;
1264 void
1265 gtk_cmclist_set_shadow_type (GtkCMCList *clist,
1266 GtkShadowType type)
1268 cm_return_if_fail (GTK_IS_CMCLIST (clist));
1270 clist->shadow_type = type;
1272 if (gtk_widget_get_visible (GTK_WIDGET(clist)))
1273 gtk_widget_queue_resize (GTK_WIDGET (clist));
1276 void
1277 gtk_cmclist_set_selection_mode (GtkCMCList *clist,
1278 GtkSelectionMode mode)
1280 cm_return_if_fail (GTK_IS_CMCLIST (clist));
1281 cm_return_if_fail (mode != GTK_SELECTION_NONE);
1283 if (mode == clist->selection_mode)
1284 return;
1286 clist->selection_mode = mode;
1287 clist->anchor = -1;
1288 clist->anchor_state = GTK_STATE_SELECTED;
1289 clist->drag_pos = -1;
1290 clist->undo_anchor = clist->focus_row;
1292 g_list_free (clist->undo_selection);
1293 g_list_free (clist->undo_unselection);
1294 clist->undo_selection = NULL;
1295 clist->undo_unselection = NULL;
1297 switch (mode)
1299 case GTK_SELECTION_MULTIPLE:
1300 return;
1301 case GTK_SELECTION_BROWSE:
1302 case GTK_SELECTION_SINGLE:
1303 gtk_cmclist_unselect_all (clist);
1304 break;
1305 default:
1306 /* Someone set it by hand */
1307 g_assert_not_reached ();
1311 void
1312 gtk_cmclist_freeze (GtkCMCList *clist)
1314 cm_return_if_fail (GTK_IS_CMCLIST (clist));
1316 clist->freeze_count++;
1319 void
1320 gtk_cmclist_thaw (GtkCMCList *clist)
1322 cm_return_if_fail (GTK_IS_CMCLIST (clist));
1324 if (clist->freeze_count)
1326 clist->freeze_count--;
1327 CLIST_REFRESH (clist);
1331 /* PUBLIC COLUMN FUNCTIONS
1332 * gtk_cmclist_column_titles_show
1333 * gtk_cmclist_column_titles_hide
1334 * gtk_cmclist_column_title_active
1335 * gtk_cmclist_column_title_passive
1336 * gtk_cmclist_column_titles_active
1337 * gtk_cmclist_column_titles_passive
1338 * gtk_cmclist_set_column_title
1339 * gtk_cmclist_get_column_title
1340 * gtk_cmclist_set_column_widget
1341 * gtk_cmclist_set_column_justification
1342 * gtk_cmclist_set_column_visibility
1343 * gtk_cmclist_set_column_resizeable
1344 * gtk_cmclist_set_column_auto_resize
1345 * gtk_cmclist_optimal_column_width
1346 * gtk_cmclist_set_column_width
1347 * gtk_cmclist_set_column_min_width
1348 * gtk_cmclist_set_column_max_width
1350 void
1351 gtk_cmclist_column_titles_show (GtkCMCList *clist)
1353 cm_return_if_fail (GTK_IS_CMCLIST (clist));
1355 if (!GTK_CMCLIST_SHOW_TITLES(clist))
1357 GTK_CMCLIST_SET_FLAG (clist, CMCLIST_SHOW_TITLES);
1358 if (clist->title_window)
1359 gdk_window_show (clist->title_window);
1360 gtk_widget_queue_resize (GTK_WIDGET (clist));
1364 void
1365 gtk_cmclist_column_titles_hide (GtkCMCList *clist)
1367 cm_return_if_fail (GTK_IS_CMCLIST (clist));
1369 if (GTK_CMCLIST_SHOW_TITLES(clist))
1371 GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_SHOW_TITLES);
1372 if (clist->title_window)
1373 gdk_window_hide (clist->title_window);
1374 gtk_widget_queue_resize (GTK_WIDGET (clist));
1378 void
1379 gtk_cmclist_column_title_active (GtkCMCList *clist,
1380 gint column)
1382 cm_return_if_fail (GTK_IS_CMCLIST (clist));
1384 if (column < 0 || column >= clist->columns)
1385 return;
1386 if (!clist->column[column].button || !clist->column[column].button_passive)
1387 return;
1389 clist->column[column].button_passive = FALSE;
1391 g_signal_handlers_disconnect_matched(G_OBJECT (clist->column[column].button), G_SIGNAL_MATCH_FUNC,
1392 0, 0, 0, column_title_passive_func, 0);
1394 gtk_widget_set_can_focus (clist->column[column].button, TRUE);
1395 if (gtk_widget_get_visible (GTK_WIDGET(clist)))
1396 gtk_widget_queue_draw (clist->column[column].button);
1399 void
1400 gtk_cmclist_column_title_passive (GtkCMCList *clist,
1401 gint column)
1403 GtkToggleButton *button;
1405 cm_return_if_fail (GTK_IS_CMCLIST (clist));
1407 if (column < 0 || column >= clist->columns)
1408 return;
1409 if (!clist->column[column].button || clist->column[column].button_passive)
1410 return;
1412 button = GTK_TOGGLE_BUTTON (clist->column[column].button);
1414 clist->column[column].button_passive = TRUE;
1416 if (gtk_toggle_button_get_active(button))
1417 g_signal_connect(G_OBJECT (clist->column[column].button),
1418 "button-release-event",
1419 G_CALLBACK(column_title_passive_func),
1420 NULL);
1421 if (gtk_widget_is_focus(gtk_bin_get_child(GTK_BIN(button))))
1422 g_signal_connect(G_OBJECT (clist->column[column].button),
1423 "leave-notify-event",
1424 G_CALLBACK(column_title_passive_func),
1425 NULL);
1427 g_signal_connect (G_OBJECT (clist->column[column].button), "event",
1428 G_CALLBACK(column_title_passive_func), NULL);
1430 gtk_widget_set_can_focus (clist->column[column].button, FALSE);
1431 if (gtk_widget_get_visible (GTK_WIDGET(clist)))
1432 gtk_widget_queue_draw (clist->column[column].button);
1435 void
1436 gtk_cmclist_column_titles_active (GtkCMCList *clist)
1438 gint i;
1440 cm_return_if_fail (GTK_IS_CMCLIST (clist));
1442 for (i = 0; i < clist->columns; i++)
1443 gtk_cmclist_column_title_active (clist, i);
1446 void
1447 gtk_cmclist_column_titles_passive (GtkCMCList *clist)
1449 gint i;
1451 cm_return_if_fail (GTK_IS_CMCLIST (clist));
1453 for (i = 0; i < clist->columns; i++)
1454 gtk_cmclist_column_title_passive (clist, i);
1457 void
1458 gtk_cmclist_set_column_title (GtkCMCList *clist,
1459 gint column,
1460 const gchar *title)
1462 gint new_button = 0;
1463 GtkWidget *old_widget;
1464 GtkWidget *alignment = NULL;
1465 GtkWidget *label;
1467 cm_return_if_fail (GTK_IS_CMCLIST (clist));
1469 if (column < 0 || column >= clist->columns)
1470 return;
1472 /* if the column button doesn't currently exist,
1473 * it has to be created first */
1474 if (!clist->column[column].button)
1476 column_button_create (clist, column);
1477 new_button = 1;
1480 column_title_new (clist, column, title);
1482 /* remove and destroy the old widget */
1483 old_widget = gtk_bin_get_child (GTK_BIN (clist->column[column].button));
1484 if (old_widget)
1485 gtk_container_remove (GTK_CONTAINER (clist->column[column].button), old_widget);
1487 /* create new alignment based no column justification */
1488 switch (clist->column[column].justification)
1490 case GTK_JUSTIFY_LEFT:
1491 alignment = gtk_alignment_new (0.0, 0.5, 0.0, 0.0);
1492 break;
1494 case GTK_JUSTIFY_RIGHT:
1495 alignment = gtk_alignment_new (1.0, 0.5, 0.0, 0.0);
1496 break;
1498 case GTK_JUSTIFY_CENTER:
1499 alignment = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
1500 break;
1502 case GTK_JUSTIFY_FILL:
1503 alignment = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
1504 break;
1507 gtk_widget_push_composite_child ();
1508 label = gtk_label_new (clist->column[column].title);
1509 gtk_widget_pop_composite_child ();
1510 gtk_container_add (GTK_CONTAINER (alignment), label);
1511 gtk_container_add (GTK_CONTAINER (clist->column[column].button), alignment);
1512 gtk_widget_show (label);
1513 gtk_widget_show (alignment);
1515 /* if this button didn't previously exist, then the
1516 * column button positions have to be re-computed */
1517 if (gtk_widget_get_visible (GTK_WIDGET(clist)) && new_button)
1518 size_allocate_title_buttons (clist);
1521 gchar *
1522 gtk_cmclist_get_column_title (GtkCMCList *clist,
1523 gint column)
1525 cm_return_val_if_fail (GTK_IS_CMCLIST (clist), NULL);
1527 if (column < 0 || column >= clist->columns)
1528 return NULL;
1530 return clist->column[column].title;
1533 void
1534 gtk_cmclist_set_column_widget (GtkCMCList *clist,
1535 gint column,
1536 GtkWidget *widget)
1538 gint new_button = 0;
1539 GtkWidget *old_widget;
1541 cm_return_if_fail (GTK_IS_CMCLIST (clist));
1543 if (column < 0 || column >= clist->columns)
1544 return;
1546 /* if the column button doesn't currently exist,
1547 * it has to be created first */
1548 if (!clist->column[column].button)
1550 column_button_create (clist, column);
1551 new_button = 1;
1554 column_title_new (clist, column, NULL);
1556 /* remove and destroy the old widget */
1557 old_widget = gtk_bin_get_child (GTK_BIN (clist->column[column].button));
1558 if (old_widget)
1559 gtk_container_remove (GTK_CONTAINER (clist->column[column].button),
1560 old_widget);
1562 /* add and show the widget */
1563 if (widget)
1565 gtk_container_add (GTK_CONTAINER (clist->column[column].button), widget);
1566 gtk_widget_show (widget);
1569 /* if this button didn't previously exist, then the
1570 * column button positions have to be re-computed */
1571 if (gtk_widget_get_visible (GTK_WIDGET(clist)) && new_button)
1572 size_allocate_title_buttons (clist);
1575 GtkWidget *
1576 gtk_cmclist_get_column_widget (GtkCMCList *clist,
1577 gint column)
1579 cm_return_val_if_fail (GTK_IS_CMCLIST (clist), NULL);
1581 if (column < 0 || column >= clist->columns)
1582 return NULL;
1584 if (clist->column[column].button)
1585 return gtk_bin_get_child (GTK_BIN (clist->column[column].button));
1587 return NULL;
1590 void
1591 gtk_cmclist_set_column_justification (GtkCMCList *clist,
1592 gint column,
1593 GtkJustification justification)
1595 GtkWidget *alignment;
1597 cm_return_if_fail (GTK_IS_CMCLIST (clist));
1599 if (column < 0 || column >= clist->columns)
1600 return;
1602 clist->column[column].justification = justification;
1604 /* change the alinment of the button title if it's not a
1605 * custom widget */
1606 if (clist->column[column].title)
1608 alignment = gtk_bin_get_child (GTK_BIN (clist->column[column].button));
1610 switch (clist->column[column].justification)
1612 case GTK_JUSTIFY_LEFT:
1613 gtk_alignment_set (GTK_ALIGNMENT (alignment), 0.0, 0.5, 0.0, 0.0);
1614 break;
1616 case GTK_JUSTIFY_RIGHT:
1617 gtk_alignment_set (GTK_ALIGNMENT (alignment), 1.0, 0.5, 0.0, 0.0);
1618 break;
1620 case GTK_JUSTIFY_CENTER:
1621 gtk_alignment_set (GTK_ALIGNMENT (alignment), 0.5, 0.5, 0.0, 0.0);
1622 break;
1624 case GTK_JUSTIFY_FILL:
1625 gtk_alignment_set (GTK_ALIGNMENT (alignment), 0.5, 0.5, 0.0, 0.0);
1626 break;
1628 default:
1629 break;
1633 if (CLIST_UNFROZEN (clist))
1634 draw_rows (clist, NULL);
1637 void
1638 gtk_cmclist_set_column_visibility (GtkCMCList *clist,
1639 gint column,
1640 gboolean visible)
1642 cm_return_if_fail (GTK_IS_CMCLIST (clist));
1644 if (column < 0 || column >= clist->columns)
1645 return;
1646 if (clist->column[column].visible == visible)
1647 return;
1649 /* don't hide last visible column */
1650 if (!visible)
1652 gint i;
1653 gint vis_columns = 0;
1655 for (i = 0, vis_columns = 0; i < clist->columns && vis_columns < 2; i++)
1656 if (clist->column[i].visible)
1657 vis_columns++;
1659 if (vis_columns < 2)
1660 return;
1663 clist->column[column].visible = visible;
1665 if (clist->column[column].button)
1667 if (visible)
1668 gtk_widget_show (clist->column[column].button);
1669 else
1670 gtk_widget_hide (clist->column[column].button);
1673 gtk_widget_queue_resize (GTK_WIDGET(clist));
1676 void
1677 gtk_cmclist_set_column_resizeable (GtkCMCList *clist,
1678 gint column,
1679 gboolean resizeable)
1681 cm_return_if_fail (GTK_IS_CMCLIST (clist));
1683 if (column < 0 || column >= clist->columns)
1684 return;
1685 if (clist->column[column].resizeable == resizeable)
1686 return;
1688 clist->column[column].resizeable = resizeable;
1689 if (resizeable)
1690 clist->column[column].auto_resize = FALSE;
1692 if (gtk_widget_get_visible (GTK_WIDGET(clist)))
1693 size_allocate_title_buttons (clist);
1696 void
1697 gtk_cmclist_set_column_auto_resize (GtkCMCList *clist,
1698 gint column,
1699 gboolean auto_resize)
1701 cm_return_if_fail (GTK_IS_CMCLIST (clist));
1703 if (column < 0 || column >= clist->columns)
1704 return;
1705 if (clist->column[column].auto_resize == auto_resize)
1706 return;
1708 clist->column[column].auto_resize = auto_resize;
1709 if (auto_resize)
1711 clist->column[column].resizeable = FALSE;
1712 if (!GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
1714 gint width;
1716 width = gtk_cmclist_optimal_column_width (clist, column);
1717 gtk_cmclist_set_column_width (clist, column, width);
1721 if (gtk_widget_get_visible (GTK_WIDGET(clist)))
1722 size_allocate_title_buttons (clist);
1725 gint
1726 gtk_cmclist_columns_autosize (GtkCMCList *clist)
1728 gint i;
1729 gint width;
1731 cm_return_val_if_fail (GTK_IS_CMCLIST (clist), 0);
1733 gtk_cmclist_freeze (clist);
1734 width = 0;
1735 for (i = 0; i < clist->columns; i++)
1737 gtk_cmclist_set_column_width (clist, i,
1738 gtk_cmclist_optimal_column_width (clist, i));
1740 width += clist->column[i].width;
1743 gtk_cmclist_thaw (clist);
1744 return width;
1747 gint
1748 gtk_cmclist_optimal_column_width (GtkCMCList *clist,
1749 gint column)
1751 GtkRequisition requisition;
1752 GList *list;
1753 gint width;
1755 cm_return_val_if_fail (GTK_CMCLIST (clist), 0);
1757 if (column < 0 || column >= clist->columns)
1758 return 0;
1760 if (GTK_CMCLIST_SHOW_TITLES(clist) && clist->column[column].button)
1762 gtk_widget_get_requisition (clist->column[column].button, &requisition);
1763 width = requisition.width
1764 #if 0
1765 (CELL_SPACING + (2 * COLUMN_INSET)))
1766 #endif
1769 else
1770 width = 0;
1772 for (list = clist->row_list; list; list = list->next)
1774 GTK_CMCLIST_GET_CLASS (clist)->cell_size_request
1775 (clist, GTK_CMCLIST_ROW (list), column, &requisition);
1776 width = MAX (width, requisition.width);
1779 return width;
1782 void
1783 gtk_cmclist_set_column_width (GtkCMCList *clist,
1784 gint column,
1785 gint width)
1787 cm_return_if_fail (GTK_IS_CMCLIST (clist));
1789 if (column < 0 || column >= clist->columns)
1790 return;
1792 g_signal_emit (G_OBJECT (clist), clist_signals[RESIZE_COLUMN], 0,
1793 column, width);
1796 void
1797 gtk_cmclist_set_column_min_width (GtkCMCList *clist,
1798 gint column,
1799 gint min_width)
1801 cm_return_if_fail (GTK_IS_CMCLIST (clist));
1803 if (column < 0 || column >= clist->columns)
1804 return;
1805 if (clist->column[column].min_width == min_width)
1806 return;
1808 if (clist->column[column].max_width >= 0 &&
1809 clist->column[column].max_width < min_width)
1810 clist->column[column].min_width = clist->column[column].max_width;
1811 else
1812 clist->column[column].min_width = min_width;
1814 if (clist->column[column].area.width < clist->column[column].min_width)
1815 gtk_cmclist_set_column_width (clist, column,clist->column[column].min_width);
1818 void
1819 gtk_cmclist_set_column_max_width (GtkCMCList *clist,
1820 gint column,
1821 gint max_width)
1823 cm_return_if_fail (GTK_IS_CMCLIST (clist));
1825 if (column < 0 || column >= clist->columns)
1826 return;
1827 if (clist->column[column].max_width == max_width)
1828 return;
1830 if (clist->column[column].min_width >= 0 && max_width >= 0 &&
1831 clist->column[column].min_width > max_width)
1832 clist->column[column].max_width = clist->column[column].min_width;
1833 else
1834 clist->column[column].max_width = max_width;
1836 if (clist->column[column].area.width > clist->column[column].max_width)
1837 gtk_cmclist_set_column_width (clist, column,clist->column[column].max_width);
1840 /* PRIVATE COLUMN FUNCTIONS
1841 * column_auto_resize
1842 * real_resize_column
1843 * abort_column_resize
1844 * size_allocate_title_buttons
1845 * size_allocate_columns
1846 * list_requisition_width
1847 * new_column_width
1848 * column_button_create
1849 * column_button_clicked
1850 * column_title_passive_func
1852 static void
1853 column_auto_resize (GtkCMCList *clist,
1854 GtkCMCListRow *clist_row,
1855 gint column,
1856 gint old_width)
1858 /* resize column if needed for auto_resize */
1859 GtkRequisition requisition;
1861 if (!clist->column[column].auto_resize ||
1862 GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
1863 return;
1865 if (clist_row)
1866 GTK_CMCLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row,
1867 column, &requisition);
1868 else
1869 requisition.width = 0;
1871 if (requisition.width > clist->column[column].width)
1872 gtk_cmclist_set_column_width (clist, column, requisition.width);
1873 else if (requisition.width < old_width &&
1874 old_width == clist->column[column].width)
1876 GList *list;
1877 gint new_width = 0;
1879 /* run a "gtk_cmclist_optimal_column_width" but break, if
1880 * the column doesn't shrink */
1881 if (GTK_CMCLIST_SHOW_TITLES(clist) && clist->column[column].button)
1883 gtk_widget_get_requisition (clist->column[column].button, &requisition);
1884 new_width = (requisition.width -
1885 (CELL_SPACING + (2 * COLUMN_INSET)));
1887 else
1888 new_width = 0;
1890 for (list = clist->row_list; list; list = list->next)
1892 GTK_CMCLIST_GET_CLASS (clist)->cell_size_request
1893 (clist, GTK_CMCLIST_ROW (list), column, &requisition);
1894 new_width = MAX (new_width, requisition.width);
1895 if (new_width == clist->column[column].width)
1896 break;
1898 if (new_width < clist->column[column].width)
1899 gtk_cmclist_set_column_width
1900 (clist, column, MAX (new_width, clist->column[column].min_width));
1904 static void
1905 real_resize_column (GtkCMCList *clist,
1906 gint column,
1907 gint width)
1909 cm_return_if_fail (GTK_IS_CMCLIST (clist));
1911 if (column < 0 || column >= clist->columns)
1912 return;
1914 if (width < MAX (COLUMN_MIN_WIDTH, clist->column[column].min_width))
1915 width = MAX (COLUMN_MIN_WIDTH, clist->column[column].min_width);
1916 if (clist->column[column].max_width >= 0 &&
1917 width > clist->column[column].max_width)
1918 width = clist->column[column].max_width;
1920 clist->column[column].width = width;
1921 clist->column[column].width_set = TRUE;
1923 /* FIXME: this is quite expensive to do if the widget hasn't
1924 * been size_allocated yet, and pointless. Should
1925 * a flag be kept
1927 size_allocate_columns (clist, TRUE);
1928 size_allocate_title_buttons (clist);
1930 CLIST_REFRESH (clist);
1933 static void
1934 abort_column_resize (GtkCMCList *clist)
1936 cm_return_if_fail (GTK_IS_CMCLIST (clist));
1938 if (!GTK_CMCLIST_IN_DRAG(clist))
1939 return;
1941 GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_IN_DRAG);
1942 gtk_grab_remove (GTK_WIDGET (clist));
1943 gdk_display_pointer_ungrab (gtk_widget_get_display (GTK_WIDGET (clist)),
1944 GDK_CURRENT_TIME);
1945 clist->drag_pos = -1;
1947 if (clist->x_drag >= 0 && clist->x_drag <= clist->clist_window_width - 1)
1948 clist_refresh(clist);
1951 static void
1952 size_allocate_title_buttons (GtkCMCList *clist)
1954 GtkAllocation button_allocation;
1955 gint last_column;
1956 gint last_button = 0;
1957 gint i;
1959 if (!gtk_widget_get_realized (GTK_WIDGET(clist)))
1960 return;
1962 /* we're too early, the widget is not yet ready */
1963 if (clist->column_title_area.height <= 1)
1964 return;
1966 button_allocation.x = clist->hoffset;
1967 button_allocation.y = 0;
1968 button_allocation.width = 0;
1969 button_allocation.height = clist->column_title_area.height;
1971 /* find last visible column */
1972 for (last_column = clist->columns - 1; last_column >= 0; last_column--)
1973 if (clist->column[last_column].visible)
1974 break;
1976 for (i = 0; i < last_column; i++)
1978 if (!clist->column[i].visible)
1980 last_button = i + 1;
1981 gdk_window_hide (clist->column[i].window);
1982 continue;
1985 button_allocation.width += (clist->column[i].area.width +
1986 CELL_SPACING + 2 * COLUMN_INSET);
1988 if (!clist->column[i + 1].button)
1990 gdk_window_hide (clist->column[i].window);
1991 continue;
1994 gtk_widget_size_allocate (clist->column[last_button].button,
1995 &button_allocation);
1996 button_allocation.x += button_allocation.width;
1997 button_allocation.width = 0;
1999 if (clist->column[last_button].resizeable)
2001 gdk_window_show (clist->column[last_button].window);
2002 gdk_window_move_resize (clist->column[last_button].window,
2003 button_allocation.x - (DRAG_WIDTH / 2),
2004 0, DRAG_WIDTH,
2005 clist->column_title_area.height);
2007 else
2008 gdk_window_hide (clist->column[last_button].window);
2010 last_button = i + 1;
2013 button_allocation.width += (clist->column[last_column].area.width +
2014 2 * (CELL_SPACING + COLUMN_INSET));
2015 gtk_widget_size_allocate (clist->column[last_button].button,
2016 &button_allocation);
2018 if (clist->column[last_button].resizeable)
2020 button_allocation.x += button_allocation.width;
2022 gdk_window_show (clist->column[last_button].window);
2023 gdk_window_move_resize (clist->column[last_button].window,
2024 button_allocation.x - (DRAG_WIDTH / 2),
2025 0, DRAG_WIDTH, clist->column_title_area.height);
2027 else
2028 gdk_window_hide (clist->column[last_button].window);
2031 static void
2032 size_allocate_columns (GtkCMCList *clist,
2033 gboolean block_resize)
2035 GtkRequisition requisition;
2036 gint xoffset = CELL_SPACING + COLUMN_INSET;
2037 gint last_column;
2038 gint i;
2040 /* find last visible column and calculate correct column width */
2041 for (last_column = clist->columns - 1;
2042 last_column >= 0 && !clist->column[last_column].visible; last_column--);
2044 if (last_column < 0)
2045 return;
2047 for (i = 0; i <= last_column; i++)
2049 if (!clist->column[i].visible)
2050 continue;
2051 clist->column[i].area.x = xoffset;
2052 if (clist->column[i].width_set)
2054 if (!block_resize && GTK_CMCLIST_SHOW_TITLES(clist) &&
2055 clist->column[i].auto_resize && clist->column[i].button)
2057 gint width;
2059 gtk_widget_get_requisition (clist->column[i].button, &requisition);
2060 width = (requisition.width -
2061 (CELL_SPACING + (2 * COLUMN_INSET)));
2063 if (width > clist->column[i].width)
2064 gtk_cmclist_set_column_width (clist, i, width);
2067 clist->column[i].area.width = clist->column[i].width;
2068 xoffset += clist->column[i].width + CELL_SPACING + (2* COLUMN_INSET);
2070 else if (GTK_CMCLIST_SHOW_TITLES(clist) && clist->column[i].button)
2072 gtk_widget_get_requisition (clist->column[i].button, &requisition);
2073 clist->column[i].area.width =
2074 requisition.width -
2075 (CELL_SPACING + (2 * COLUMN_INSET));
2076 xoffset += requisition.width;
2080 clist->column[last_column].area.width = clist->column[last_column].area.width
2081 + MAX (0, clist->clist_window_width + COLUMN_INSET - xoffset);
2084 static gint
2085 list_requisition_width (GtkCMCList *clist)
2087 GtkRequisition requisition;
2088 gint width = CELL_SPACING;
2089 gint i;
2091 for (i = clist->columns - 1; i >= 0; i--)
2093 if (!clist->column[i].visible)
2094 continue;
2096 if (clist->column[i].width_set)
2097 width += clist->column[i].width + CELL_SPACING + (2 * COLUMN_INSET);
2098 else if (GTK_CMCLIST_SHOW_TITLES(clist) && clist->column[i].button)
2100 gtk_widget_get_requisition (clist->column[i].button, &requisition);
2101 width += requisition.width;
2105 return width;
2108 /* this function returns the new width of the column being resized given
2109 * the column and x position of the cursor; the x cursor position is passed
2110 * in as a pointer and automagicly corrected if it's beyond min/max limits */
2111 static gint
2112 new_column_width (GtkCMCList *clist,
2113 gint column,
2114 gint *x)
2116 gint xthickness = gtk_widget_get_style (GTK_WIDGET (clist))->xthickness;
2117 gint width;
2118 gint cx;
2119 gint dx;
2120 gint last_column;
2122 /* first translate the x position from widget->window
2123 * to clist->clist_window */
2124 cx = *x - xthickness;
2126 for (last_column = clist->columns - 1;
2127 last_column >= 0 && !clist->column[last_column].visible; last_column--);
2129 /* calculate new column width making sure it doesn't end up
2130 * less than the minimum width */
2131 dx = (COLUMN_LEFT_XPIXEL (clist, column) + COLUMN_INSET +
2132 (column < last_column) * CELL_SPACING);
2133 width = cx - dx;
2135 if (width < MAX (COLUMN_MIN_WIDTH, clist->column[column].min_width))
2137 width = MAX (COLUMN_MIN_WIDTH, clist->column[column].min_width);
2138 cx = dx + width;
2139 *x = cx + xthickness;
2141 else if (clist->column[column].max_width >= COLUMN_MIN_WIDTH &&
2142 width > clist->column[column].max_width)
2144 width = clist->column[column].max_width;
2145 cx = dx + clist->column[column].max_width;
2146 *x = cx + xthickness;
2149 if (cx < 0 || cx > clist->clist_window_width)
2150 *x = -1;
2152 return width;
2155 static void
2156 column_button_create (GtkCMCList *clist,
2157 gint column)
2159 GtkWidget *button;
2161 gtk_widget_push_composite_child ();
2162 button = clist->column[column].button = gtk_button_new ();
2163 GtkRcStyle *style = gtk_rc_style_new();
2164 style->ythickness = 0;
2165 gtk_widget_modify_style(clist->column[column].button, style);
2166 g_object_unref(style);
2167 gtk_container_set_border_width(GTK_CONTAINER(button), 0);
2168 gtk_widget_pop_composite_child ();
2170 if (gtk_widget_get_realized (GTK_WIDGET(clist)) && clist->title_window)
2171 gtk_widget_set_parent_window (clist->column[column].button,
2172 clist->title_window);
2173 gtk_widget_set_parent (button, GTK_WIDGET (clist));
2175 g_signal_connect (G_OBJECT (button), "clicked",
2176 G_CALLBACK(column_button_clicked),
2177 (gpointer) clist);
2178 gtk_widget_show (button);
2181 static void
2182 column_button_clicked (GtkWidget *widget,
2183 gpointer data)
2185 gint i;
2186 GtkCMCList *clist;
2188 cm_return_if_fail (widget != NULL);
2189 cm_return_if_fail (GTK_IS_CMCLIST (data));
2191 clist = GTK_CMCLIST (data);
2193 /* find the column who's button was pressed */
2194 for (i = 0; i < clist->columns; i++)
2195 if (clist->column[i].button == widget)
2196 break;
2198 g_signal_emit (G_OBJECT (clist), clist_signals[CLICK_COLUMN], 0, i);
2201 static gint
2202 column_title_passive_func (GtkWidget *widget,
2203 GdkEvent *event,
2204 gpointer data)
2206 cm_return_val_if_fail (event != NULL, FALSE);
2208 switch (event->type)
2210 case GDK_MOTION_NOTIFY:
2211 case GDK_BUTTON_PRESS:
2212 case GDK_2BUTTON_PRESS:
2213 case GDK_3BUTTON_PRESS:
2214 case GDK_BUTTON_RELEASE:
2215 case GDK_ENTER_NOTIFY:
2216 case GDK_LEAVE_NOTIFY:
2217 return TRUE;
2218 default:
2219 break;
2221 return FALSE;
2225 /* PUBLIC CELL FUNCTIONS
2226 * gtk_cmclist_get_cell_type
2227 * gtk_cmclist_set_text
2228 * gtk_cmclist_get_text
2229 * gtk_cmclist_set_pixbuf
2230 * gtk_cmclist_get_pixbuf
2231 * gtk_cmclist_set_pixtext
2232 * gtk_cmclist_get_pixtext
2233 * gtk_cmclist_set_shift
2235 GtkCMCellType
2236 gtk_cmclist_get_cell_type (GtkCMCList *clist,
2237 gint row,
2238 gint column)
2240 GtkCMCListRow *clist_row;
2242 cm_return_val_if_fail (GTK_IS_CMCLIST (clist), -1);
2244 if (row < 0 || row >= clist->rows)
2245 return -1;
2246 if (column < 0 || column >= clist->columns)
2247 return -1;
2249 clist_row = ROW_ELEMENT (clist, row)->data;
2251 return clist_row->cell[column].type;
2254 void
2255 gtk_cmclist_set_text (GtkCMCList *clist,
2256 gint row,
2257 gint column,
2258 const gchar *text)
2260 GtkCMCListRow *clist_row;
2262 cm_return_if_fail (GTK_IS_CMCLIST (clist));
2264 if (row < 0 || row >= clist->rows)
2265 return;
2266 if (column < 0 || column >= clist->columns)
2267 return;
2269 clist_row = ROW_ELEMENT (clist, row)->data;
2271 /* if text is null, then the cell is empty */
2272 GTK_CMCLIST_GET_CLASS (clist)->set_cell_contents
2273 (clist, clist_row, column, GTK_CMCELL_TEXT, text, 0, NULL);
2275 /* redraw the list if it's not frozen */
2276 if (CLIST_UNFROZEN (clist))
2278 if (gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
2279 GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
2283 gint
2284 gtk_cmclist_get_text (GtkCMCList *clist,
2285 gint row,
2286 gint column,
2287 gchar **text)
2289 GtkCMCListRow *clist_row;
2291 cm_return_val_if_fail (GTK_IS_CMCLIST (clist), 0);
2293 if (row < 0 || row >= clist->rows)
2294 return 0;
2295 if (column < 0 || column >= clist->columns)
2296 return 0;
2298 clist_row = ROW_ELEMENT (clist, row)->data;
2300 if (clist_row->cell[column].type != GTK_CMCELL_TEXT)
2301 return 0;
2303 if (text)
2304 *text = GTK_CMCELL_TEXT (clist_row->cell[column])->text;
2306 return 1;
2309 void
2310 gtk_cmclist_set_pixbuf (GtkCMCList *clist,
2311 gint row,
2312 gint column,
2313 GdkPixbuf *pixbuf)
2315 GtkCMCListRow *clist_row;
2317 cm_return_if_fail (GTK_IS_CMCLIST (clist));
2319 if (row < 0 || row >= clist->rows)
2320 return;
2321 if (column < 0 || column >= clist->columns)
2322 return;
2324 clist_row = ROW_ELEMENT (clist, row)->data;
2326 g_object_ref (pixbuf);
2328 GTK_CMCLIST_GET_CLASS (clist)->set_cell_contents
2329 (clist, clist_row, column, GTK_CMCELL_PIXBUF, NULL, 0, pixbuf);
2331 /* redraw the list if it's not frozen */
2332 if (CLIST_UNFROZEN (clist))
2334 if (gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
2335 GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
2339 gint
2340 gtk_cmclist_get_pixbuf (GtkCMCList *clist,
2341 gint row,
2342 gint column,
2343 GdkPixbuf **pixbuf)
2345 GtkCMCListRow *clist_row;
2347 cm_return_val_if_fail (GTK_IS_CMCLIST (clist), 0);
2349 if (row < 0 || row >= clist->rows)
2350 return 0;
2351 if (column < 0 || column >= clist->columns)
2352 return 0;
2354 clist_row = ROW_ELEMENT (clist, row)->data;
2356 if (clist_row->cell[column].type != GTK_CMCELL_PIXBUF)
2357 return 0;
2359 if (pixbuf)
2361 *pixbuf = GTK_CMCELL_PIXBUF (clist_row->cell[column])->pixbuf;
2364 return 1;
2367 void
2368 gtk_cmclist_set_pixtext (GtkCMCList *clist,
2369 gint row,
2370 gint column,
2371 const gchar *text,
2372 guint8 spacing,
2373 GdkPixbuf *pixbuf)
2375 GtkCMCListRow *clist_row;
2377 cm_return_if_fail (GTK_IS_CMCLIST (clist));
2379 if (row < 0 || row >= clist->rows)
2380 return;
2381 if (column < 0 || column >= clist->columns)
2382 return;
2384 clist_row = ROW_ELEMENT (clist, row)->data;
2386 g_object_ref (pixbuf);
2387 GTK_CMCLIST_GET_CLASS (clist)->set_cell_contents
2388 (clist, clist_row, column, GTK_CMCELL_PIXTEXT, text, spacing, pixbuf);
2390 /* redraw the list if it's not frozen */
2391 if (CLIST_UNFROZEN (clist))
2393 if (gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
2394 GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
2398 gint
2399 gtk_cmclist_get_pixtext (GtkCMCList *clist,
2400 gint row,
2401 gint column,
2402 gchar **text,
2403 guint8 *spacing,
2404 GdkPixbuf **pixbuf)
2406 GtkCMCListRow *clist_row;
2408 cm_return_val_if_fail (GTK_IS_CMCLIST (clist), 0);
2410 if (row < 0 || row >= clist->rows)
2411 return 0;
2412 if (column < 0 || column >= clist->columns)
2413 return 0;
2415 clist_row = ROW_ELEMENT (clist, row)->data;
2417 if (clist_row->cell[column].type != GTK_CMCELL_PIXTEXT)
2418 return 0;
2420 if (text)
2421 *text = GTK_CMCELL_PIXTEXT (clist_row->cell[column])->text;
2422 if (spacing)
2423 *spacing = GTK_CMCELL_PIXTEXT (clist_row->cell[column])->spacing;
2424 if (pixbuf)
2425 *pixbuf = GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf;
2427 return 1;
2430 void
2431 gtk_cmclist_set_shift (GtkCMCList *clist,
2432 gint row,
2433 gint column,
2434 gint vertical,
2435 gint horizontal)
2437 GtkRequisition requisition = { 0 };
2438 GtkCMCListRow *clist_row;
2440 cm_return_if_fail (GTK_IS_CMCLIST (clist));
2442 if (row < 0 || row >= clist->rows)
2443 return;
2444 if (column < 0 || column >= clist->columns)
2445 return;
2447 clist_row = ROW_ELEMENT (clist, row)->data;
2449 if (clist->column[column].auto_resize &&
2450 !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
2451 GTK_CMCLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row,
2452 column, &requisition);
2454 clist_row->cell[column].vertical = vertical;
2455 clist_row->cell[column].horizontal = horizontal;
2457 column_auto_resize (clist, clist_row, column, requisition.width);
2459 if (CLIST_UNFROZEN (clist) && gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
2460 GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
2463 /* PRIVATE CELL FUNCTIONS
2464 * set_cell_contents
2465 * cell_size_request
2467 static void
2468 set_cell_contents (GtkCMCList *clist,
2469 GtkCMCListRow *clist_row,
2470 gint column,
2471 GtkCMCellType type,
2472 const gchar *text,
2473 guint8 spacing,
2474 GdkPixbuf *pixbuf)
2476 GtkRequisition requisition;
2477 gchar *old_text = NULL;
2478 GdkPixbuf *old_pixbuf = NULL;
2480 cm_return_if_fail (GTK_IS_CMCLIST (clist));
2481 cm_return_if_fail (clist_row != NULL);
2483 if (clist->column[column].auto_resize &&
2484 !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
2485 GTK_CMCLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row,
2486 column, &requisition);
2488 switch (clist_row->cell[column].type)
2490 case GTK_CMCELL_EMPTY:
2491 break;
2492 case GTK_CMCELL_TEXT:
2493 old_text = GTK_CMCELL_TEXT (clist_row->cell[column])->text;
2494 break;
2495 case GTK_CMCELL_PIXBUF:
2496 old_pixbuf = GTK_CMCELL_PIXBUF (clist_row->cell[column])->pixbuf;
2497 break;
2498 case GTK_CMCELL_PIXTEXT:
2499 old_text = GTK_CMCELL_PIXTEXT (clist_row->cell[column])->text;
2500 old_pixbuf = GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf;
2501 break;
2502 case GTK_CMCELL_WIDGET:
2503 /* unimplemented */
2504 break;
2505 default:
2506 break;
2509 clist_row->cell[column].type = GTK_CMCELL_EMPTY;
2511 /* Note that pixbuf and mask were already ref'ed by the caller
2513 switch (type)
2515 case GTK_CMCELL_TEXT:
2516 if (text)
2518 clist_row->cell[column].type = GTK_CMCELL_TEXT;
2519 GTK_CMCELL_TEXT (clist_row->cell[column])->text = g_strdup (text);
2521 break;
2522 case GTK_CMCELL_PIXBUF:
2523 if (pixbuf)
2525 clist_row->cell[column].type = GTK_CMCELL_PIXBUF;
2526 GTK_CMCELL_PIXBUF (clist_row->cell[column])->pixbuf = pixbuf;
2528 break;
2529 case GTK_CMCELL_PIXTEXT:
2530 if (text && pixbuf)
2532 clist_row->cell[column].type = GTK_CMCELL_PIXTEXT;
2533 GTK_CMCELL_PIXTEXT (clist_row->cell[column])->text = g_strdup (text);
2534 GTK_CMCELL_PIXTEXT (clist_row->cell[column])->spacing = spacing;
2535 GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf = pixbuf;
2537 break;
2538 default:
2539 break;
2542 if (clist->column[column].auto_resize &&
2543 !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
2544 column_auto_resize (clist, clist_row, column, requisition.width);
2546 g_free (old_text);
2547 if (old_pixbuf)
2548 g_object_unref (old_pixbuf);
2551 PangoLayout *
2552 _gtk_cmclist_create_cell_layout (GtkCMCList *clist,
2553 GtkCMCListRow *clist_row,
2554 gint column)
2556 PangoLayout *layout;
2557 GtkStyle *style;
2558 GtkCMCell *cell;
2559 gchar *text;
2561 get_cell_style (clist, clist_row, GTK_STATE_NORMAL, column, &style);
2564 cell = &clist_row->cell[column];
2565 switch (cell->type)
2567 case GTK_CMCELL_TEXT:
2568 case GTK_CMCELL_PIXTEXT:
2569 text = ((cell->type == GTK_CMCELL_PIXTEXT) ?
2570 GTK_CMCELL_PIXTEXT (*cell)->text :
2571 GTK_CMCELL_TEXT (*cell)->text);
2573 if (!text)
2574 return NULL;
2576 layout = gtk_widget_create_pango_layout (GTK_WIDGET (clist),
2577 ((cell->type == GTK_CMCELL_PIXTEXT) ?
2578 GTK_CMCELL_PIXTEXT (*cell)->text :
2579 GTK_CMCELL_TEXT (*cell)->text));
2580 pango_layout_set_font_description (layout, style->font_desc);
2582 return layout;
2584 default:
2585 return NULL;
2589 static void
2590 cell_size_request (GtkCMCList *clist,
2591 GtkCMCListRow *clist_row,
2592 gint column,
2593 GtkRequisition *requisition)
2595 gint width;
2596 gint height;
2597 PangoLayout *layout;
2598 PangoRectangle logical_rect;
2600 cm_return_if_fail (GTK_IS_CMCLIST (clist));
2601 cm_return_if_fail (requisition != NULL);
2603 layout = _gtk_cmclist_create_cell_layout (clist, clist_row, column);
2604 if (layout)
2606 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2608 requisition->width = logical_rect.width;
2609 requisition->height = logical_rect.height;
2611 g_object_unref (G_OBJECT (layout));
2613 else
2615 requisition->width = 0;
2616 requisition->height = 0;
2619 if (layout && clist_row->cell[column].type == GTK_CMCELL_PIXTEXT)
2620 requisition->width += GTK_CMCELL_PIXTEXT (clist_row->cell[column])->spacing;
2622 switch (clist_row->cell[column].type)
2624 case GTK_CMCELL_PIXTEXT:
2625 width = gdk_pixbuf_get_width(GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf);
2626 height = gdk_pixbuf_get_height(GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf);
2627 requisition->width += width;
2628 requisition->height = MAX (requisition->height, height);
2629 break;
2630 case GTK_CMCELL_PIXBUF:
2631 width = gdk_pixbuf_get_width(GTK_CMCELL_PIXBUF (clist_row->cell[column])->pixbuf);
2632 height = gdk_pixbuf_get_height(GTK_CMCELL_PIXBUF (clist_row->cell[column])->pixbuf);
2633 requisition->width += width;
2634 requisition->height = MAX (requisition->height, height);
2635 break;
2637 default:
2638 break;
2641 requisition->width += clist_row->cell[column].horizontal;
2642 requisition->height += clist_row->cell[column].vertical;
2645 /* PUBLIC INSERT/REMOVE ROW FUNCTIONS
2646 * gtk_cmclist_prepend
2647 * gtk_cmclist_append
2648 * gtk_cmclist_insert
2649 * gtk_cmclist_remove
2650 * gtk_cmclist_clear
2652 gint
2653 gtk_cmclist_prepend (GtkCMCList *clist,
2654 gchar *text[])
2656 cm_return_val_if_fail (GTK_IS_CMCLIST (clist), -1);
2657 cm_return_val_if_fail (text != NULL, -1);
2659 return GTK_CMCLIST_GET_CLASS (clist)->insert_row (clist, 0, text);
2662 gint
2663 gtk_cmclist_append (GtkCMCList *clist,
2664 gchar *text[])
2666 cm_return_val_if_fail (GTK_IS_CMCLIST (clist), -1);
2667 cm_return_val_if_fail (text != NULL, -1);
2669 return GTK_CMCLIST_GET_CLASS (clist)->insert_row (clist, clist->rows, text);
2672 gint
2673 gtk_cmclist_insert (GtkCMCList *clist,
2674 gint row,
2675 gchar *text[])
2677 cm_return_val_if_fail (GTK_IS_CMCLIST (clist), -1);
2678 cm_return_val_if_fail (text != NULL, -1);
2680 if (row < 0 || row > clist->rows)
2681 row = clist->rows;
2683 return GTK_CMCLIST_GET_CLASS (clist)->insert_row (clist, row, text);
2686 void
2687 gtk_cmclist_remove (GtkCMCList *clist,
2688 gint row)
2690 GTK_CMCLIST_GET_CLASS (clist)->remove_row (clist, row);
2693 void
2694 gtk_cmclist_clear (GtkCMCList *clist)
2696 cm_return_if_fail (GTK_IS_CMCLIST (clist));
2698 GTK_CMCLIST_GET_CLASS (clist)->clear (clist);
2701 /* PRIVATE INSERT/REMOVE ROW FUNCTIONS
2702 * real_insert_row
2703 * real_remove_row
2704 * real_clear
2705 * real_row_move
2707 static gint
2708 real_insert_row (GtkCMCList *clist,
2709 gint row,
2710 gchar *text[])
2712 gint i;
2713 GtkCMCListRow *clist_row;
2715 cm_return_val_if_fail (GTK_IS_CMCLIST (clist), -1);
2716 cm_return_val_if_fail (text != NULL, -1);
2718 /* return if out of bounds */
2719 if (row < 0 || row > clist->rows)
2720 return -1;
2722 /* create the row */
2723 clist_row = row_new (clist);
2725 /* set the text in the row's columns */
2726 for (i = 0; i < clist->columns; i++)
2727 if (text[i])
2728 GTK_CMCLIST_GET_CLASS (clist)->set_cell_contents
2729 (clist, clist_row, i, GTK_CMCELL_TEXT, text[i], 0, NULL);
2731 if (!clist->rows)
2733 clist->row_list = g_list_append (clist->row_list, clist_row);
2734 clist->row_list_end = clist->row_list;
2736 else
2738 if (GTK_CMCLIST_AUTO_SORT(clist)) /* override insertion pos */
2740 GList *work;
2742 row = 0;
2743 work = clist->row_list;
2745 if (clist->sort_type == GTK_SORT_ASCENDING)
2747 while (row < clist->rows &&
2748 clist->compare (clist, clist_row,
2749 GTK_CMCLIST_ROW (work)) > 0)
2751 row++;
2752 work = work->next;
2755 else
2757 while (row < clist->rows &&
2758 clist->compare (clist, clist_row,
2759 GTK_CMCLIST_ROW (work)) < 0)
2761 row++;
2762 work = work->next;
2767 /* reset the row end pointer if we're inserting at the end of the list */
2768 if (row == clist->rows)
2769 clist->row_list_end = (g_list_append (clist->row_list_end,
2770 clist_row))->next;
2771 else
2772 clist->row_list = g_list_insert (clist->row_list, clist_row, row);
2775 clist->rows++;
2777 if (row < ROW_FROM_YPIXEL (clist, 0))
2778 clist->voffset -= (clist->row_height + CELL_SPACING);
2780 /* syncronize the selection list */
2781 sync_selection (clist, row, SYNC_INSERT);
2783 if (clist->rows == 1)
2785 clist->focus_row = 0;
2786 if (clist->selection_mode == GTK_SELECTION_BROWSE)
2787 gtk_cmclist_select_row (clist, 0, -1);
2790 /* redraw the list if it isn't frozen */
2791 if (CLIST_UNFROZEN (clist))
2793 adjust_adjustments (clist, FALSE);
2795 if (gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
2796 draw_rows (clist, NULL);
2799 return row;
2802 static void
2803 real_remove_row (GtkCMCList *clist,
2804 gint row)
2806 gint was_visible;
2807 GList *list;
2808 GtkCMCListRow *clist_row;
2810 cm_return_if_fail (GTK_IS_CMCLIST (clist));
2812 /* return if out of bounds */
2813 if (row < 0 || row > (clist->rows - 1))
2814 return;
2816 was_visible = (gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE);
2818 /* get the row we're going to delete */
2819 list = ROW_ELEMENT (clist, row);
2820 g_assert (list != NULL);
2821 clist_row = list->data;
2823 /* if we're removing a selected row, we have to make sure
2824 * it's properly unselected, and then sync up the clist->selected
2825 * list to reflect the deincrimented indexies of rows after the
2826 * removal */
2827 if (clist_row->state == GTK_STATE_SELECTED)
2828 g_signal_emit (G_OBJECT (clist), clist_signals[UNSELECT_ROW], 0,
2829 row, -1, NULL);
2831 sync_selection (clist, row, SYNC_REMOVE);
2833 /* reset the row end pointer if we're removing at the end of the list */
2834 clist->rows--;
2835 if (clist->row_list == list)
2836 clist->row_list = g_list_next (list);
2837 if (clist->row_list_end == list)
2838 clist->row_list_end = g_list_previous (list);
2839 list = g_list_remove (list, clist_row);
2841 if (row < ROW_FROM_YPIXEL (clist, 0))
2842 clist->voffset += clist->row_height + CELL_SPACING;
2844 if (clist->selection_mode == GTK_SELECTION_BROWSE && !clist->selection &&
2845 clist->focus_row >= 0)
2846 g_signal_emit (G_OBJECT (clist), clist_signals[SELECT_ROW], 0,
2847 clist->focus_row, -1, NULL);
2849 /* toast the row */
2850 row_delete (clist, clist_row);
2852 /* redraw the row if it isn't frozen */
2853 if (CLIST_UNFROZEN (clist))
2855 adjust_adjustments (clist, FALSE);
2857 if (was_visible)
2858 draw_rows (clist, NULL);
2862 static void
2863 real_clear (GtkCMCList *clist)
2865 GList *list;
2866 GList *free_list;
2867 GtkRequisition requisition;
2868 gint i;
2870 cm_return_if_fail (GTK_IS_CMCLIST (clist));
2872 /* free up the selection list */
2873 g_list_free (clist->selection);
2874 g_list_free (clist->undo_selection);
2875 g_list_free (clist->undo_unselection);
2877 clist->selection = NULL;
2878 clist->selection_end = NULL;
2879 clist->undo_selection = NULL;
2880 clist->undo_unselection = NULL;
2881 clist->voffset = 0;
2882 clist->focus_row = -1;
2883 clist->anchor = -1;
2884 clist->undo_anchor = -1;
2885 clist->anchor_state = GTK_STATE_SELECTED;
2886 clist->drag_pos = -1;
2888 /* remove all the rows */
2889 GTK_CMCLIST_SET_FLAG (clist, CMCLIST_AUTO_RESIZE_BLOCKED);
2890 free_list = clist->row_list;
2891 clist->row_list = NULL;
2892 clist->row_list_end = NULL;
2893 clist->rows = 0;
2894 for (list = free_list; list; list = list->next)
2895 row_delete (clist, GTK_CMCLIST_ROW (list));
2896 g_list_free (free_list);
2897 GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_AUTO_RESIZE_BLOCKED);
2898 for (i = 0; i < clist->columns; i++)
2899 if (clist->column[i].auto_resize)
2901 if (GTK_CMCLIST_SHOW_TITLES(clist) && clist->column[i].button)
2903 gtk_widget_get_requisition (clist->column[i].button, &requisition);
2904 gtk_cmclist_set_column_width
2905 (clist, i, (requisition.width -
2906 (CELL_SPACING + (2 * COLUMN_INSET))));
2908 else
2909 gtk_cmclist_set_column_width (clist, i, 0);
2911 /* zero-out the scrollbars */
2912 if (clist->vadjustment)
2914 gtk_adjustment_set_value (clist->vadjustment, 0.0);
2915 CLIST_REFRESH (clist);
2917 else
2918 gtk_widget_queue_resize (GTK_WIDGET (clist));
2921 static void
2922 real_row_move (GtkCMCList *clist,
2923 gint source_row,
2924 gint dest_row)
2926 GtkCMCListRow *clist_row;
2927 GList *list;
2928 gint first, last;
2929 gint d;
2931 cm_return_if_fail (GTK_IS_CMCLIST (clist));
2933 if (GTK_CMCLIST_AUTO_SORT(clist))
2934 return;
2936 if (source_row < 0 || source_row >= clist->rows ||
2937 dest_row < 0 || dest_row >= clist->rows ||
2938 source_row == dest_row)
2939 return;
2941 gtk_cmclist_freeze (clist);
2943 /* unlink source row */
2944 clist_row = ROW_ELEMENT (clist, source_row)->data;
2945 if (source_row == clist->rows - 1)
2946 clist->row_list_end = clist->row_list_end->prev;
2947 clist->row_list = g_list_remove (clist->row_list, clist_row);
2948 clist->rows--;
2950 /* relink source row */
2951 clist->row_list = g_list_insert (clist->row_list, clist_row, dest_row);
2952 if (dest_row == clist->rows)
2953 clist->row_list_end = clist->row_list_end->next;
2954 clist->rows++;
2956 /* sync selection */
2957 if (source_row > dest_row)
2959 first = dest_row;
2960 last = source_row;
2961 d = 1;
2963 else
2965 first = source_row;
2966 last = dest_row;
2967 d = -1;
2970 for (list = clist->selection; list; list = list->next)
2972 if (list->data == GINT_TO_POINTER (source_row))
2973 list->data = GINT_TO_POINTER (dest_row);
2974 else if (first <= GPOINTER_TO_INT (list->data) &&
2975 last >= GPOINTER_TO_INT (list->data))
2976 list->data = GINT_TO_POINTER (GPOINTER_TO_INT (list->data) + d);
2979 if (clist->focus_row == source_row)
2980 clist->focus_row = dest_row;
2981 else if (clist->focus_row > first)
2982 clist->focus_row += d;
2984 gtk_cmclist_thaw (clist);
2987 /* PUBLIC ROW FUNCTIONS
2988 * gtk_cmclist_moveto
2989 * gtk_cmclist_set_row_height
2990 * gtk_cmclist_set_row_data
2991 * gtk_cmclist_set_row_data_full
2992 * gtk_cmclist_get_row_data
2993 * gtk_cmclist_find_row_from_data
2994 * gtk_cmclist_swap_rows
2995 * gtk_cmclist_row_move
2996 * gtk_cmclist_row_is_visible
2997 * gtk_cmclist_set_foreground
2998 * gtk_cmclist_set_background
3000 void
3001 gtk_cmclist_moveto (GtkCMCList *clist,
3002 gint row,
3003 gint column,
3004 gfloat row_align,
3005 gfloat col_align)
3007 cm_return_if_fail (GTK_IS_CMCLIST (clist));
3009 if (row < -1 || row >= clist->rows)
3010 return;
3011 if (column < -1 || column >= clist->columns)
3012 return;
3014 row_align = CLAMP (row_align, 0, 1);
3015 col_align = CLAMP (col_align, 0, 1);
3017 /* adjust horizontal scrollbar */
3018 if (clist->hadjustment && column >= 0)
3020 gint x;
3022 x = (COLUMN_LEFT (clist, column) - CELL_SPACING - COLUMN_INSET -
3023 (col_align * (clist->clist_window_width - 2 * COLUMN_INSET -
3024 CELL_SPACING - clist->column[column].area.width)));
3025 if (x < 0)
3026 gtk_adjustment_set_value (clist->hadjustment, 0.0);
3027 else if (x > LIST_WIDTH (clist) - clist->clist_window_width)
3028 gtk_adjustment_set_value
3029 (clist->hadjustment, LIST_WIDTH (clist) - clist->clist_window_width);
3030 else
3031 gtk_adjustment_set_value (clist->hadjustment, x);
3034 /* adjust vertical scrollbar */
3035 if (clist->vadjustment && row >= 0)
3036 move_vertical (clist, row, row_align);
3039 void
3040 gtk_cmclist_set_row_height (GtkCMCList *clist,
3041 guint height)
3043 GtkStyle *style;
3044 GtkWidget *widget;
3046 cm_return_if_fail (GTK_IS_CMCLIST (clist));
3048 widget = GTK_WIDGET (clist);
3050 style = gtk_widget_get_style (widget);
3052 if (height > 0)
3054 clist->row_height = height;
3055 GTK_CMCLIST_SET_FLAG (clist, CMCLIST_ROW_HEIGHT_SET);
3057 else
3059 GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_ROW_HEIGHT_SET);
3060 clist->row_height = 0;
3063 if (style->font_desc)
3065 PangoContext *context = gtk_widget_get_pango_context (widget);
3066 PangoFontMetrics *metrics;
3068 metrics = pango_context_get_metrics (context,
3069 style->font_desc,
3070 pango_context_get_language (context));
3072 if (!GTK_CMCLIST_ROW_HEIGHT_SET(clist))
3074 clist->row_height = (pango_font_metrics_get_ascent (metrics) +
3075 pango_font_metrics_get_descent (metrics));
3076 clist->row_height = PANGO_PIXELS (clist->row_height) + 1;
3079 pango_font_metrics_unref (metrics);
3082 CLIST_REFRESH (clist);
3085 void
3086 gtk_cmclist_set_row_data (GtkCMCList *clist,
3087 gint row,
3088 gpointer data)
3090 gtk_cmclist_set_row_data_full (clist, row, data, NULL);
3093 void
3094 gtk_cmclist_set_row_data_full (GtkCMCList *clist,
3095 gint row,
3096 gpointer data,
3097 GDestroyNotify destroy)
3099 GtkCMCListRow *clist_row;
3101 cm_return_if_fail (GTK_IS_CMCLIST (clist));
3103 if (row < 0 || row > (clist->rows - 1))
3104 return;
3106 clist_row = ROW_ELEMENT (clist, row)->data;
3108 if (clist_row->destroy)
3109 clist_row->destroy (clist_row->data);
3111 clist_row->data = data;
3112 clist_row->destroy = destroy;
3115 gpointer
3116 gtk_cmclist_get_row_data (GtkCMCList *clist,
3117 gint row)
3119 GtkCMCListRow *clist_row;
3121 cm_return_val_if_fail (GTK_IS_CMCLIST (clist), NULL);
3123 if (row < 0 || row > (clist->rows - 1))
3124 return NULL;
3126 clist_row = ROW_ELEMENT (clist, row)->data;
3127 return clist_row->data;
3130 gint
3131 gtk_cmclist_find_row_from_data (GtkCMCList *clist,
3132 gpointer data)
3134 GList *list;
3135 gint n;
3137 cm_return_val_if_fail (GTK_IS_CMCLIST (clist), -1);
3139 for (n = 0, list = clist->row_list; list; n++, list = list->next)
3140 if (GTK_CMCLIST_ROW (list)->data == data)
3141 return n;
3143 return -1;
3146 void
3147 gtk_cmclist_swap_rows (GtkCMCList *clist,
3148 gint row1,
3149 gint row2)
3151 gint first, last;
3153 cm_return_if_fail (GTK_IS_CMCLIST (clist));
3154 cm_return_if_fail (row1 != row2);
3156 if (GTK_CMCLIST_AUTO_SORT(clist))
3157 return;
3159 gtk_cmclist_freeze (clist);
3161 first = MIN (row1, row2);
3162 last = MAX (row1, row2);
3164 gtk_cmclist_row_move (clist, last, first);
3165 gtk_cmclist_row_move (clist, first + 1, last);
3167 gtk_cmclist_thaw (clist);
3170 void
3171 gtk_cmclist_row_move (GtkCMCList *clist,
3172 gint source_row,
3173 gint dest_row)
3175 cm_return_if_fail (GTK_IS_CMCLIST (clist));
3177 if (GTK_CMCLIST_AUTO_SORT(clist))
3178 return;
3180 if (source_row < 0 || source_row >= clist->rows ||
3181 dest_row < 0 || dest_row >= clist->rows ||
3182 source_row == dest_row)
3183 return;
3185 g_signal_emit (G_OBJECT (clist), clist_signals[ROW_MOVE], 0,
3186 source_row, dest_row);
3189 GtkVisibility
3190 gtk_cmclist_row_is_visible (GtkCMCList *clist,
3191 gint row)
3193 gint top;
3195 cm_return_val_if_fail (GTK_IS_CMCLIST (clist), 0);
3197 if (row < 0 || row >= clist->rows)
3198 return GTK_VISIBILITY_NONE;
3200 if (clist->row_height == 0)
3201 return GTK_VISIBILITY_NONE;
3203 if (row < ROW_FROM_YPIXEL (clist, 0))
3204 return GTK_VISIBILITY_NONE;
3206 if (row > ROW_FROM_YPIXEL (clist, clist->clist_window_height))
3207 return GTK_VISIBILITY_NONE;
3209 top = ROW_TOP_YPIXEL (clist, row);
3211 if ((top < 0)
3212 || ((top + clist->row_height) >= clist->clist_window_height))
3213 return GTK_VISIBILITY_PARTIAL;
3215 return GTK_VISIBILITY_FULL;
3218 gboolean
3219 gtk_cmclist_row_is_above_viewport (GtkCMCList *clist,
3220 gint row)
3222 cm_return_val_if_fail(GTK_IS_CMCLIST (clist), 0);
3224 if (row < 0 || row >= clist->rows)
3225 return FALSE;
3227 if (clist->row_height == 0)
3228 return FALSE;
3230 if (row < ROW_FROM_YPIXEL (clist, 0))
3231 return TRUE;
3233 return FALSE;
3236 gboolean
3237 gtk_cmclist_row_is_below_viewport (GtkCMCList *clist,
3238 gint row)
3240 cm_return_val_if_fail(GTK_IS_CMCLIST (clist), 0);
3242 if (row < 0 || row >= clist->rows)
3243 return FALSE;
3245 if (clist->row_height == 0)
3246 return FALSE;
3248 if (row > ROW_FROM_YPIXEL (clist, clist->clist_window_height))
3249 return TRUE;
3251 return FALSE;
3254 void
3255 gtk_cmclist_set_foreground (GtkCMCList *clist,
3256 gint row,
3257 const GdkColor *color)
3259 GtkCMCListRow *clist_row;
3261 cm_return_if_fail (GTK_IS_CMCLIST (clist));
3263 if (row < 0 || row >= clist->rows)
3264 return;
3266 clist_row = ROW_ELEMENT (clist, row)->data;
3268 if (color)
3270 clist_row->foreground = *color;
3271 clist_row->fg_set = TRUE;
3273 else
3274 clist_row->fg_set = FALSE;
3276 if (CLIST_UNFROZEN (clist) && gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
3277 GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
3280 void
3281 gtk_cmclist_set_background (GtkCMCList *clist,
3282 gint row,
3283 const GdkColor *color)
3285 GtkCMCListRow *clist_row;
3287 cm_return_if_fail (GTK_IS_CMCLIST (clist));
3289 if (row < 0 || row >= clist->rows)
3290 return;
3292 clist_row = ROW_ELEMENT (clist, row)->data;
3294 if (color)
3296 clist_row->background = *color;
3297 clist_row->bg_set = TRUE;
3299 else
3300 clist_row->bg_set = FALSE;
3302 if (CLIST_UNFROZEN (clist)
3303 && (gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE))
3304 GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
3307 /* PUBLIC ROW/CELL STYLE FUNCTIONS
3308 * gtk_cmclist_set_cell_style
3309 * gtk_cmclist_get_cell_style
3310 * gtk_cmclist_set_row_style
3311 * gtk_cmclist_get_row_style
3313 void
3314 gtk_cmclist_set_cell_style (GtkCMCList *clist,
3315 gint row,
3316 gint column,
3317 GtkStyle *style)
3319 GtkRequisition requisition = { 0 };
3320 GtkCMCListRow *clist_row;
3322 cm_return_if_fail (GTK_IS_CMCLIST (clist));
3324 if (row < 0 || row >= clist->rows)
3325 return;
3326 if (column < 0 || column >= clist->columns)
3327 return;
3329 clist_row = ROW_ELEMENT (clist, row)->data;
3331 if (clist_row->cell[column].style == style)
3332 return;
3334 if (clist->column[column].auto_resize &&
3335 !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
3336 GTK_CMCLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row,
3337 column, &requisition);
3339 if (clist_row->cell[column].style)
3341 if (gtk_widget_get_realized (GTK_WIDGET(clist)))
3342 gtk_style_detach (clist_row->cell[column].style);
3343 g_object_unref (clist_row->cell[column].style);
3346 clist_row->cell[column].style = style;
3348 if (clist_row->cell[column].style)
3350 g_object_ref (clist_row->cell[column].style);
3352 if (gtk_widget_get_realized (GTK_WIDGET(clist)))
3353 clist_row->cell[column].style =
3354 gtk_style_attach (clist_row->cell[column].style,
3355 clist->clist_window);
3358 column_auto_resize (clist, clist_row, column, requisition.width);
3360 /* redraw the list if it's not frozen */
3361 if (CLIST_UNFROZEN (clist))
3363 if (gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
3364 GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
3368 GtkStyle *
3369 gtk_cmclist_get_cell_style (GtkCMCList *clist,
3370 gint row,
3371 gint column)
3373 GtkCMCListRow *clist_row;
3375 cm_return_val_if_fail (GTK_IS_CMCLIST (clist), NULL);
3377 if (row < 0 || row >= clist->rows || column < 0 || column >= clist->columns)
3378 return NULL;
3380 clist_row = ROW_ELEMENT (clist, row)->data;
3382 return clist_row->cell[column].style;
3385 void
3386 gtk_cmclist_set_row_style (GtkCMCList *clist,
3387 gint row,
3388 GtkStyle *style)
3390 GtkRequisition requisition;
3391 GtkCMCListRow *clist_row;
3392 gint *old_width;
3393 gint i;
3395 cm_return_if_fail (GTK_IS_CMCLIST (clist));
3397 if (row < 0 || row >= clist->rows)
3398 return;
3400 clist_row = ROW_ELEMENT (clist, row)->data;
3402 if (clist_row->style == style)
3403 return;
3405 old_width = g_new (gint, clist->columns);
3407 if (!GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
3409 for (i = 0; i < clist->columns; i++)
3410 if (clist->column[i].auto_resize)
3412 GTK_CMCLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row,
3413 i, &requisition);
3414 old_width[i] = requisition.width;
3418 if (clist_row->style)
3420 if (gtk_widget_get_realized (GTK_WIDGET(clist)))
3421 gtk_style_detach (clist_row->style);
3422 g_object_unref (clist_row->style);
3425 clist_row->style = style;
3427 if (clist_row->style)
3429 g_object_ref (clist_row->style);
3431 if (gtk_widget_get_realized (GTK_WIDGET(clist)))
3432 clist_row->style = gtk_style_attach (clist_row->style,
3433 clist->clist_window);
3436 if (GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
3437 for (i = 0; i < clist->columns; i++)
3438 column_auto_resize (clist, clist_row, i, old_width[i]);
3440 g_free (old_width);
3442 /* redraw the list if it's not frozen */
3443 if (CLIST_UNFROZEN (clist))
3445 if (gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
3446 GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
3450 GtkStyle *
3451 gtk_cmclist_get_row_style (GtkCMCList *clist,
3452 gint row)
3454 GtkCMCListRow *clist_row;
3456 cm_return_val_if_fail (GTK_IS_CMCLIST (clist), NULL);
3458 if (row < 0 || row >= clist->rows)
3459 return NULL;
3461 clist_row = ROW_ELEMENT (clist, row)->data;
3463 return clist_row->style;
3466 /* PUBLIC SELECTION FUNCTIONS
3467 * gtk_cmclist_set_selectable
3468 * gtk_cmclist_get_selectable
3469 * gtk_cmclist_select_row
3470 * gtk_cmclist_unselect_row
3471 * gtk_cmclist_select_all
3472 * gtk_cmclist_unselect_all
3473 * gtk_cmclist_undo_selection
3475 void
3476 gtk_cmclist_set_selectable (GtkCMCList *clist,
3477 gint row,
3478 gboolean selectable)
3480 GtkCMCListRow *clist_row;
3482 cm_return_if_fail (GTK_IS_CMCLIST (clist));
3484 if (row < 0 || row >= clist->rows)
3485 return;
3487 clist_row = ROW_ELEMENT (clist, row)->data;
3489 if (selectable == clist_row->selectable)
3490 return;
3492 clist_row->selectable = selectable;
3494 if (!selectable && clist_row->state == GTK_STATE_SELECTED)
3496 if (clist->anchor >= 0 &&
3497 clist->selection_mode == GTK_SELECTION_MULTIPLE)
3499 clist->drag_button = 0;
3500 remove_grab (clist);
3501 GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
3503 g_signal_emit (G_OBJECT (clist), clist_signals[UNSELECT_ROW], 0,
3504 row, -1, NULL);
3508 gboolean
3509 gtk_cmclist_get_selectable (GtkCMCList *clist,
3510 gint row)
3512 cm_return_val_if_fail (GTK_IS_CMCLIST (clist), FALSE);
3514 if (row < 0 || row >= clist->rows)
3515 return FALSE;
3517 return GTK_CMCLIST_ROW (ROW_ELEMENT (clist, row))->selectable;
3520 void
3521 gtk_cmclist_select_row (GtkCMCList *clist,
3522 gint row,
3523 gint column)
3525 cm_return_if_fail (GTK_IS_CMCLIST (clist));
3527 if (row < 0 || row >= clist->rows)
3528 return;
3529 if (column < -1 || column >= clist->columns)
3530 return;
3532 g_signal_emit (G_OBJECT (clist), clist_signals[SELECT_ROW], 0,
3533 row, column, NULL);
3536 void
3537 gtk_cmclist_unselect_row (GtkCMCList *clist,
3538 gint row,
3539 gint column)
3541 cm_return_if_fail (GTK_IS_CMCLIST (clist));
3543 if (row < 0 || row >= clist->rows)
3544 return;
3545 if (column < -1 || column >= clist->columns)
3546 return;
3548 g_signal_emit (G_OBJECT (clist), clist_signals[UNSELECT_ROW], 0,
3549 row, column, NULL);
3552 void
3553 gtk_cmclist_select_all (GtkCMCList *clist)
3555 cm_return_if_fail (GTK_IS_CMCLIST (clist));
3557 GTK_CMCLIST_GET_CLASS (clist)->select_all (clist);
3560 void
3561 gtk_cmclist_unselect_all (GtkCMCList *clist)
3563 cm_return_if_fail (GTK_IS_CMCLIST (clist));
3565 GTK_CMCLIST_GET_CLASS (clist)->unselect_all (clist);
3568 void
3569 gtk_cmclist_undo_selection (GtkCMCList *clist)
3571 cm_return_if_fail (GTK_IS_CMCLIST (clist));
3573 if (clist->selection_mode == GTK_SELECTION_MULTIPLE &&
3574 (clist->undo_selection || clist->undo_unselection))
3575 g_signal_emit (G_OBJECT (clist), clist_signals[UNDO_SELECTION], 0);
3578 /* PRIVATE SELECTION FUNCTIONS
3579 * selection_find
3580 * toggle_row
3581 * fake_toggle_row
3582 * toggle_focus_row
3583 * toggle_add_mode
3584 * real_select_row
3585 * real_unselect_row
3586 * real_select_all
3587 * real_unselect_all
3588 * fake_unselect_all
3589 * real_undo_selection
3590 * set_anchor
3591 * resync_selection
3592 * update_extended_selection
3593 * start_selection
3594 * end_selection
3595 * extend_selection
3596 * sync_selection
3598 static GList *
3599 selection_find (GtkCMCList *clist,
3600 gint row_number,
3601 GList *row_list_element)
3603 return g_list_find (clist->selection, GINT_TO_POINTER (row_number));
3606 static void
3607 toggle_row (GtkCMCList *clist,
3608 gint row,
3609 gint column,
3610 GdkEvent *event)
3612 GtkCMCListRow *clist_row;
3614 switch (clist->selection_mode)
3616 case GTK_SELECTION_MULTIPLE:
3617 case GTK_SELECTION_SINGLE:
3618 clist_row = ROW_ELEMENT (clist, row)->data;
3620 if (!clist_row)
3621 return;
3623 if (clist_row->state == GTK_STATE_SELECTED)
3625 g_signal_emit (G_OBJECT (clist), clist_signals[UNSELECT_ROW], 0,
3626 row, column, event);
3627 return;
3629 break;
3630 case GTK_SELECTION_BROWSE:
3631 g_signal_emit (G_OBJECT (clist), clist_signals[SELECT_ROW], 0,
3632 row, column, event);
3633 break;
3634 default:
3635 g_assert_not_reached ();
3639 static void
3640 fake_toggle_row (GtkCMCList *clist,
3641 gint row)
3643 GList *work;
3645 work = ROW_ELEMENT (clist, row);
3647 if (!work || !GTK_CMCLIST_ROW (work)->selectable)
3648 return;
3650 if (GTK_CMCLIST_ROW (work)->state == GTK_STATE_NORMAL)
3651 clist->anchor_state = GTK_CMCLIST_ROW (work)->state = GTK_STATE_SELECTED;
3652 else
3653 clist->anchor_state = GTK_CMCLIST_ROW (work)->state = GTK_STATE_NORMAL;
3655 if (CLIST_UNFROZEN (clist) &&
3656 gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
3657 GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row,
3658 GTK_CMCLIST_ROW (work));
3661 static gboolean
3662 clist_has_grab (GtkCMCList *clist)
3664 return (gtk_widget_has_grab (GTK_WIDGET(clist)) &&
3665 gtkut_pointer_is_grabbed(GTK_WIDGET(clist)));
3668 static void
3669 toggle_focus_row (GtkCMCList *clist)
3671 cm_return_if_fail (clist != 0);
3672 cm_return_if_fail (GTK_IS_CMCLIST (clist));
3674 if (clist_has_grab (clist) ||
3675 clist->focus_row < 0 || clist->focus_row >= clist->rows)
3676 return;
3678 switch (clist->selection_mode)
3680 case GTK_SELECTION_SINGLE:
3681 toggle_row (clist, clist->focus_row, 0, NULL);
3682 break;
3683 case GTK_SELECTION_MULTIPLE:
3684 g_list_free (clist->undo_selection);
3685 g_list_free (clist->undo_unselection);
3686 clist->undo_selection = NULL;
3687 clist->undo_unselection = NULL;
3689 clist->anchor = clist->focus_row;
3690 clist->drag_pos = clist->focus_row;
3691 clist->undo_anchor = clist->focus_row;
3693 if (GTK_CMCLIST_ADD_MODE(clist))
3694 fake_toggle_row (clist, clist->focus_row);
3695 else
3696 GTK_CMCLIST_GET_CLASS (clist)->fake_unselect_all (clist,clist->focus_row);
3698 GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
3699 break;
3700 default:
3701 break;
3705 static void
3706 toggle_add_mode (GtkCMCList *clist)
3708 cm_return_if_fail (clist != 0);
3709 cm_return_if_fail (GTK_IS_CMCLIST (clist));
3711 if (clist_has_grab (clist) ||
3712 clist->selection_mode != GTK_SELECTION_MULTIPLE)
3713 return;
3715 gtk_cmclist_undraw_focus (GTK_WIDGET (clist));
3716 if (!GTK_CMCLIST_ADD_MODE(clist))
3718 GTK_CMCLIST_SET_FLAG (clist, CMCLIST_ADD_MODE);
3720 else
3722 GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_ADD_MODE);
3723 clist->anchor_state = GTK_STATE_SELECTED;
3725 gtk_cmclist_draw_focus (GTK_WIDGET (clist));
3728 static void
3729 real_select_row (GtkCMCList *clist,
3730 gint row,
3731 gint column,
3732 GdkEvent *event)
3734 GtkCMCListRow *clist_row;
3735 GList *list;
3736 gint sel_row;
3737 gboolean row_selected;
3739 cm_return_if_fail (GTK_IS_CMCLIST (clist));
3741 if (row < 0 || row > (clist->rows - 1))
3742 return;
3744 switch (clist->selection_mode)
3746 case GTK_SELECTION_SINGLE:
3747 case GTK_SELECTION_BROWSE:
3749 row_selected = FALSE;
3750 list = clist->selection;
3752 while (list)
3754 sel_row = GPOINTER_TO_INT (list->data);
3755 list = list->next;
3757 if (row == sel_row)
3758 row_selected = TRUE;
3759 else
3760 g_signal_emit (G_OBJECT (clist), clist_signals[UNSELECT_ROW], 0,
3761 sel_row, column, event);
3764 if (row_selected)
3765 return;
3767 default:
3768 break;
3771 clist_row = ROW_ELEMENT (clist, row)->data;
3773 if (clist_row->state != GTK_STATE_NORMAL || !clist_row->selectable)
3774 return;
3776 clist_row->state = GTK_STATE_SELECTED;
3777 if (!clist->selection)
3779 clist->selection = g_list_append (clist->selection,
3780 GINT_TO_POINTER (row));
3781 clist->selection_end = clist->selection;
3783 else
3784 clist->selection_end =
3785 g_list_append (clist->selection_end, GINT_TO_POINTER (row))->next;
3787 if (CLIST_UNFROZEN (clist)
3788 && (gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE))
3789 GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
3792 static void
3793 real_unselect_row (GtkCMCList *clist,
3794 gint row,
3795 gint column,
3796 GdkEvent *event)
3798 GtkCMCListRow *clist_row;
3800 cm_return_if_fail (GTK_IS_CMCLIST (clist));
3802 if (row < 0 || row > (clist->rows - 1))
3803 return;
3805 clist_row = ROW_ELEMENT (clist, row)->data;
3807 if (clist_row->state == GTK_STATE_SELECTED)
3809 clist_row->state = GTK_STATE_NORMAL;
3811 if (clist->selection_end &&
3812 clist->selection_end->data == GINT_TO_POINTER (row))
3813 clist->selection_end = clist->selection_end->prev;
3815 clist->selection = g_list_remove (clist->selection,
3816 GINT_TO_POINTER (row));
3818 if (CLIST_UNFROZEN (clist)
3819 && (gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE))
3820 GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
3824 static void
3825 real_select_all (GtkCMCList *clist)
3827 cm_return_if_fail (GTK_IS_CMCLIST (clist));
3829 if (clist_has_grab (clist))
3830 return;
3832 switch (clist->selection_mode)
3834 case GTK_SELECTION_SINGLE:
3835 case GTK_SELECTION_BROWSE:
3836 return;
3838 case GTK_SELECTION_MULTIPLE:
3839 g_list_free (clist->undo_selection);
3840 g_list_free (clist->undo_unselection);
3841 clist->undo_selection = NULL;
3842 clist->undo_unselection = NULL;
3844 if (clist->rows &&
3845 ((GtkCMCListRow *) (clist->row_list->data))->state !=
3846 GTK_STATE_SELECTED)
3847 fake_toggle_row (clist, 0);
3849 clist->anchor_state = GTK_STATE_SELECTED;
3850 clist->anchor = 0;
3851 clist->drag_pos = 0;
3852 clist->undo_anchor = clist->focus_row;
3853 update_extended_selection (clist, clist->rows);
3854 GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
3855 return;
3856 default:
3857 g_assert_not_reached ();
3861 static void
3862 real_unselect_all (GtkCMCList *clist)
3864 GList *list;
3865 gint i;
3867 cm_return_if_fail (GTK_IS_CMCLIST (clist));
3869 if (clist_has_grab (clist))
3870 return;
3872 switch (clist->selection_mode)
3874 case GTK_SELECTION_BROWSE:
3875 if (clist->focus_row >= 0)
3877 g_signal_emit (G_OBJECT (clist),
3878 clist_signals[SELECT_ROW], 0,
3879 clist->focus_row, -1, NULL);
3880 return;
3882 break;
3883 case GTK_SELECTION_MULTIPLE:
3884 g_list_free (clist->undo_selection);
3885 g_list_free (clist->undo_unselection);
3886 clist->undo_selection = NULL;
3887 clist->undo_unselection = NULL;
3889 clist->anchor = -1;
3890 clist->drag_pos = -1;
3891 clist->undo_anchor = clist->focus_row;
3892 break;
3893 default:
3894 break;
3897 list = clist->selection;
3898 while (list)
3900 i = GPOINTER_TO_INT (list->data);
3901 list = list->next;
3902 g_signal_emit (G_OBJECT (clist),
3903 clist_signals[UNSELECT_ROW], 0, i, -1, NULL);
3907 static void
3908 fake_unselect_all (GtkCMCList *clist,
3909 gint row)
3911 GList *list;
3912 GList *work;
3913 gint i;
3915 if (row >= 0 && (work = ROW_ELEMENT (clist, row)))
3917 if (GTK_CMCLIST_ROW (work)->state == GTK_STATE_NORMAL &&
3918 GTK_CMCLIST_ROW (work)->selectable)
3920 GTK_CMCLIST_ROW (work)->state = GTK_STATE_SELECTED;
3922 if (CLIST_UNFROZEN (clist) &&
3923 gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
3924 GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row,
3925 GTK_CMCLIST_ROW (work));
3929 clist->undo_selection = clist->selection;
3930 clist->selection = NULL;
3931 clist->selection_end = NULL;
3933 for (list = clist->undo_selection; list; list = list->next)
3935 if ((i = GPOINTER_TO_INT (list->data)) == row ||
3936 !(work = g_list_nth (clist->row_list, i)))
3937 continue;
3939 GTK_CMCLIST_ROW (work)->state = GTK_STATE_NORMAL;
3940 if (CLIST_UNFROZEN (clist) &&
3941 gtk_cmclist_row_is_visible (clist, i) != GTK_VISIBILITY_NONE)
3942 GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, i,
3943 GTK_CMCLIST_ROW (work));
3947 static void
3948 real_undo_selection (GtkCMCList *clist)
3950 GList *work;
3952 cm_return_if_fail (GTK_IS_CMCLIST (clist));
3954 if (clist_has_grab (clist) ||
3955 clist->selection_mode != GTK_SELECTION_MULTIPLE)
3956 return;
3958 GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
3960 if (!(clist->undo_selection || clist->undo_unselection))
3962 gtk_cmclist_unselect_all (clist);
3963 return;
3966 for (work = clist->undo_selection; work; work = work->next)
3967 g_signal_emit (G_OBJECT (clist), clist_signals[SELECT_ROW], 0,
3968 GPOINTER_TO_INT (work->data), -1, NULL);
3970 for (work = clist->undo_unselection; work; work = work->next)
3972 /* g_print ("unselect %d\n",GPOINTER_TO_INT (work->data)); */
3973 g_signal_emit (G_OBJECT (clist), clist_signals[UNSELECT_ROW], 0,
3974 GPOINTER_TO_INT (work->data), -1, NULL);
3977 if (gtk_widget_has_focus(GTK_WIDGET(clist)) && clist->focus_row != clist->undo_anchor)
3979 gtk_cmclist_undraw_focus (GTK_WIDGET (clist));
3980 clist->focus_row = clist->undo_anchor;
3981 gtk_cmclist_draw_focus (GTK_WIDGET (clist));
3983 else
3984 clist->focus_row = clist->undo_anchor;
3986 clist->undo_anchor = -1;
3988 g_list_free (clist->undo_selection);
3989 g_list_free (clist->undo_unselection);
3990 clist->undo_selection = NULL;
3991 clist->undo_unselection = NULL;
3993 if (ROW_TOP_YPIXEL (clist, clist->focus_row) + clist->row_height >
3994 clist->clist_window_height)
3995 gtk_cmclist_moveto (clist, clist->focus_row, -1, 1, 0);
3996 else if (ROW_TOP_YPIXEL (clist, clist->focus_row) < 0)
3997 gtk_cmclist_moveto (clist, clist->focus_row, -1, 0, 0);
4000 static void
4001 set_anchor (GtkCMCList *clist,
4002 gboolean add_mode,
4003 gint anchor,
4004 gint undo_anchor)
4006 cm_return_if_fail (GTK_IS_CMCLIST (clist));
4008 if (clist->selection_mode != GTK_SELECTION_MULTIPLE || clist->anchor >= 0)
4009 return;
4011 g_list_free (clist->undo_selection);
4012 g_list_free (clist->undo_unselection);
4013 clist->undo_selection = NULL;
4014 clist->undo_unselection = NULL;
4016 if (add_mode)
4017 fake_toggle_row (clist, anchor);
4018 else
4020 GTK_CMCLIST_GET_CLASS (clist)->fake_unselect_all (clist, anchor);
4021 clist->anchor_state = GTK_STATE_SELECTED;
4024 clist->anchor = anchor;
4025 clist->drag_pos = anchor;
4026 clist->undo_anchor = undo_anchor;
4029 static void
4030 resync_selection (GtkCMCList *clist,
4031 GdkEvent *event)
4033 gint i;
4034 gint e;
4035 gint row;
4036 GList *list;
4037 GtkCMCListRow *clist_row;
4039 if (clist->selection_mode != GTK_SELECTION_MULTIPLE)
4040 return;
4042 if (clist->anchor < 0 || clist->drag_pos < 0)
4043 return;
4045 gtk_cmclist_freeze (clist);
4047 i = MIN (clist->anchor, clist->drag_pos);
4048 e = MAX (clist->anchor, clist->drag_pos);
4050 if (clist->undo_selection)
4052 list = clist->selection;
4053 clist->selection = clist->undo_selection;
4054 clist->selection_end = g_list_last (clist->selection);
4055 clist->undo_selection = list;
4056 list = clist->selection;
4057 while (list)
4059 row = GPOINTER_TO_INT (list->data);
4060 list = list->next;
4061 if (row < i || row > e)
4063 clist_row = g_list_nth (clist->row_list, row)->data;
4064 if (clist_row->selectable)
4066 clist_row->state = GTK_STATE_SELECTED;
4067 g_signal_emit (G_OBJECT (clist),
4068 clist_signals[UNSELECT_ROW], 0,
4069 row, -1, event);
4070 clist->undo_selection = g_list_prepend
4071 (clist->undo_selection, GINT_TO_POINTER (row));
4077 if (clist->anchor < clist->drag_pos)
4079 for (list = g_list_nth (clist->row_list, i); i <= e;
4080 i++, list = list->next)
4081 if (GTK_CMCLIST_ROW (list)->selectable)
4083 if (g_list_find (clist->selection, GINT_TO_POINTER(i)))
4085 if (GTK_CMCLIST_ROW (list)->state == GTK_STATE_NORMAL)
4087 GTK_CMCLIST_ROW (list)->state = GTK_STATE_SELECTED;
4088 g_signal_emit (G_OBJECT (clist),
4089 clist_signals[UNSELECT_ROW], 0,
4090 i, -1, event);
4091 clist->undo_selection =
4092 g_list_prepend (clist->undo_selection,
4093 GINT_TO_POINTER (i));
4096 else if (GTK_CMCLIST_ROW (list)->state == GTK_STATE_SELECTED)
4098 GTK_CMCLIST_ROW (list)->state = GTK_STATE_NORMAL;
4099 clist->undo_unselection =
4100 g_list_prepend (clist->undo_unselection,
4101 GINT_TO_POINTER (i));
4105 else
4107 for (list = g_list_nth (clist->row_list, e); i <= e;
4108 e--, list = list->prev)
4109 if (GTK_CMCLIST_ROW (list)->selectable)
4111 if (g_list_find (clist->selection, GINT_TO_POINTER(e)))
4113 if (GTK_CMCLIST_ROW (list)->state == GTK_STATE_NORMAL)
4115 GTK_CMCLIST_ROW (list)->state = GTK_STATE_SELECTED;
4116 g_signal_emit (G_OBJECT (clist),
4117 clist_signals[UNSELECT_ROW], 0,
4118 e, -1, event);
4119 clist->undo_selection =
4120 g_list_prepend (clist->undo_selection,
4121 GINT_TO_POINTER (e));
4124 else if (GTK_CMCLIST_ROW (list)->state == GTK_STATE_SELECTED)
4126 GTK_CMCLIST_ROW (list)->state = GTK_STATE_NORMAL;
4127 clist->undo_unselection =
4128 g_list_prepend (clist->undo_unselection,
4129 GINT_TO_POINTER (e));
4134 clist->undo_unselection = g_list_reverse (clist->undo_unselection);
4135 for (list = clist->undo_unselection; list; list = list->next)
4136 g_signal_emit (G_OBJECT (clist), clist_signals[SELECT_ROW], 0,
4137 GPOINTER_TO_INT (list->data), -1, event);
4139 clist->anchor = -1;
4140 clist->drag_pos = -1;
4142 gtk_cmclist_thaw (clist);
4145 static void
4146 update_extended_selection (GtkCMCList *clist,
4147 gint row)
4149 gint i;
4150 GList *list;
4151 GdkRectangle area;
4152 gint s1 = -1;
4153 gint s2 = -1;
4154 gint e1 = -1;
4155 gint e2 = -1;
4156 gint y1 = clist->clist_window_height;
4157 gint y2 = clist->clist_window_height;
4158 gint h1 = 0;
4159 gint h2 = 0;
4160 gint top;
4162 if (clist->selection_mode != GTK_SELECTION_MULTIPLE || clist->anchor == -1)
4163 return;
4165 if (row < 0)
4166 row = 0;
4167 if (row >= clist->rows)
4168 row = clist->rows - 1;
4170 /* extending downwards */
4171 if (row > clist->drag_pos && clist->anchor <= clist->drag_pos)
4173 s2 = clist->drag_pos + 1;
4174 e2 = row;
4176 /* extending upwards */
4177 else if (row < clist->drag_pos && clist->anchor >= clist->drag_pos)
4179 s2 = row;
4180 e2 = clist->drag_pos - 1;
4182 else if (row < clist->drag_pos && clist->anchor < clist->drag_pos)
4184 e1 = clist->drag_pos;
4185 /* row and drag_pos on different sides of anchor :
4186 take back the selection between anchor and drag_pos,
4187 select between anchor and row */
4188 if (row < clist->anchor)
4190 s1 = clist->anchor + 1;
4191 s2 = row;
4192 e2 = clist->anchor - 1;
4194 /* take back the selection between anchor and drag_pos */
4195 else
4196 s1 = row + 1;
4198 else if (row > clist->drag_pos && clist->anchor > clist->drag_pos)
4200 s1 = clist->drag_pos;
4201 /* row and drag_pos on different sides of anchor :
4202 take back the selection between anchor and drag_pos,
4203 select between anchor and row */
4204 if (row > clist->anchor)
4206 e1 = clist->anchor - 1;
4207 s2 = clist->anchor + 1;
4208 e2 = row;
4210 /* take back the selection between anchor and drag_pos */
4211 else
4212 e1 = row - 1;
4215 clist->drag_pos = row;
4217 area.x = 0;
4218 area.width = clist->clist_window_width;
4220 /* restore the elements between s1 and e1 */
4221 if (s1 >= 0)
4223 for (i = s1, list = g_list_nth (clist->row_list, i); i <= e1;
4224 i++, list = list->next)
4225 if (GTK_CMCLIST_ROW (list)->selectable)
4227 if (GTK_CMCLIST_GET_CLASS (clist)->selection_find (clist, i, list))
4228 GTK_CMCLIST_ROW (list)->state = GTK_STATE_SELECTED;
4229 else
4230 GTK_CMCLIST_ROW (list)->state = GTK_STATE_NORMAL;
4233 top = ROW_TOP_YPIXEL (clist, clist->focus_row);
4235 if (top + clist->row_height <= 0)
4237 area.y = 0;
4238 area.height = ROW_TOP_YPIXEL (clist, e1) + clist->row_height;
4239 draw_rows (clist, &area);
4240 gtk_cmclist_moveto (clist, clist->focus_row, -1, 0, 0);
4242 else if (top >= clist->clist_window_height)
4244 area.y = ROW_TOP_YPIXEL (clist, s1) - 1;
4245 area.height = clist->clist_window_height - area.y;
4246 draw_rows (clist, &area);
4247 gtk_cmclist_moveto (clist, clist->focus_row, -1, 1, 0);
4249 else if (top < 0)
4250 gtk_cmclist_moveto (clist, clist->focus_row, -1, 0, 0);
4251 else if (top + clist->row_height > clist->clist_window_height)
4252 gtk_cmclist_moveto (clist, clist->focus_row, -1, 1, 0);
4254 y1 = ROW_TOP_YPIXEL (clist, s1) - 1;
4255 h1 = (e1 - s1 + 1) * (clist->row_height + CELL_SPACING);
4258 /* extend the selection between s2 and e2 */
4259 if (s2 >= 0)
4261 for (i = s2, list = g_list_nth (clist->row_list, i); i <= e2;
4262 i++, list = list->next)
4263 if (GTK_CMCLIST_ROW (list)->selectable &&
4264 GTK_CMCLIST_ROW (list)->state != clist->anchor_state)
4265 GTK_CMCLIST_ROW (list)->state = clist->anchor_state;
4267 top = ROW_TOP_YPIXEL (clist, clist->focus_row);
4269 if (top + clist->row_height <= 0)
4271 area.y = 0;
4272 area.height = ROW_TOP_YPIXEL (clist, e2) + clist->row_height;
4273 draw_rows (clist, &area);
4274 gtk_cmclist_moveto (clist, clist->focus_row, -1, 0, 0);
4276 else if (top >= clist->clist_window_height)
4278 area.y = ROW_TOP_YPIXEL (clist, s2) - 1;
4279 area.height = clist->clist_window_height - area.y;
4280 draw_rows (clist, &area);
4281 gtk_cmclist_moveto (clist, clist->focus_row, -1, 1, 0);
4283 else if (top < 0)
4284 gtk_cmclist_moveto (clist, clist->focus_row, -1, 0, 0);
4285 else if (top + clist->row_height > clist->clist_window_height)
4286 gtk_cmclist_moveto (clist, clist->focus_row, -1, 1, 0);
4288 y2 = ROW_TOP_YPIXEL (clist, s2) - 1;
4289 h2 = (e2 - s2 + 1) * (clist->row_height + CELL_SPACING);
4292 area.y = MAX (0, MIN (y1, y2));
4293 if (area.y > clist->clist_window_height)
4294 area.y = 0;
4295 area.height = MIN (clist->clist_window_height, h1 + h2);
4296 if (s1 >= 0 && s2 >= 0)
4297 area.height += (clist->row_height + CELL_SPACING);
4298 draw_rows (clist, &area);
4301 static void
4302 start_selection (GtkCMCList *clist)
4304 cm_return_if_fail (GTK_IS_CMCLIST (clist));
4306 if (clist_has_grab (clist))
4307 return;
4309 set_anchor (clist, GTK_CMCLIST_ADD_MODE(clist), clist->focus_row,
4310 clist->focus_row);
4313 static void
4314 end_selection (GtkCMCList *clist)
4316 cm_return_if_fail (GTK_IS_CMCLIST (clist));
4318 if (gtkut_pointer_is_grabbed (GTK_WIDGET (clist)) &&
4319 gtk_widget_has_focus (GTK_WIDGET(clist)))
4320 return;
4322 GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
4325 static void
4326 extend_selection (GtkCMCList *clist,
4327 GtkScrollType scroll_type,
4328 gfloat position,
4329 gboolean auto_start_selection)
4331 cm_return_if_fail (GTK_IS_CMCLIST (clist));
4333 if (clist_has_grab (clist) ||
4334 clist->selection_mode != GTK_SELECTION_MULTIPLE)
4335 return;
4337 if (auto_start_selection)
4338 set_anchor (clist, GTK_CMCLIST_ADD_MODE(clist), clist->focus_row,
4339 clist->focus_row);
4340 else if (clist->anchor == -1)
4341 return;
4343 move_focus_row (clist, scroll_type, position);
4345 if (ROW_TOP_YPIXEL (clist, clist->focus_row) + clist->row_height >
4346 clist->clist_window_height)
4347 gtk_cmclist_moveto (clist, clist->focus_row, -1, 1, 0);
4348 else if (ROW_TOP_YPIXEL (clist, clist->focus_row) < 0)
4349 gtk_cmclist_moveto (clist, clist->focus_row, -1, 0, 0);
4351 update_extended_selection (clist, clist->focus_row);
4354 static void
4355 sync_selection (GtkCMCList *clist,
4356 gint row,
4357 gint mode)
4359 GList *list;
4360 gint d;
4362 if (mode == SYNC_INSERT)
4363 d = 1;
4364 else
4365 d = -1;
4367 if (clist->focus_row >= row)
4369 if (d > 0 || clist->focus_row > row)
4370 clist->focus_row += d;
4371 if (clist->focus_row == -1 && clist->rows >= 1)
4372 clist->focus_row = 0;
4373 else if (d < 0 && clist->focus_row >= clist->rows - 1)
4374 clist->focus_row = clist->rows - 2;
4375 else if (clist->focus_row >= clist->rows) /* Paranoia */
4376 clist->focus_row = clist->rows - 1;
4379 GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
4381 g_list_free (clist->undo_selection);
4382 g_list_free (clist->undo_unselection);
4383 clist->undo_selection = NULL;
4384 clist->undo_unselection = NULL;
4386 clist->anchor = -1;
4387 clist->drag_pos = -1;
4388 clist->undo_anchor = clist->focus_row;
4390 list = clist->selection;
4392 while (list)
4394 if (GPOINTER_TO_INT (list->data) >= row)
4395 list->data = ((gchar*) list->data) + d;
4396 list = list->next;
4400 /* GTKOBJECT
4401 * gtk_cmclist_destroy
4402 * gtk_cmclist_finalize
4404 static void gtk_cmclist_destroy (GtkWidget *object)
4407 gint i;
4408 GtkCMCList *clist;
4410 cm_return_if_fail (GTK_IS_CMCLIST (object));
4412 clist = GTK_CMCLIST (object);
4414 /* freeze the list */
4415 clist->freeze_count++;
4417 /* get rid of all the rows */
4418 gtk_cmclist_clear (clist);
4420 /* Since we don't have a _remove method, unparent the children
4421 * instead of destroying them so the focus will be unset properly.
4422 * (For other containers, the _remove method takes care of the
4423 * unparent) The destroy will happen when the refcount drops
4424 * to zero.
4427 /* unref adjustments */
4428 if (clist->hadjustment)
4430 g_signal_handlers_disconnect_matched(G_OBJECT (clist->hadjustment), G_SIGNAL_MATCH_DATA,
4431 0, 0, 0, 0, clist);
4432 g_object_unref (G_OBJECT (clist->hadjustment));
4433 clist->hadjustment = NULL;
4435 if (clist->vadjustment)
4437 g_signal_handlers_disconnect_matched(G_OBJECT (clist->vadjustment), G_SIGNAL_MATCH_DATA,
4438 0, 0, 0, 0, clist);
4439 g_object_unref (G_OBJECT (clist->vadjustment));
4440 clist->vadjustment = NULL;
4443 remove_grab (clist);
4445 /* destroy the column buttons */
4446 for (i = 0; i < clist->columns; i++)
4447 if (clist->column[i].button)
4449 gtk_widget_unparent (clist->column[i].button);
4450 clist->column[i].button = NULL;
4453 if (GTK_WIDGET_CLASS (gtk_cmclist_parent_class)->destroy)
4454 (*GTK_WIDGET_CLASS (gtk_cmclist_parent_class)->destroy) (object);
4457 static void
4458 gtk_cmclist_finalize (GObject *object)
4460 GtkCMCList *clist;
4462 cm_return_if_fail (GTK_IS_CMCLIST (object));
4464 clist = GTK_CMCLIST (object);
4466 columns_delete (clist);
4468 G_OBJECT_CLASS (gtk_cmclist_parent_class)->finalize (object);
4471 /* GTKWIDGET
4472 * gtk_cmclist_realize
4473 * gtk_cmclist_unrealize
4474 * gtk_cmclist_map
4475 * gtk_cmclist_unmap
4476 * gtk_cmclist_draw
4477 * gtk_cmclist_style_set
4478 * gtk_cmclist_button_press
4479 * gtk_cmclist_button_release
4480 * gtk_cmclist_motion
4481 * gtk_cmclist_size_request
4482 * gtk_cmclist_size_allocate
4484 static void
4485 gtk_cmclist_realize (GtkWidget *widget)
4487 GtkAllocation allocation;
4488 GtkCMCList *clist;
4489 GtkStyle *style, *attached_style;
4490 GdkWindow *window;
4491 GdkWindowAttr attributes;
4492 GtkCMCListRow *clist_row;
4493 GList *list;
4494 gint attributes_mask;
4495 gint event_mask;
4496 gint i;
4497 gint j;
4499 cm_return_if_fail (GTK_IS_CMCLIST (widget));
4501 clist = GTK_CMCLIST (widget);
4503 gtk_widget_set_realized (widget, TRUE);
4505 gtk_widget_get_allocation (widget, &allocation);
4507 attributes.window_type = GDK_WINDOW_CHILD;
4508 attributes.x = allocation.x;
4509 attributes.y = allocation.y;
4510 attributes.width = allocation.width;
4511 attributes.height = allocation.height;
4512 attributes.wclass = GDK_INPUT_OUTPUT;
4513 attributes.visual = gtk_widget_get_visual (widget);
4515 event_mask = gtk_widget_get_events (widget);
4516 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
4518 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
4520 /* main window */
4521 window = gdk_window_new (gtk_widget_get_parent_window (widget),
4522 &attributes, attributes_mask);
4523 gtk_widget_set_window (widget, window);
4524 gtk_widget_register_window (widget, window);
4526 style = gtk_widget_get_style (widget);
4527 attached_style = gtk_style_attach (style, window);
4528 if (attached_style != style) {
4529 gtk_widget_set_style(widget, attached_style);
4530 style = attached_style;
4533 gtk_style_set_background (style, window, GTK_STATE_NORMAL);
4535 /* column-title window */
4537 attributes.x = clist->column_title_area.x;
4538 attributes.y = clist->column_title_area.y;
4539 attributes.width = clist->column_title_area.width;
4540 attributes.height = clist->column_title_area.height;
4542 clist->title_window = gdk_window_new (window, &attributes,
4543 attributes_mask);
4544 gtk_widget_register_window (widget, clist->title_window);
4546 gtk_style_set_background (style, clist->title_window,
4547 GTK_STATE_NORMAL);
4548 gdk_window_show (clist->title_window);
4550 /* set things up so column buttons are drawn in title window */
4551 for (i = 0; i < clist->columns; i++)
4552 if (clist->column[i].button)
4553 gtk_widget_set_parent_window (clist->column[i].button,
4554 clist->title_window);
4556 /* clist-window */
4557 attributes.x = (clist->internal_allocation.x +
4558 style->xthickness);
4559 attributes.y = (clist->internal_allocation.y +
4560 style->ythickness +
4561 clist->column_title_area.height);
4562 attributes.width = clist->clist_window_width;
4563 attributes.height = clist->clist_window_height;
4564 attributes.event_mask = event_mask |
4565 GDK_SCROLL_MASK |
4566 GDK_SMOOTH_SCROLL_MASK |
4567 GDK_POINTER_MOTION_MASK |
4568 GDK_KEY_RELEASE_MASK |
4569 GDK_BUTTON_PRESS_MASK |
4570 GDK_BUTTON_RELEASE_MASK;
4572 clist->clist_window = gdk_window_new (window, &attributes,
4573 attributes_mask);
4574 gtk_widget_register_window (widget, clist->clist_window);
4576 gdk_window_set_background (clist->clist_window,
4577 &style->base[GTK_STATE_NORMAL]);
4578 gdk_window_show (clist->clist_window);
4579 clist->clist_window_width = gdk_window_get_width(clist->clist_window);
4580 clist->clist_window_height = gdk_window_get_height(clist->clist_window);
4582 /* create resize windows */
4583 attributes.wclass = GDK_INPUT_ONLY;
4584 attributes_mask = GDK_WA_CURSOR;
4585 attributes.cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
4586 GDK_SB_H_DOUBLE_ARROW);
4587 clist->cursor_drag = attributes.cursor;
4589 attributes.x = LIST_WIDTH (clist) + 1;
4590 attributes.y = 0;
4591 attributes.width = 0;
4592 attributes.height = 0;
4593 attributes.event_mask = event_mask |
4594 GDK_BUTTON_PRESS_MASK |
4595 GDK_BUTTON_RELEASE_MASK |
4596 GDK_POINTER_MOTION_MASK;
4598 for (i = 0; i < clist->columns; i++)
4600 clist->column[i].window = gdk_window_new (clist->title_window,
4601 &attributes, attributes_mask);
4602 gtk_widget_register_window (widget, clist->column[i].window);
4605 /* This is slightly less efficient than creating them with the
4606 * right size to begin with, but easier
4608 size_allocate_title_buttons (clist);
4610 /* attach optional row/cell styles, allocate foreground/background colors */
4611 list = clist->row_list;
4612 for (i = 0; i < clist->rows; i++)
4614 clist_row = list->data;
4615 list = list->next;
4617 if (clist_row->style)
4618 clist_row->style = gtk_style_attach (clist_row->style,
4619 clist->clist_window);
4621 for (j = 0; j < clist->columns; j++)
4622 if (clist_row->cell[j].style)
4623 clist_row->cell[j].style =
4624 gtk_style_attach (clist_row->cell[j].style, clist->clist_window);
4628 static void
4629 gtk_cmclist_unrealize (GtkWidget *widget)
4631 gint i;
4632 GtkCMCList *clist;
4634 cm_return_if_fail (GTK_IS_CMCLIST (widget));
4636 clist = GTK_CMCLIST (widget);
4638 /* freeze the list */
4639 clist->freeze_count++;
4641 if (gtk_widget_get_mapped (widget))
4642 gtk_cmclist_unmap (widget);
4644 gtk_widget_set_mapped (widget, FALSE);
4646 /* detach optional row/cell styles */
4647 if (gtk_widget_get_realized (widget))
4649 GtkCMCListRow *clist_row;
4650 GList *list;
4651 gint j;
4653 list = clist->row_list;
4654 for (i = 0; i < clist->rows; i++)
4656 clist_row = list->data;
4657 list = list->next;
4659 if (clist_row->style)
4660 gtk_style_detach (clist_row->style);
4661 for (j = 0; j < clist->columns; j++)
4662 if (clist_row->cell[j].style)
4663 gtk_style_detach (clist_row->cell[j].style);
4667 gdk_cursor_unref (clist->cursor_drag);
4669 for (i = 0; i < clist->columns; i++)
4671 if (clist->column[i].button)
4672 gtk_widget_unrealize (clist->column[i].button);
4673 if (clist->column[i].window)
4675 gtk_widget_unregister_window (widget, clist->column[i].window);
4676 gdk_window_destroy (clist->column[i].window);
4677 clist->column[i].window = NULL;
4681 gtk_widget_unregister_window (widget, clist->clist_window);
4682 gdk_window_destroy (clist->clist_window);
4683 clist->clist_window = NULL;
4685 gtk_widget_unregister_window (widget, clist->title_window);
4686 gdk_window_destroy (clist->title_window);
4687 clist->title_window = NULL;
4689 clist->cursor_drag = NULL;
4691 if (GTK_WIDGET_CLASS (gtk_cmclist_parent_class)->unrealize)
4692 (* GTK_WIDGET_CLASS (gtk_cmclist_parent_class)->unrealize) (widget);
4695 static void
4696 gtk_cmclist_map (GtkWidget *widget)
4698 gint i;
4699 GtkCMCList *clist;
4701 cm_return_if_fail (GTK_IS_CMCLIST (widget));
4703 clist = GTK_CMCLIST (widget);
4705 if (!gtk_widget_get_mapped (widget))
4707 gtk_widget_set_mapped (widget, TRUE);
4709 /* map column buttons */
4710 for (i = 0; i < clist->columns; i++)
4712 if (clist->column[i].button &&
4713 gtk_widget_get_visible (clist->column[i].button) &&
4714 !gtk_widget_get_mapped (clist->column[i].button))
4715 gtk_widget_map (clist->column[i].button);
4718 for (i = 0; i < clist->columns; i++)
4719 if (clist->column[i].window && clist->column[i].button)
4721 gdk_window_raise (clist->column[i].window);
4722 gdk_window_show (clist->column[i].window);
4725 gdk_window_show (clist->title_window);
4726 gdk_window_show (clist->clist_window);
4727 gdk_window_show (gtk_widget_get_window (widget));
4729 /* unfreeze the list */
4730 clist->freeze_count = 0;
4734 static void
4735 gtk_cmclist_unmap (GtkWidget *widget)
4737 gint i;
4738 GtkCMCList *clist;
4740 cm_return_if_fail (GTK_IS_CMCLIST (widget));
4742 clist = GTK_CMCLIST (widget);
4744 if (gtk_widget_get_mapped (widget))
4746 gtk_widget_set_mapped (widget, FALSE);
4748 if (clist_has_grab (clist))
4750 remove_grab (clist);
4752 GTK_CMCLIST_GET_CLASS (widget)->resync_selection (clist, NULL);
4754 clist->click_cell.row = -1;
4755 clist->click_cell.column = -1;
4756 clist->drag_button = 0;
4758 if (GTK_CMCLIST_IN_DRAG(clist))
4760 gpointer drag_data;
4762 GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_IN_DRAG);
4763 drag_data = g_object_get_data (G_OBJECT (clist),
4764 "gtk-site-data");
4765 if (drag_data)
4766 g_signal_handlers_unblock_matched(G_OBJECT(clist), G_SIGNAL_MATCH_DATA,
4767 0, 0, 0, 0, drag_data);
4771 for (i = 0; i < clist->columns; i++)
4772 if (clist->column[i].window)
4773 gdk_window_hide (clist->column[i].window);
4775 gdk_window_hide (clist->clist_window);
4776 gdk_window_hide (clist->title_window);
4777 gdk_window_hide (gtk_widget_get_window (widget));
4779 /* unmap column buttons */
4780 for (i = 0; i < clist->columns; i++)
4781 if (clist->column[i].button &&
4782 gtk_widget_get_mapped (clist->column[i].button))
4783 gtk_widget_unmap (clist->column[i].button);
4785 /* freeze the list */
4786 clist->freeze_count++;
4790 static gint
4791 gtk_cmclist_draw (GtkWidget *widget,
4792 cairo_t *cr)
4794 GtkCMCList *clist;
4796 cm_return_val_if_fail (GTK_IS_CMCLIST (widget), FALSE);
4797 cm_return_val_if_fail (cr != NULL, FALSE);
4799 if (gtk_widget_is_drawable (widget))
4801 clist = GTK_CMCLIST (widget);
4802 clist->draw_now = 0;
4804 /* Draw clist_window */
4805 if (gtk_cairo_should_draw_window (cr, clist->clist_window))
4807 GdkRectangle area;
4809 /* The painting area is currently relative to GdkWindow
4810 * of the entire widget, we're only interested in the
4811 * part that is inside clist_window. */
4812 /* First, get geometry of clist_window in coordinates
4813 * relative to the parent window. */
4814 gdk_window_get_position(clist->clist_window, &area.x, &area.y);
4815 area.height = gdk_window_get_height(clist->clist_window);
4816 area.width = gdk_window_get_width(clist->clist_window);
4818 /* Store current state of the painting area, as we will
4819 * want to use it for title_window later. */
4820 cairo_save(cr);
4822 /* Now clip the painting area to just the part that is inside
4823 * clist_window, and call draw_rows() with a GdkRectangle
4824 * corresponding to that. */
4825 gdk_cairo_rectangle(cr, &area);
4826 cairo_clip(cr);
4828 if (gdk_cairo_get_clip_rectangle (cr, &area))
4830 gdouble x, y;
4832 /* Before we pass the area to draw_rows(), we need to
4833 * transform it to coordinates relative to clist_window.
4834 * We already made sure that it is entirely inside
4835 * this window, so no further checks have to be made. */
4836 gdk_window_coords_from_parent(clist->clist_window, area.x, area.y, &x, &y);
4837 area.x = x;
4838 area.y = y;
4840 draw_rows (clist, &area);
4843 /* Restore the original painting area for further use. */
4844 cairo_restore(cr);
4847 /* Draw title_window - just propagate the draw event
4848 * to the individual button widgets, they can draw
4849 * themselves. */
4850 if (gtk_cairo_should_draw_window (cr, clist->title_window))
4852 gint i;
4854 for (i = 0; i < clist->columns; i++)
4856 if (clist->column[i].button) {
4857 gtk_container_propagate_draw (GTK_CONTAINER (clist), clist->column[i].button, cr);
4861 clist->draw_now = 1;
4863 return FALSE;
4866 static void
4867 gtk_cmclist_style_set (GtkWidget *widget,
4868 GtkStyle *previous_style)
4870 GtkCMCList *clist;
4871 GtkStyle *style;
4873 cm_return_if_fail (GTK_IS_CMCLIST (widget));
4875 if (GTK_WIDGET_CLASS (gtk_cmclist_parent_class)->style_set)
4876 (*GTK_WIDGET_CLASS (gtk_cmclist_parent_class)->style_set) (widget, previous_style);
4878 clist = GTK_CMCLIST (widget);
4880 if (gtk_widget_get_realized (widget))
4882 style = gtk_widget_get_style (widget);
4883 gtk_style_set_background (style, gtk_widget_get_window (widget),
4884 gtk_widget_get_state(widget));
4885 gtk_style_set_background (style, clist->title_window, GTK_STATE_NORMAL);
4886 gdk_window_set_background (clist->clist_window, &style->base[GTK_STATE_NORMAL]);
4889 /* Fill in data after widget has correct style */
4891 /* text properties */
4892 if (!GTK_CMCLIST_ROW_HEIGHT_SET(clist))
4893 /* Reset clist->row_height */
4894 gtk_cmclist_set_row_height (clist, 0);
4896 /* Column widths */
4897 if (!GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
4899 gint width;
4900 gint i;
4902 for (i = 0; i < clist->columns; i++)
4903 if (clist->column[i].auto_resize)
4905 width = gtk_cmclist_optimal_column_width (clist, i);
4906 if (width != clist->column[i].width)
4907 gtk_cmclist_set_column_width (clist, i, width);
4912 static gint
4913 gtk_cmclist_button_press (GtkWidget *widget,
4914 GdkEventButton *event)
4916 gint i;
4917 GtkCMCList *clist;
4918 gint x;
4919 gint y;
4920 gint row;
4921 gint column;
4922 gint button_actions;
4924 cm_return_val_if_fail (GTK_IS_CMCLIST (widget), FALSE);
4925 cm_return_val_if_fail (event != NULL, FALSE);
4927 clist = GTK_CMCLIST (widget);
4929 button_actions = clist->button_actions[event->button - 1];
4931 if (button_actions == GTK_CMBUTTON_IGNORED)
4932 return FALSE;
4934 /* selections on the list */
4935 if (event->window == clist->clist_window)
4937 x = event->x;
4938 y = event->y;
4940 if (get_selection_info (clist, x, y, &row, &column))
4942 gint old_row = clist->focus_row;
4944 if (clist->focus_row == -1)
4945 old_row = row;
4947 if (event->type == GDK_BUTTON_PRESS)
4949 GdkEventMask mask = ((1 << (4 + event->button)) |
4950 GDK_POINTER_MOTION_HINT_MASK |
4951 GDK_BUTTON_RELEASE_MASK);
4953 if (gdk_pointer_grab (clist->clist_window, FALSE, mask,
4954 NULL, NULL, event->time))
4955 return FALSE;
4956 gtk_grab_add (widget);
4958 clist->click_cell.row = row;
4959 clist->click_cell.column = column;
4960 clist->drag_button = event->button;
4962 else
4964 clist->click_cell.row = -1;
4965 clist->click_cell.column = -1;
4967 clist->drag_button = 0;
4968 remove_grab (clist);
4971 if (button_actions & GTK_CMBUTTON_SELECTS)
4973 if (GTK_CMCLIST_ADD_MODE(clist))
4975 GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_ADD_MODE);
4976 if (gtk_widget_has_focus(widget))
4978 gtk_cmclist_undraw_focus (widget);
4979 clist->focus_row = row;
4980 gtk_cmclist_draw_focus (widget);
4982 else
4984 clist->focus_row = row;
4987 else if (row != clist->focus_row)
4989 if (gtk_widget_has_focus(widget))
4991 gtk_cmclist_undraw_focus (widget);
4992 clist->focus_row = row;
4993 gtk_cmclist_draw_focus (widget);
4995 else
4996 clist->focus_row = row;
5000 if (!gtk_widget_has_focus(widget))
5001 gtk_widget_grab_focus (widget);
5003 if (button_actions & GTK_CMBUTTON_SELECTS)
5005 switch (clist->selection_mode)
5007 case GTK_SELECTION_SINGLE:
5008 if (event->type != GDK_BUTTON_PRESS)
5010 g_signal_emit (G_OBJECT (clist),
5011 clist_signals[SELECT_ROW], 0,
5012 row, column, event);
5013 clist->anchor = -1;
5015 else
5016 clist->anchor = row;
5017 break;
5018 case GTK_SELECTION_BROWSE:
5019 g_signal_emit (G_OBJECT (clist),
5020 clist_signals[SELECT_ROW], 0,
5021 row, column, event);
5022 break;
5023 case GTK_SELECTION_MULTIPLE:
5024 if (event->type != GDK_BUTTON_PRESS)
5026 if (clist->anchor != -1)
5028 update_extended_selection (clist, clist->focus_row);
5029 GTK_CMCLIST_GET_CLASS (clist)->resync_selection
5030 (clist, (GdkEvent *) event);
5032 g_signal_emit (G_OBJECT (clist),
5033 clist_signals[SELECT_ROW], 0,
5034 row, column, event);
5035 break;
5038 if (event->state & GDK_CONTROL_MASK)
5040 if (event->state & GDK_SHIFT_MASK)
5042 if (clist->anchor < 0)
5044 g_list_free (clist->undo_selection);
5045 g_list_free (clist->undo_unselection);
5046 clist->undo_selection = NULL;
5047 clist->undo_unselection = NULL;
5048 clist->anchor = old_row;
5049 clist->drag_pos = old_row;
5050 clist->undo_anchor = old_row;
5052 update_extended_selection (clist, clist->focus_row);
5054 else
5056 if (clist->anchor == -1)
5057 set_anchor (clist, TRUE, row, old_row);
5058 else
5059 update_extended_selection (clist,
5060 clist->focus_row);
5062 break;
5065 if (event->state & GDK_SHIFT_MASK)
5067 set_anchor (clist, FALSE, old_row, old_row);
5068 update_extended_selection (clist, clist->focus_row);
5069 break;
5072 if (clist->anchor == -1)
5073 set_anchor (clist, FALSE, row, old_row);
5074 else
5075 update_extended_selection (clist, clist->focus_row);
5076 break;
5077 default:
5078 break;
5082 return TRUE;
5085 /* press on resize windows */
5086 for (i = 0; i < clist->columns; i++)
5087 if (clist->column[i].resizeable && clist->column[i].window &&
5088 event->window == clist->column[i].window)
5090 gpointer drag_data;
5092 if (gdk_pointer_grab (clist->column[i].window, FALSE,
5093 GDK_POINTER_MOTION_HINT_MASK |
5094 GDK_BUTTON1_MOTION_MASK |
5095 GDK_BUTTON_RELEASE_MASK,
5096 NULL, NULL, event->time))
5097 return FALSE;
5099 gtk_grab_add (widget);
5100 GTK_CMCLIST_SET_FLAG (clist, CMCLIST_IN_DRAG);
5102 /* block attached dnd signal handler */
5103 drag_data = g_object_get_data (G_OBJECT (clist), "gtk-site-data");
5104 if (drag_data)
5105 g_signal_handlers_block_matched(G_OBJECT(clist), G_SIGNAL_MATCH_DATA,
5106 0, 0, 0, 0, drag_data);
5108 if (!gtk_widget_has_focus(widget))
5109 gtk_widget_grab_focus (widget);
5111 clist->drag_pos = i;
5112 clist->x_drag = (COLUMN_LEFT_XPIXEL(clist, i) + COLUMN_INSET +
5113 clist->column[i].area.width + CELL_SPACING);
5115 return TRUE;
5118 return FALSE;
5121 static gint
5122 gtk_cmclist_button_release (GtkWidget *widget,
5123 GdkEventButton *event)
5125 GtkCMCList *clist;
5126 gint button_actions;
5128 cm_return_val_if_fail (GTK_IS_CMCLIST (widget), FALSE);
5129 cm_return_val_if_fail (event != NULL, FALSE);
5131 clist = GTK_CMCLIST (widget);
5133 button_actions = clist->button_actions[event->button - 1];
5134 if (button_actions == GTK_CMBUTTON_IGNORED)
5135 return FALSE;
5137 /* release on resize windows */
5138 if (GTK_CMCLIST_IN_DRAG(clist))
5140 gpointer drag_data;
5141 gint width;
5142 gint x;
5143 gint i;
5145 i = clist->drag_pos;
5146 clist->drag_pos = -1;
5148 /* unblock attached dnd signal handler */
5149 drag_data = g_object_get_data (G_OBJECT (clist), "gtk-site-data");
5150 if (drag_data)
5151 g_signal_handlers_unblock_matched(G_OBJECT(clist), G_SIGNAL_MATCH_DATA,
5152 0, 0, 0, 0, drag_data);
5154 GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_IN_DRAG);
5155 gtk_widget_get_pointer (widget, &x, NULL);
5156 gtk_grab_remove (widget);
5157 gdk_display_pointer_ungrab (gtk_widget_get_display (widget), event->time);
5159 if (clist->x_drag >= 0)
5160 clist_refresh(clist);
5162 width = new_column_width (clist, i, &x);
5163 gtk_cmclist_set_column_width (clist, i, width);
5165 return TRUE;
5168 if (clist->drag_button == event->button)
5170 gint row;
5171 gint column;
5173 clist->drag_button = 0;
5174 clist->click_cell.row = -1;
5175 clist->click_cell.column = -1;
5177 remove_grab (clist);
5179 if (button_actions & GTK_CMBUTTON_SELECTS)
5181 switch (clist->selection_mode)
5183 case GTK_SELECTION_MULTIPLE:
5184 if (!(event->state & GDK_SHIFT_MASK) ||
5185 !gtk_widget_get_can_focus (widget) ||
5186 event->x < 0 || event->x >= clist->clist_window_width ||
5187 event->y < 0 || event->y >= clist->clist_window_height)
5188 GTK_CMCLIST_GET_CLASS (clist)->resync_selection
5189 (clist, (GdkEvent *) event);
5190 break;
5191 case GTK_SELECTION_SINGLE:
5192 if (get_selection_info (clist, event->x, event->y,
5193 &row, &column))
5195 if (row >= 0 && row < clist->rows && clist->anchor == row)
5196 toggle_row (clist, row, column, (GdkEvent *) event);
5198 clist->anchor = -1;
5199 break;
5200 default:
5201 break;
5205 return TRUE;
5208 return FALSE;
5211 static gint
5212 gtk_cmclist_motion (GtkWidget *widget,
5213 GdkEventMotion *event)
5215 GtkCMCList *clist;
5216 gint x;
5217 gint y;
5218 gint row;
5219 gint new_width;
5220 gint button_actions = 0;
5221 guint value;
5223 cm_return_val_if_fail (GTK_IS_CMCLIST (widget), FALSE);
5225 clist = GTK_CMCLIST (widget);
5226 if (!clist_has_grab (clist))
5227 return FALSE;
5229 if (clist->drag_button > 0)
5230 button_actions = clist->button_actions[clist->drag_button - 1];
5232 if (GTK_CMCLIST_IN_DRAG(clist))
5234 if (event->is_hint || event->window != gtk_widget_get_window (widget))
5235 gtk_widget_get_pointer (widget, &x, NULL);
5236 else
5237 x = event->x;
5239 new_width = new_column_width (clist, clist->drag_pos, &x);
5240 if (x != clist->x_drag)
5242 /* x_drag < 0 indicates that the xor line is already invisible */
5243 if (clist->x_drag >= 0)
5244 clist_refresh(clist);
5246 clist->x_drag = x;
5248 if (clist->x_drag >= 0)
5249 draw_xor_line (clist);
5252 if (new_width <= MAX (COLUMN_MIN_WIDTH + 1,
5253 clist->column[clist->drag_pos].min_width + 1))
5255 if (COLUMN_LEFT_XPIXEL (clist, clist->drag_pos) < 0 && x < 0)
5256 gtk_cmclist_moveto (clist, -1, clist->drag_pos, 0, 0);
5257 return FALSE;
5259 if (clist->column[clist->drag_pos].max_width >= COLUMN_MIN_WIDTH &&
5260 new_width >= clist->column[clist->drag_pos].max_width)
5262 if (COLUMN_LEFT_XPIXEL (clist, clist->drag_pos) + new_width >
5263 clist->clist_window_width && x < 0)
5264 move_horizontal (clist,
5265 COLUMN_LEFT_XPIXEL (clist, clist->drag_pos) +
5266 new_width - clist->clist_window_width +
5267 COLUMN_INSET + CELL_SPACING);
5268 return FALSE;
5272 if (event->is_hint || event->window != clist->clist_window) {
5273 GdkDisplay *display;
5274 GdkSeat *seat;
5276 display = gdk_window_get_display(event->window);
5277 seat = gdk_display_get_default_seat(display);
5278 gdk_device_get_position(gdk_seat_get_pointer(seat),
5279 NULL, &x, &y);
5281 else
5283 x = event->x;
5284 y = event->y;
5287 if (GTK_CMCLIST_REORDERABLE(clist) && button_actions & GTK_CMBUTTON_DRAGS)
5289 /* delayed drag start */
5290 if (event->window == clist->clist_window &&
5291 clist->click_cell.row >= 0 && clist->click_cell.column >= 0 &&
5292 (y < 0 || y >= clist->clist_window_height ||
5293 x < 0 || x >= clist->clist_window_width ||
5294 y < ROW_TOP_YPIXEL (clist, clist->click_cell.row) ||
5295 y >= (ROW_TOP_YPIXEL (clist, clist->click_cell.row) +
5296 clist->row_height) ||
5297 x < COLUMN_LEFT_XPIXEL (clist, clist->click_cell.column) ||
5298 x >= (COLUMN_LEFT_XPIXEL(clist, clist->click_cell.column) +
5299 clist->column[clist->click_cell.column].area.width)))
5301 GtkTargetList *target_list;
5303 target_list = gtk_target_list_new (&clist_target_table, 1);
5304 gtk_drag_begin_with_coordinates(widget, target_list, GDK_ACTION_MOVE,
5305 clist->drag_button, (GdkEvent *)event, -1, -1);
5308 return TRUE;
5311 /* horizontal autoscrolling */
5312 if (clist->hadjustment && LIST_WIDTH (clist) > clist->clist_window_width &&
5313 (x < 0 || x >= clist->clist_window_width))
5315 if (clist->htimer)
5316 return FALSE;
5318 clist->htimer = gdk_threads_add_timeout
5319 (SCROLL_TIME, (GSourceFunc) horizontal_timeout, clist);
5320 value = gtk_adjustment_get_value (clist->hadjustment);
5321 if (!((x < 0 && value == 0) ||
5322 (x >= clist->clist_window_width &&
5323 value ==
5324 LIST_WIDTH (clist) - clist->clist_window_width)))
5326 if (x < 0)
5327 move_horizontal (clist, -1 + (x/2));
5328 else
5329 move_horizontal (clist, 1 + (x - clist->clist_window_width) / 2);
5333 if (GTK_CMCLIST_IN_DRAG(clist))
5334 return FALSE;
5336 /* vertical autoscrolling */
5337 row = ROW_FROM_YPIXEL (clist, y);
5339 /* don't scroll on last pixel row if it's a cell spacing */
5340 if (y == clist->clist_window_height - 1 &&
5341 y == ROW_TOP_YPIXEL (clist, row-1) + clist->row_height)
5342 return FALSE;
5344 if (LIST_HEIGHT (clist) > clist->clist_window_height &&
5345 (y < 0 || y >= clist->clist_window_height))
5347 if (clist->vtimer)
5348 return FALSE;
5349 clist->vtimer = gdk_threads_add_timeout (SCROLL_TIME,
5350 (GSourceFunc) vertical_timeout, clist);
5351 if (clist->drag_button &&
5352 ((y < 0 && clist->focus_row == 0) ||
5353 (y >= clist->clist_window_height &&
5354 clist->focus_row == clist->rows - 1)))
5355 return FALSE;
5358 row = CLAMP (row, 0, clist->rows - 1);
5360 if (button_actions & GTK_CMBUTTON_SELECTS &&
5361 !g_object_get_data (G_OBJECT (widget), "gtk-site-data"))
5363 if (row == clist->focus_row)
5364 return FALSE;
5366 gtk_cmclist_undraw_focus (widget);
5367 clist->focus_row = row;
5368 gtk_cmclist_draw_focus (widget);
5370 switch (clist->selection_mode)
5372 case GTK_SELECTION_BROWSE:
5373 g_signal_emit (G_OBJECT (clist), clist_signals[SELECT_ROW], 0,
5374 clist->focus_row, -1, event);
5375 break;
5376 case GTK_SELECTION_MULTIPLE:
5377 update_extended_selection (clist, clist->focus_row);
5378 break;
5379 default:
5380 break;
5384 if (ROW_TOP_YPIXEL(clist, row) < 0)
5385 move_vertical (clist, row, 0);
5386 else if (ROW_TOP_YPIXEL(clist, row) + clist->row_height >
5387 clist->clist_window_height)
5388 move_vertical (clist, row, 1);
5390 return FALSE;
5393 static void
5394 gtk_cmclist_get_preferred_width (GtkWidget *widget,
5395 gint *minimal_width,
5396 gint *natural_width)
5398 GtkRequisition requisition;
5400 gtk_cmclist_size_request (widget, &requisition);
5402 *minimal_width = *natural_width = requisition.width;
5405 static void
5406 gtk_cmclist_get_preferred_height (GtkWidget *widget,
5407 gint *minimal_height,
5408 gint *natural_height)
5410 GtkRequisition requisition;
5412 gtk_cmclist_size_request (widget, &requisition);
5414 *minimal_height = *natural_height = requisition.height;
5417 static void
5418 gtk_cmclist_size_request (GtkWidget *widget,
5419 GtkRequisition *requisition)
5421 GtkCMCList *clist;
5422 GtkStyle *style;
5423 gint i;
5424 guint border_width;
5425 cm_return_if_fail (GTK_IS_CMCLIST (widget));
5426 cm_return_if_fail (requisition != NULL);
5428 clist = GTK_CMCLIST (widget);
5429 style = gtk_widget_get_style (widget);
5431 requisition->width = 0;
5432 requisition->height = 0;
5434 /* compute the size of the column title (title) area */
5435 clist->column_title_area.height = 0;
5436 if (GTK_CMCLIST_SHOW_TITLES(clist)) {
5437 for (i = 0; i < clist->columns; i++)
5438 if (clist->column[i].button)
5440 GtkRequisition child_requisition;
5442 gtk_widget_get_preferred_size(clist->column[i].button,
5443 &child_requisition, NULL);
5444 clist->column_title_area.height =
5445 MAX (clist->column_title_area.height,
5446 child_requisition.height);
5448 //clist->column_title_area.height = font_height;
5450 border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
5451 requisition->width += (style->xthickness +
5452 border_width) * 2;
5453 requisition->height += (clist->column_title_area.height +
5454 (style->ythickness +
5455 border_width) * 2);
5457 /* if (!clist->hadjustment) */
5458 requisition->width += list_requisition_width (clist);
5459 /* if (!clist->vadjustment) */
5460 requisition->height += LIST_HEIGHT (clist);
5463 static void
5464 gtk_cmclist_size_allocate (GtkWidget *widget,
5465 GtkAllocation *allocation)
5467 GtkStyle *style;
5468 GtkCMCList *clist;
5469 GtkAllocation clist_allocation;
5470 gint border_width;
5472 cm_return_if_fail (GTK_IS_CMCLIST (widget));
5473 cm_return_if_fail (allocation != NULL);
5475 style = gtk_widget_get_style (widget);
5476 clist = GTK_CMCLIST (widget);
5477 gtk_widget_set_allocation (widget, allocation);
5478 border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
5480 if (gtk_widget_get_realized (widget))
5482 gdk_window_move_resize (gtk_widget_get_window (widget),
5483 allocation->x + border_width,
5484 allocation->y + border_width,
5485 allocation->width - border_width * 2,
5486 allocation->height - border_width * 2);
5489 /* use internal allocation structure for all the math
5490 * because it's easier than always subtracting the container
5491 * border width */
5492 clist->internal_allocation.x = 0;
5493 clist->internal_allocation.y = 0;
5494 clist->internal_allocation.width = MAX (1, (gint)allocation->width -
5495 border_width * 2);
5496 clist->internal_allocation.height = MAX (1, (gint)allocation->height -
5497 border_width * 2);
5499 /* allocate clist window assuming no scrollbars */
5500 clist_allocation.x = (clist->internal_allocation.x +
5501 style->xthickness);
5502 clist_allocation.y = (clist->internal_allocation.y +
5503 style->ythickness +
5504 clist->column_title_area.height);
5505 clist_allocation.width = MAX (1, (gint)clist->internal_allocation.width -
5506 (2 * (gint)style->xthickness));
5507 clist_allocation.height = MAX (1, (gint)clist->internal_allocation.height -
5508 (2 * (gint)style->ythickness) -
5509 (gint)clist->column_title_area.height);
5511 clist->clist_window_width = clist_allocation.width;
5512 clist->clist_window_height = clist_allocation.height;
5514 if (gtk_widget_get_realized (widget))
5516 gdk_window_move_resize (clist->clist_window,
5517 clist_allocation.x,
5518 clist_allocation.y,
5519 clist_allocation.width,
5520 clist_allocation.height);
5523 /* position the window which holds the column title buttons */
5524 clist->column_title_area.x = style->xthickness;
5525 clist->column_title_area.y = style->ythickness;
5526 clist->column_title_area.width = clist_allocation.width;
5528 if (gtk_widget_get_realized (widget))
5530 gdk_window_move_resize (clist->title_window,
5531 clist->column_title_area.x,
5532 clist->column_title_area.y,
5533 clist->column_title_area.width,
5534 clist->column_title_area.height);
5537 /* column button allocation */
5538 size_allocate_columns (clist, FALSE);
5539 size_allocate_title_buttons (clist);
5541 adjust_adjustments (clist, TRUE);
5544 /* GTKCONTAINER
5545 * gtk_cmclist_forall
5547 static void
5548 gtk_cmclist_forall (GtkContainer *container,
5549 gboolean include_internals,
5550 GtkCallback callback,
5551 gpointer callback_data)
5553 GtkCMCList *clist;
5554 guint i;
5556 cm_return_if_fail (GTK_IS_CMCLIST (container));
5557 cm_return_if_fail (callback != NULL);
5559 if (!include_internals)
5560 return;
5562 clist = GTK_CMCLIST (container);
5564 /* callback for the column buttons */
5565 for (i = 0; i < clist->columns; i++)
5566 if (clist->column[i].button)
5567 (*callback) (clist->column[i].button, callback_data);
5570 /* PRIVATE DRAWING FUNCTIONS
5571 * get_cell_style
5572 * draw_cell_pixbuf
5573 * draw_row
5574 * draw_rows
5575 * draw_xor_line
5576 * clist_refresh
5578 static void
5579 get_cell_style (GtkCMCList *clist,
5580 GtkCMCListRow *clist_row,
5581 gint state,
5582 gint column,
5583 GtkStyle **style)
5585 GtkStyle *gtkstyle;
5587 if (clist_row->cell[column].style)
5589 if (style)
5590 *style = clist_row->cell[column].style;
5592 else if (clist_row->style)
5594 if (style)
5595 *style = clist_row->style;
5597 else
5599 gtkstyle = gtk_widget_get_style (GTK_WIDGET (clist));
5600 if (style)
5601 *style = gtkstyle;
5605 static gint
5606 draw_cell_pixbuf (GdkWindow *window,
5607 GdkRectangle *clip_rectangle,
5608 cairo_t *cr,
5609 GdkPixbuf *pixbuf,
5610 gint x,
5611 gint y,
5612 gint width,
5613 gint height)
5615 gint xsrc = 0;
5616 gint ysrc = 0;
5618 if (!pixbuf || (width == 0 && height == 0))
5619 return x;
5621 if (x < clip_rectangle->x)
5623 xsrc = clip_rectangle->x - x;
5624 width -= xsrc;
5625 x = clip_rectangle->x;
5627 if (x + width > clip_rectangle->x + clip_rectangle->width)
5628 width = clip_rectangle->x + clip_rectangle->width - x;
5630 if (y < clip_rectangle->y)
5632 ysrc = clip_rectangle->y - y;
5633 height -= ysrc;
5634 y = clip_rectangle->y;
5637 if (y + height > clip_rectangle->y + clip_rectangle->height)
5638 height = clip_rectangle->y + clip_rectangle->height - y;
5640 gdk_cairo_set_source_pixbuf(cr, pixbuf, x, y);
5641 cairo_paint(cr);
5643 return x + MAX (width, 0);
5646 static void cairo_dash_from_add_mode(GtkCMCList *clist, cairo_t *cr)
5648 const double dashes[] = { 4.0, 4.0 };
5649 if (GTK_CMCLIST_ADD_MODE(clist))
5650 cairo_set_dash(cr, dashes, 2, 0);
5651 else
5652 cairo_set_dash(cr, NULL, 0, 0);
5655 static void
5656 draw_row (GtkCMCList *clist,
5657 GdkRectangle *area,
5658 gint row,
5659 GtkCMCListRow *clist_row)
5661 GtkStyle *style;
5662 GtkWidget *widget;
5663 GdkRectangle *rect;
5664 GdkRectangle row_rectangle;
5665 GdkRectangle cell_rectangle;
5666 GdkRectangle clip_rectangle;
5667 GdkRectangle intersect_rectangle;
5668 gint last_column;
5669 gint state;
5670 gint i;
5671 cairo_t *cr;
5672 cm_return_if_fail (clist != NULL);
5674 if (clist->draw_now) {
5675 gtk_widget_queue_draw(GTK_WIDGET(clist));
5676 return;
5679 /* bail now if we arn't drawable yet */
5680 if (!gtk_widget_is_drawable (GTK_WIDGET(clist)) || row < 0 || row >= clist->rows)
5681 return;
5683 widget = GTK_WIDGET (clist);
5685 /* if the function is passed the pointer to the row instead of null,
5686 * it avoids this expensive lookup */
5687 if (!clist_row)
5688 clist_row = ROW_ELEMENT (clist, row)->data;
5690 style = clist_row->style ? clist_row->style : gtk_widget_get_style (widget);
5692 /* rectangle of the entire row */
5693 row_rectangle.x = 0;
5694 row_rectangle.y = ROW_TOP_YPIXEL (clist, row);
5695 row_rectangle.width = clist->clist_window_width;
5696 row_rectangle.height = clist->row_height;
5698 /* rectangle of the cell spacing above the row */
5699 cell_rectangle.x = 0;
5700 cell_rectangle.y = row_rectangle.y - CELL_SPACING;
5701 cell_rectangle.width = row_rectangle.width;
5702 cell_rectangle.height = CELL_SPACING;
5704 /* rectangle used to clip drawing operations, its y and height
5705 * positions only need to be set once, so we set them once here.
5706 * the x and width are set withing the drawing loop below once per
5707 * column */
5708 clip_rectangle.y = row_rectangle.y;
5709 clip_rectangle.height = row_rectangle.height;
5711 state = clist_row->state;
5712 cr = gdk_cairo_create(clist->clist_window);
5714 /* draw the cell borders and background */
5715 if (area)
5717 rect = &intersect_rectangle;
5718 if (gdk_rectangle_intersect (area, &cell_rectangle,
5719 &intersect_rectangle)) {
5720 gdk_cairo_rectangle(cr, &intersect_rectangle);
5721 gdk_cairo_set_source_color(cr, &style->base[GTK_STATE_NORMAL]);
5722 cairo_fill(cr);
5725 /* the last row has to clear its bottom cell spacing too */
5726 if (clist_row == clist->row_list_end->data)
5728 cell_rectangle.y += clist->row_height + CELL_SPACING;
5730 if (gdk_rectangle_intersect (area, &cell_rectangle,
5731 &intersect_rectangle)) {
5732 gdk_cairo_rectangle(cr, &intersect_rectangle);
5733 gdk_cairo_set_source_color(cr, &style->base[GTK_STATE_NORMAL]);
5734 cairo_fill(cr);
5738 if (!gdk_rectangle_intersect (area, &row_rectangle,&intersect_rectangle))
5739 return;
5742 else
5744 rect = &clip_rectangle;
5745 gdk_cairo_rectangle(cr, &cell_rectangle);
5746 gdk_cairo_set_source_color(cr, &style->base[GTK_STATE_NORMAL]);
5747 cairo_fill(cr);
5749 /* the last row has to clear its bottom cell spacing too */
5750 if (clist_row == clist->row_list_end->data)
5752 cell_rectangle.y += clist->row_height + CELL_SPACING;
5753 gdk_cairo_rectangle(cr, &cell_rectangle);
5754 gdk_cairo_set_source_color(cr, &style->base[GTK_STATE_NORMAL]);
5755 cairo_fill(cr);
5759 for (last_column = clist->columns - 1;
5760 last_column >= 0 && !clist->column[last_column].visible; last_column--)
5763 /* iterate and draw all the columns (row cells) and draw their contents */
5764 for (i = 0; i < clist->columns; i++)
5766 GtkStyle *style;
5767 PangoLayout *layout;
5768 PangoRectangle logical_rect;
5770 gint width;
5771 gint height;
5772 gint pixbuf_width;
5773 gint offset = 0;
5775 if (!clist->column[i].visible)
5776 continue;
5778 get_cell_style (clist, clist_row, state, i, &style);
5780 clip_rectangle.x = clist->column[i].area.x + clist->hoffset;
5781 clip_rectangle.width = clist->column[i].area.width;
5783 /* calculate clipping region clipping region */
5784 clip_rectangle.x -= COLUMN_INSET + CELL_SPACING;
5785 clip_rectangle.width += (2 * COLUMN_INSET + CELL_SPACING +
5786 (i == last_column) * CELL_SPACING);
5788 if (area && !gdk_rectangle_intersect (area, &clip_rectangle,
5789 &intersect_rectangle))
5790 continue;
5792 gdk_cairo_rectangle(cr, rect);
5793 gdk_cairo_set_source_color(cr, &style->base[state]);
5794 cairo_fill(cr);
5796 clip_rectangle.x += COLUMN_INSET + CELL_SPACING;
5797 clip_rectangle.width -= (2 * COLUMN_INSET + CELL_SPACING +
5798 (i == last_column) * CELL_SPACING);
5801 /* calculate real width for column justification */
5803 layout = _gtk_cmclist_create_cell_layout (clist, clist_row, i);
5804 if (layout)
5806 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
5807 width = logical_rect.width;
5809 else
5810 width = 0;
5812 pixbuf_width = 0;
5813 height = 0;
5814 offset = 0;
5815 switch (clist_row->cell[i].type)
5817 case GTK_CMCELL_PIXBUF:
5818 pixbuf_width = gdk_pixbuf_get_width(GTK_CMCELL_PIXBUF (clist_row->cell[i])->pixbuf);
5819 height = gdk_pixbuf_get_height(GTK_CMCELL_PIXBUF (clist_row->cell[i])->pixbuf);
5820 width += pixbuf_width;
5821 break;
5822 case GTK_CMCELL_PIXTEXT:
5823 pixbuf_width = gdk_pixbuf_get_width(GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf);
5824 height = gdk_pixbuf_get_height(GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf);
5825 width += pixbuf_width + GTK_CMCELL_PIXTEXT (clist_row->cell[i])->spacing;
5826 break;
5827 default:
5828 break;
5831 switch (clist->column[i].justification)
5833 case GTK_JUSTIFY_LEFT:
5834 offset = clip_rectangle.x + clist_row->cell[i].horizontal;
5835 break;
5836 case GTK_JUSTIFY_RIGHT:
5837 offset = (clip_rectangle.x + clist_row->cell[i].horizontal +
5838 clip_rectangle.width - width);
5839 break;
5840 case GTK_JUSTIFY_CENTER:
5841 case GTK_JUSTIFY_FILL:
5842 offset = (clip_rectangle.x + clist_row->cell[i].horizontal +
5843 (clip_rectangle.width / 2) - (width / 2));
5844 break;
5847 /* Draw Text and/or Pixbuf */
5848 switch (clist_row->cell[i].type)
5850 case GTK_CMCELL_PIXBUF:
5851 draw_cell_pixbuf (clist->clist_window, &clip_rectangle, cr,
5852 GTK_CMCELL_PIXBUF (clist_row->cell[i])->pixbuf,
5853 offset,
5854 clip_rectangle.y + clist_row->cell[i].vertical +
5855 (clip_rectangle.height - height) / 2,
5856 pixbuf_width, height);
5857 break;
5858 case GTK_CMCELL_PIXTEXT:
5859 offset =
5860 draw_cell_pixbuf (clist->clist_window, &clip_rectangle, cr,
5861 GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf,
5862 offset,
5863 clip_rectangle.y + clist_row->cell[i].vertical+
5864 (clip_rectangle.height - height) / 2,
5865 pixbuf_width, height);
5866 offset += GTK_CMCELL_PIXTEXT (clist_row->cell[i])->spacing;
5868 /* Fall through */
5869 case GTK_CMCELL_TEXT:
5870 if (layout)
5872 gint row_center_offset = (clist->row_height - logical_rect.height - 1) / 2;
5873 gdk_cairo_set_source_color(cr, clist_row->fg_set ? &clist_row->foreground : &style->text[state]);
5874 cairo_move_to(cr, offset, row_rectangle.y + row_center_offset + clist_row->cell[i].vertical);
5875 pango_cairo_show_layout(cr, layout);
5876 g_object_unref (G_OBJECT (layout));
5878 break;
5879 default:
5880 break;
5884 /* draw focus rectangle */
5885 cairo_dash_from_add_mode(clist, cr);
5886 cairo_set_line_width(cr, 1.0);
5887 cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
5888 if (clist->focus_row == row &&
5889 gtk_widget_get_can_focus (widget) && gtk_widget_has_focus(widget))
5891 if (!area) {
5892 cairo_rectangle(cr, row_rectangle.x, row_rectangle.y,
5893 row_rectangle.width + 1, row_rectangle.height);
5894 gdk_cairo_set_source_color(cr, &style->text[GTK_STATE_NORMAL]);
5895 cairo_stroke(cr);
5897 else if (gdk_rectangle_intersect (area, &row_rectangle,
5898 &intersect_rectangle))
5900 cairo_rectangle(cr, row_rectangle.x, row_rectangle.y,
5901 row_rectangle.width + 1, row_rectangle.height);
5902 gdk_cairo_set_source_color(cr, &style->text[GTK_STATE_NORMAL]);
5903 cairo_stroke(cr);
5906 cairo_destroy(cr);
5910 static void
5911 draw_rows (GtkCMCList *clist,
5912 GdkRectangle *area)
5914 GList *list;
5915 GtkCMCListRow *clist_row;
5916 gint i;
5917 gint first_row;
5918 gint last_row;
5920 cm_return_if_fail (GTK_IS_CMCLIST (clist));
5922 if (clist->row_height == 0 ||
5923 !gtk_widget_is_drawable (GTK_WIDGET(clist)))
5924 return;
5926 if (area)
5928 first_row = ROW_FROM_YPIXEL (clist, area->y);
5929 last_row = ROW_FROM_YPIXEL (clist, area->y + area->height);
5931 else
5933 first_row = ROW_FROM_YPIXEL (clist, 0);
5934 last_row = ROW_FROM_YPIXEL (clist, clist->clist_window_height);
5937 /* this is a small special case which exposes the bottom cell line
5938 * on the last row -- it might go away if I change the wall the cell
5939 * spacings are drawn
5941 if (clist->rows == first_row)
5942 first_row--;
5944 list = ROW_ELEMENT (clist, first_row);
5945 i = first_row;
5946 while (list)
5948 clist_row = list->data;
5949 list = list->next;
5951 if (i > last_row)
5952 return;
5954 GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, i, clist_row);
5955 i++;
5958 if (!area) {
5959 if (!clist->draw_now) {
5960 int w, h, y;
5961 cairo_t *cr;
5962 w = gdk_window_get_width(clist->clist_window);
5963 h = gdk_window_get_height(clist->clist_window);
5964 cr = gdk_cairo_create(clist->clist_window);
5965 y = ROW_TOP_YPIXEL (clist, i);
5966 gdk_cairo_set_source_color(cr, &gtk_widget_get_style(GTK_WIDGET(clist))->base[GTK_STATE_NORMAL]);
5967 cairo_rectangle(cr, 0, y, w, h - y);
5968 cairo_fill(cr);
5969 cairo_destroy(cr);
5970 } else {
5971 gtk_widget_queue_draw(GTK_WIDGET(clist));
5976 static void
5977 draw_xor_line (GtkCMCList *clist)
5979 cairo_t *cr;
5980 cr = gdk_cairo_create(clist->clist_window);
5981 cairo_set_line_width(cr, 1.0);
5982 cairo_move_to(cr, clist->x_drag,
5983 gtk_widget_get_style (GTK_WIDGET(clist))->ythickness);
5984 cairo_line_to(cr, clist->x_drag,
5985 clist->column_title_area.height +
5986 clist->clist_window_height + 1);
5987 cairo_stroke(cr);
5988 cairo_destroy(cr);
5991 static void
5992 clist_refresh (GtkCMCList *clist)
5994 cm_return_if_fail (GTK_IS_CMCLIST (clist));
5996 if (CLIST_UNFROZEN (clist))
5998 adjust_adjustments (clist, FALSE);
5999 draw_rows (clist, NULL);
6003 /* get cell from coordinates
6004 * get_selection_info
6005 * gtk_cmclist_get_selection_info
6007 static gint
6008 get_selection_info (GtkCMCList *clist,
6009 gint x,
6010 gint y,
6011 gint *row,
6012 gint *column)
6014 gint trow, tcol;
6016 cm_return_val_if_fail (GTK_IS_CMCLIST (clist), 0);
6018 /* bounds checking, return false if the user clicked
6019 * on a blank area */
6020 trow = ROW_FROM_YPIXEL (clist, y);
6021 if (trow >= clist->rows)
6022 return 0;
6024 if (row)
6025 *row = trow;
6027 tcol = COLUMN_FROM_XPIXEL (clist, x);
6028 if (tcol >= clist->columns)
6029 return 0;
6031 if (column)
6032 *column = tcol;
6034 return 1;
6037 gint
6038 gtk_cmclist_get_selection_info (GtkCMCList *clist,
6039 gint x,
6040 gint y,
6041 gint *row,
6042 gint *column)
6044 cm_return_val_if_fail (GTK_IS_CMCLIST (clist), 0);
6045 return get_selection_info (clist, x, y, row, column);
6048 /* PRIVATE ADJUSTMENT FUNCTIONS
6049 * adjust_adjustments
6050 * vadjustment_changed
6051 * hadjustment_changed
6052 * vadjustment_value_changed
6053 * hadjustment_value_changed
6054 * check_exposures
6056 static void
6057 adjust_adjustments (GtkCMCList *clist,
6058 gboolean block_resize)
6060 if (clist->vadjustment)
6062 g_object_freeze_notify(G_OBJECT(clist->vadjustment));
6063 gtk_adjustment_set_page_size (clist->vadjustment, clist->clist_window_height);
6064 gtk_adjustment_set_step_increment (clist->vadjustment, clist->row_height);
6065 gtk_adjustment_set_page_increment (clist->vadjustment,
6066 MAX (clist->clist_window_height - clist->row_height,
6067 clist->clist_window_height / 2));
6068 gtk_adjustment_set_lower (clist->vadjustment, 0);
6069 gtk_adjustment_set_upper (clist->vadjustment, LIST_HEIGHT (clist));
6070 g_object_thaw_notify(G_OBJECT(clist->vadjustment));
6072 if ((clist->clist_window_height - clist->voffset) > LIST_HEIGHT (clist) ||
6073 (clist->voffset + (gint)gtk_adjustment_get_value (clist->vadjustment)) != 0)
6075 gtk_adjustment_set_value (clist->vadjustment,
6076 MAX (0, (LIST_HEIGHT (clist) - clist->clist_window_height)));
6077 g_signal_emit_by_name (G_OBJECT (clist->vadjustment),
6078 "value_changed");
6080 g_signal_emit_by_name (G_OBJECT (clist->vadjustment), "changed");
6083 if (clist->hadjustment)
6085 g_object_freeze_notify(G_OBJECT(clist->hadjustment));
6086 gtk_adjustment_set_page_size (clist->hadjustment, clist->clist_window_width);
6087 gtk_adjustment_set_step_increment (clist->hadjustment, 10);
6088 gtk_adjustment_set_page_increment (clist->hadjustment,
6089 MAX (clist->clist_window_width -
6090 gtk_adjustment_get_step_increment (clist->hadjustment),
6091 clist->clist_window_width / 2));
6092 gtk_adjustment_set_lower (clist->hadjustment, 0);
6093 gtk_adjustment_set_upper (clist->hadjustment, LIST_WIDTH (clist));
6094 g_object_thaw_notify(G_OBJECT(clist->hadjustment));
6096 if ((clist->clist_window_width - clist->hoffset) > LIST_WIDTH (clist) ||
6097 (clist->hoffset + (gint)gtk_adjustment_get_value (clist->hadjustment)) != 0)
6099 gtk_adjustment_set_value (clist->hadjustment, MAX (0, (LIST_WIDTH (clist) -
6100 clist->clist_window_width)));
6101 g_signal_emit_by_name (G_OBJECT (clist->hadjustment),
6102 "value_changed");
6104 g_signal_emit_by_name (G_OBJECT (clist->hadjustment), "changed");
6107 if (!block_resize && (!clist->vadjustment || !clist->hadjustment))
6109 GtkWidget *widget;
6110 GtkRequisition requisition;
6111 GtkAllocation allocation;
6113 widget = GTK_WIDGET (clist);
6114 gtk_widget_get_preferred_size(widget, &requisition, NULL);
6115 gtk_widget_get_allocation (widget, &allocation);
6117 if ((!clist->hadjustment &&
6118 requisition.width != allocation.width) ||
6119 (!clist->vadjustment &&
6120 requisition.height != allocation.height))
6121 gtk_widget_queue_resize (widget);
6125 static void
6126 vadjustment_value_changed (GtkAdjustment *adjustment,
6127 gpointer data)
6129 GtkCMCList *clist;
6130 gint dy, value;
6132 cm_return_if_fail (adjustment != NULL);
6133 cm_return_if_fail (GTK_IS_CMCLIST (data));
6135 clist = GTK_CMCLIST (data);
6137 if (adjustment != clist->vadjustment)
6138 return;
6140 value = -gtk_adjustment_get_value (adjustment);
6141 dy = value - clist->voffset;
6142 clist->voffset = value;
6144 if (gtk_widget_is_drawable (GTK_WIDGET(clist)))
6146 gdk_window_scroll (clist->clist_window, 0, dy);
6149 return;
6152 typedef struct
6154 GdkWindow *window;
6155 gint dx;
6156 } ScrollData;
6158 /* The window to which widget->window is relative */
6159 #define ALLOCATION_WINDOW(widget) \
6160 (!gtk_widget_get_has_window (widget) ? \
6161 gtk_widget_get_window (widget) : \
6162 gdk_window_get_parent (gtk_widget_get_window(widget)))
6164 static void
6165 adjust_allocation_recurse (GtkWidget *widget,
6166 gpointer data)
6168 GtkAllocation allocation;
6169 ScrollData *scroll_data = data;
6171 gtk_widget_get_allocation (widget, &allocation);
6173 if (!gtk_widget_get_realized (widget))
6175 if (gtk_widget_get_visible (widget))
6177 GdkRectangle tmp_rectangle = allocation;
6178 tmp_rectangle.x += scroll_data->dx;
6180 gtk_widget_size_allocate (widget, &tmp_rectangle);
6183 else
6185 if (ALLOCATION_WINDOW (widget) == scroll_data->window)
6187 allocation.x += scroll_data->dx;
6188 gtk_widget_set_allocation (widget, &allocation);
6190 if (GTK_IS_CONTAINER (widget))
6191 gtk_container_forall (GTK_CONTAINER (widget),
6192 adjust_allocation_recurse,
6193 data);
6198 static void
6199 adjust_allocation (GtkWidget *widget,
6200 gint dx)
6202 ScrollData scroll_data;
6204 if (gtk_widget_get_realized (widget))
6205 scroll_data.window = ALLOCATION_WINDOW (widget);
6206 else
6207 scroll_data.window = NULL;
6209 scroll_data.dx = dx;
6211 adjust_allocation_recurse (widget, &scroll_data);
6214 static void
6215 hadjustment_value_changed (GtkAdjustment *adjustment,
6216 gpointer data)
6218 GtkCMCList *clist;
6219 GtkContainer *container;
6220 gint i;
6221 gint y = 0;
6222 gint value;
6223 gint dx;
6224 cairo_t *cr;
6226 cm_return_if_fail (adjustment != NULL);
6227 cm_return_if_fail (GTK_IS_CMCLIST (data));
6229 clist = GTK_CMCLIST (data);
6230 container = GTK_CONTAINER (data);
6232 if (adjustment != clist->hadjustment)
6233 return;
6235 value = gtk_adjustment_get_value (adjustment);
6237 dx = -value - clist->hoffset;
6239 if (gtk_widget_get_realized (GTK_WIDGET(clist)))
6240 gdk_window_scroll (clist->title_window, dx, 0);
6242 /* adjust the column button's allocations */
6243 for (i = 0; i < clist->columns; i++)
6244 if (clist->column[i].button)
6245 adjust_allocation (clist->column[i].button, dx);
6247 clist->hoffset = -value;
6249 cr = gdk_cairo_create(clist->clist_window);
6250 cairo_dash_from_add_mode(clist, cr);
6251 cairo_set_line_width(cr, 1.0);
6252 cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
6253 if (gtk_widget_is_drawable (GTK_WIDGET(clist)))
6255 GtkWidget *focus_child = gtk_container_get_focus_child (container);
6257 gdk_window_scroll (clist->clist_window, dx, 0);
6259 if (gtk_widget_get_can_focus(GTK_WIDGET(clist)) &&
6260 gtk_widget_has_focus(GTK_WIDGET(clist)) &&
6261 !focus_child && GTK_CMCLIST_ADD_MODE(clist))
6263 y = ROW_TOP_YPIXEL (clist, clist->focus_row);
6264 cairo_rectangle(cr, 0, y, clist->clist_window_width + 1,
6265 clist->row_height);
6266 cairo_stroke(cr);
6269 if (gtk_widget_get_can_focus(GTK_WIDGET(clist)) &&
6270 gtk_widget_has_focus(GTK_WIDGET(clist)) &&
6271 !focus_child)
6273 if (GTK_CMCLIST_ADD_MODE(clist))
6275 gint focus_row;
6277 focus_row = clist->focus_row;
6278 clist->focus_row = -1;
6279 draw_rows (clist, NULL);
6280 clist->focus_row = focus_row;
6282 cairo_rectangle(cr, 0, y, clist->clist_window_width + 1,
6283 clist->row_height);
6284 cairo_stroke(cr);
6285 return;
6289 cairo_destroy(cr);
6292 /* PRIVATE
6293 * Memory Allocation/Distruction Routines for GtkCMCList stuctures
6295 * functions:
6296 * columns_new
6297 * column_title_new
6298 * columns_delete
6299 * row_new
6300 * row_delete
6302 static GtkCMCListColumn *
6303 columns_new (GtkCMCList *clist)
6305 GtkCMCListColumn *column;
6306 gint i;
6308 column = g_new (GtkCMCListColumn, clist->columns);
6310 for (i = 0; i < clist->columns; i++)
6312 column[i].area.x = 0;
6313 column[i].area.y = 0;
6314 column[i].area.width = 0;
6315 column[i].area.height = 0;
6316 column[i].title = NULL;
6317 column[i].button = NULL;
6318 column[i].window = NULL;
6319 column[i].width = 0;
6320 column[i].min_width = -1;
6321 column[i].max_width = -1;
6322 column[i].visible = TRUE;
6323 column[i].width_set = FALSE;
6324 column[i].resizeable = TRUE;
6325 column[i].auto_resize = FALSE;
6326 column[i].button_passive = FALSE;
6327 column[i].justification = GTK_JUSTIFY_LEFT;
6330 return column;
6333 static void
6334 column_title_new (GtkCMCList *clist,
6335 gint column,
6336 const gchar *title)
6338 g_free (clist->column[column].title);
6340 clist->column[column].title = g_strdup (title);
6343 static void
6344 columns_delete (GtkCMCList *clist)
6346 gint i;
6348 for (i = 0; i < clist->columns; i++)
6349 g_free (clist->column[i].title);
6351 g_free (clist->column);
6354 static GtkCMCListRow *
6355 row_new (GtkCMCList *clist)
6357 int i;
6358 GtkCMCListRow *clist_row;
6360 clist_row = g_slice_new (GtkCMCListRow);
6361 clist_row->cell = g_slice_alloc (sizeof (GtkCMCell) * clist->columns);
6363 for (i = 0; i < clist->columns; i++)
6365 clist_row->cell[i].type = GTK_CMCELL_EMPTY;
6366 clist_row->cell[i].vertical = 0;
6367 clist_row->cell[i].horizontal = 0;
6368 clist_row->cell[i].style = NULL;
6371 clist_row->fg_set = FALSE;
6372 clist_row->bg_set = FALSE;
6373 clist_row->style = NULL;
6374 clist_row->selectable = TRUE;
6375 clist_row->state = GTK_STATE_NORMAL;
6376 clist_row->data = NULL;
6377 clist_row->destroy = NULL;
6379 return clist_row;
6382 static void
6383 row_delete (GtkCMCList *clist,
6384 GtkCMCListRow *clist_row)
6386 gint i;
6388 for (i = 0; i < clist->columns; i++)
6390 GTK_CMCLIST_GET_CLASS (clist)->set_cell_contents
6391 (clist, clist_row, i, GTK_CMCELL_EMPTY, NULL, 0, NULL);
6392 if (clist_row->cell[i].style)
6394 if (gtk_widget_get_realized (GTK_WIDGET(clist)))
6395 gtk_style_detach (clist_row->cell[i].style);
6396 g_object_unref (clist_row->cell[i].style);
6400 if (clist_row->style)
6402 if (gtk_widget_get_realized (GTK_WIDGET(clist)))
6403 gtk_style_detach (clist_row->style);
6404 g_object_unref (clist_row->style);
6407 if (clist_row->destroy)
6408 clist_row->destroy (clist_row->data);
6410 g_slice_free1 (sizeof (GtkCMCell) * clist->columns, clist_row->cell);
6411 g_slice_free (GtkCMCListRow, clist_row);
6414 /* FOCUS FUNCTIONS
6415 * gtk_cmclist_focus_content_area
6416 * gtk_cmclist_focus
6417 * gtk_cmclist_draw_focus
6418 * gtk_cmclist_focus_in
6419 * gtk_cmclist_focus_out
6420 * title_focus
6422 static void
6423 gtk_cmclist_focus_content_area (GtkCMCList *clist)
6425 if (clist->focus_row < 0)
6427 clist->focus_row = 0;
6429 if ((clist->selection_mode == GTK_SELECTION_BROWSE ||
6430 clist->selection_mode == GTK_SELECTION_MULTIPLE) &&
6431 !clist->selection)
6432 g_signal_emit (G_OBJECT (clist),
6433 clist_signals[SELECT_ROW], 0,
6434 clist->focus_row, -1, NULL);
6436 gtk_widget_grab_focus (GTK_WIDGET (clist));
6439 static gboolean
6440 gtk_cmclist_focus (GtkWidget *widget,
6441 GtkDirectionType direction)
6443 GtkCMCList *clist = GTK_CMCLIST (widget);
6444 GtkWidget *focus_child;
6445 gboolean is_current_focus;
6447 if (!gtk_widget_is_sensitive (widget))
6448 return FALSE;
6450 focus_child = gtk_container_get_focus_child (GTK_CONTAINER (widget));
6452 is_current_focus = gtk_widget_is_focus (GTK_WIDGET (clist));
6454 if (focus_child &&
6455 gtk_widget_child_focus (focus_child, direction))
6456 return TRUE;
6458 switch (direction)
6460 case GTK_DIR_LEFT:
6461 case GTK_DIR_RIGHT:
6462 if (focus_child)
6464 if (title_focus_move (clist, direction))
6465 return TRUE;
6467 else if (!is_current_focus)
6469 gtk_cmclist_focus_content_area (clist);
6470 return TRUE;
6472 break;
6473 case GTK_DIR_DOWN:
6474 case GTK_DIR_TAB_FORWARD:
6475 if (!focus_child && !is_current_focus)
6477 if (title_focus_in (clist, direction))
6478 return TRUE;
6481 if (!is_current_focus && clist->rows)
6483 gtk_cmclist_focus_content_area (clist);
6484 return TRUE;
6486 break;
6487 case GTK_DIR_UP:
6488 case GTK_DIR_TAB_BACKWARD:
6489 if (!focus_child && is_current_focus)
6491 if (title_focus_in (clist, direction))
6492 return TRUE;
6495 if (!is_current_focus && !focus_child && clist->rows)
6497 gtk_cmclist_focus_content_area (clist);
6498 return TRUE;
6500 break;
6501 default:
6502 break;
6505 return FALSE;
6508 static void
6509 gtk_cmclist_set_focus_child (GtkContainer *container,
6510 GtkWidget *child)
6512 GtkCMCList *clist = GTK_CMCLIST (container);
6513 gint i;
6515 for (i = 0; i < clist->columns; i++)
6516 if (clist->column[i].button == child)
6517 clist->focus_header_column = i;
6519 if (GTK_CONTAINER_CLASS (gtk_cmclist_parent_class)->set_focus_child)
6520 (*GTK_CONTAINER_CLASS (gtk_cmclist_parent_class)->set_focus_child) (container, child);
6523 static void
6524 gtk_cmclist_draw_focus (GtkWidget *widget)
6526 GtkCMCList *clist;
6527 cairo_t *cr;
6529 cm_return_if_fail (GTK_IS_CMCLIST (widget));
6531 if (!gtk_widget_is_drawable (widget) || !gtk_widget_get_can_focus (widget))
6532 return;
6534 clist = GTK_CMCLIST (widget);
6535 if (clist->focus_row >= 0) {
6536 if (!clist->draw_now) {
6537 cr = gdk_cairo_create(clist->clist_window);
6538 cairo_dash_from_add_mode(clist, cr);
6539 cairo_set_line_width(cr, 1.0);
6540 cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
6541 cairo_rectangle(cr, 0, ROW_TOP_YPIXEL(clist, clist->focus_row) + 0.5,
6542 clist->clist_window_width + 1,
6543 clist->row_height - 0.5);
6544 cairo_stroke(cr);
6545 cairo_destroy(cr);
6546 } else {
6547 gtk_widget_queue_draw(GTK_WIDGET(clist));
6552 static void
6553 gtk_cmclist_undraw_focus (GtkWidget *widget)
6555 GtkCMCList *clist;
6556 int row;
6557 cm_return_if_fail (GTK_IS_CMCLIST (widget));
6559 clist = GTK_CMCLIST(widget);
6561 if (clist->focus_row < 0)
6562 return;
6564 if (!gtk_widget_is_drawable (widget) || !gtk_widget_get_can_focus (widget))
6565 return;
6567 clist = GTK_CMCLIST (widget);
6568 if (clist->focus_row >= 0) {
6569 if (!clist->draw_now) {
6570 cairo_t *cr = gdk_cairo_create(clist->clist_window);
6571 cairo_set_line_width(cr, 1.0);
6572 gdk_cairo_set_source_color(cr, &gtk_widget_get_style(widget)->base[GTK_STATE_NORMAL]);
6573 cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
6574 cairo_rectangle(cr, 0, ROW_TOP_YPIXEL(clist, clist->focus_row) + 0.5,
6575 clist->clist_window_width + 1,
6576 clist->row_height - 0.5);
6577 cairo_stroke(cr);
6578 cairo_destroy(cr);
6579 } else {
6580 gtk_widget_queue_draw(GTK_WIDGET(clist));
6584 row = clist->focus_row;
6585 GTK_CMCLIST_GET_CLASS(GTK_CMCLIST(widget))->draw_row(clist, NULL, row, ROW_ELEMENT (clist, row)->data);
6588 static gint
6589 gtk_cmclist_focus_in (GtkWidget *widget,
6590 GdkEventFocus *event)
6592 GtkCMCList *clist = GTK_CMCLIST (widget);
6594 if (clist->selection_mode == GTK_SELECTION_BROWSE &&
6595 clist->selection == NULL && clist->focus_row > -1)
6597 GList *list;
6599 list = g_list_nth (clist->row_list, clist->focus_row);
6600 if (list && GTK_CMCLIST_ROW (list)->selectable)
6601 g_signal_emit (G_OBJECT (clist), clist_signals[SELECT_ROW], 0,
6602 clist->focus_row, -1, event);
6603 else
6604 gtk_cmclist_draw_focus (widget);
6606 else
6607 gtk_cmclist_undraw_focus (widget);
6609 return FALSE;
6612 static gint
6613 gtk_cmclist_focus_out (GtkWidget *widget,
6614 GdkEventFocus *event)
6616 GtkCMCList *clist = GTK_CMCLIST (widget);
6618 gtk_cmclist_undraw_focus (widget);
6620 GTK_CMCLIST_GET_CLASS (widget)->resync_selection (clist, (GdkEvent *) event);
6622 return FALSE;
6625 static gboolean
6626 focus_column (GtkCMCList *clist, gint column, gint dir)
6628 GtkWidget *child = clist->column[column].button;
6630 if (gtk_widget_child_focus (child, dir))
6632 return TRUE;
6634 else if (gtk_widget_get_can_focus (child))
6636 gtk_widget_grab_focus (child);
6637 return TRUE;
6640 return FALSE;
6643 /* Focus moved onto the headers. Focus first focusable and visible child.
6644 * (FIXME: focus the last focused child if visible)
6646 static gboolean
6647 title_focus_in (GtkCMCList *clist, gint dir)
6649 gint i;
6650 gint left, right;
6652 if (!GTK_CMCLIST_SHOW_TITLES (clist))
6653 return FALSE;
6655 /* Check last focused column */
6656 if (clist->focus_header_column != -1)
6658 i = clist->focus_header_column;
6660 left = COLUMN_LEFT_XPIXEL (clist, i);
6661 right = left + clist->column[i].area.width;
6663 if (left >= 0 && right <= clist->clist_window_width)
6665 if (focus_column (clist, i, dir))
6666 return TRUE;
6670 /* Check fully visible columns */
6671 for (i = 0 ; i < clist->columns ; i++)
6673 left = COLUMN_LEFT_XPIXEL (clist, i);
6674 right = left + clist->column[i].area.width;
6676 if (left >= 0 && right <= clist->clist_window_width)
6678 if (focus_column (clist, i, dir))
6679 return TRUE;
6683 /* Check partially visible columns */
6684 for (i = 0 ; i < clist->columns ; i++)
6686 left = COLUMN_LEFT_XPIXEL (clist, i);
6687 right = left + clist->column[i].area.width;
6689 if ((left < 0 && right > 0) ||
6690 (left < clist->clist_window_width && right > clist->clist_window_width))
6692 if (focus_column (clist, i, dir))
6693 return TRUE;
6697 return FALSE;
6700 /* Move the focus right or left within the title buttons, scrolling
6701 * as necessary to keep the focused child visible.
6703 static gboolean
6704 title_focus_move (GtkCMCList *clist,
6705 gint dir)
6707 GtkWidget *focus_child;
6708 gboolean return_val = FALSE;
6709 gint d = 0;
6710 gint i = -1;
6711 gint j;
6713 if (!GTK_CMCLIST_SHOW_TITLES(clist))
6714 return FALSE;
6716 focus_child = gtk_container_get_focus_child (GTK_CONTAINER (clist));
6717 g_assert (focus_child);
6719 /* Movement direction within headers
6721 switch (dir)
6723 case GTK_DIR_RIGHT:
6724 d = 1;
6725 break;
6726 case GTK_DIR_LEFT:
6727 d = -1;
6728 break;
6731 for (i = 0; i < clist->columns; i++)
6732 if (clist->column[i].button == focus_child)
6733 break;
6735 g_assert (i != -1); /* Have a starting column */
6737 j = i + d;
6738 while (!return_val && j >= 0 && j < clist->columns)
6740 if (clist->column[j].button &&
6741 gtk_widget_get_visible (clist->column[j].button))
6743 if (focus_column (clist, j, dir))
6745 return_val = TRUE;
6746 break;
6749 j += d;
6752 /* If we didn't find it, wrap around and keep looking
6754 if (!return_val)
6756 j = d > 0 ? 0 : clist->columns - 1;
6758 while (!return_val && j != i)
6760 if (clist->column[j].button &&
6761 gtk_widget_get_visible (clist->column[j].button))
6763 if (focus_column (clist, j, dir))
6765 return_val = TRUE;
6766 break;
6769 j += d;
6773 /* Scroll horizontally so focused column is visible
6775 if (return_val)
6777 if (COLUMN_LEFT_XPIXEL (clist, j) < CELL_SPACING + COLUMN_INSET)
6778 gtk_cmclist_moveto (clist, -1, j, 0, 0);
6779 else if (COLUMN_LEFT_XPIXEL(clist, j) + clist->column[j].area.width >
6780 clist->clist_window_width)
6782 gint last_column;
6784 for (last_column = clist->columns - 1;
6785 last_column >= 0 && !clist->column[last_column].visible; last_column--);
6787 if (j == last_column)
6788 gtk_cmclist_moveto (clist, -1, j, 0, 0);
6789 else
6790 gtk_cmclist_moveto (clist, -1, j, 0, 1);
6793 return TRUE; /* Even if we didn't find a new one, we can keep the
6794 * focus in the same place.
6798 /* PRIVATE SCROLLING FUNCTIONS
6799 * move_focus_row
6800 * scroll_horizontal
6801 * scroll_vertical
6802 * move_horizontal
6803 * move_vertical
6804 * horizontal_timeout
6805 * vertical_timeout
6806 * remove_grab
6808 static void
6809 move_focus_row (GtkCMCList *clist,
6810 GtkScrollType scroll_type,
6811 gfloat position)
6813 GtkWidget *widget;
6815 cm_return_if_fail (clist != 0);
6816 cm_return_if_fail (GTK_IS_CMCLIST (clist));
6818 widget = GTK_WIDGET (clist);
6820 switch (scroll_type)
6822 case GTK_SCROLL_STEP_UP:
6823 case GTK_SCROLL_STEP_BACKWARD:
6824 if (clist->focus_row <= 0)
6825 return;
6826 gtk_cmclist_undraw_focus (widget);
6827 clist->focus_row--;
6828 gtk_cmclist_draw_focus (widget);
6829 break;
6831 case GTK_SCROLL_STEP_DOWN:
6832 case GTK_SCROLL_STEP_FORWARD:
6833 if (clist->focus_row >= clist->rows - 1)
6834 return;
6835 gtk_cmclist_undraw_focus (widget);
6836 clist->focus_row++;
6837 gtk_cmclist_draw_focus (widget);
6838 break;
6839 case GTK_SCROLL_PAGE_UP:
6840 case GTK_SCROLL_PAGE_BACKWARD:
6841 if (clist->focus_row <= 0)
6842 return;
6843 gtk_cmclist_undraw_focus (widget);
6844 clist->focus_row = MAX (0, clist->focus_row -
6845 (2 * clist->clist_window_height -
6846 clist->row_height - CELL_SPACING) /
6847 (2 * (clist->row_height + CELL_SPACING)));
6848 gtk_cmclist_draw_focus (widget);
6849 break;
6850 case GTK_SCROLL_PAGE_DOWN:
6851 case GTK_SCROLL_PAGE_FORWARD:
6852 if (clist->focus_row >= clist->rows - 1)
6853 return;
6854 gtk_cmclist_undraw_focus (widget);
6855 clist->focus_row = MIN (clist->rows - 1, clist->focus_row +
6856 (2 * clist->clist_window_height -
6857 clist->row_height - CELL_SPACING) /
6858 (2 * (clist->row_height + CELL_SPACING)));
6859 gtk_cmclist_draw_focus (widget);
6860 break;
6861 case GTK_SCROLL_JUMP:
6862 if (position >= 0 && position <= 1)
6864 gint row = position * (clist->rows - 1);
6866 if (row == clist->focus_row)
6867 return;
6869 gtk_cmclist_undraw_focus (widget);
6870 clist->focus_row = row;
6871 gtk_cmclist_draw_focus (widget);
6873 break;
6874 default:
6875 break;
6879 static void
6880 scroll_horizontal (GtkCMCList *clist,
6881 GtkScrollType scroll_type,
6882 gfloat position)
6884 gint column = 0;
6885 gint last_column;
6887 cm_return_if_fail (clist != 0);
6888 cm_return_if_fail (GTK_IS_CMCLIST (clist));
6890 if (clist_has_grab (clist))
6891 return;
6893 for (last_column = clist->columns - 1;
6894 last_column >= 0 && !clist->column[last_column].visible; last_column--)
6897 switch (scroll_type)
6899 case GTK_SCROLL_STEP_BACKWARD:
6900 column = COLUMN_FROM_XPIXEL (clist, 0);
6901 if (COLUMN_LEFT_XPIXEL (clist, column) - CELL_SPACING - COLUMN_INSET >= 0
6902 && column > 0)
6903 column--;
6904 break;
6905 case GTK_SCROLL_STEP_FORWARD:
6906 column = COLUMN_FROM_XPIXEL (clist, clist->clist_window_width);
6907 if (column < 0)
6908 return;
6909 if (COLUMN_LEFT_XPIXEL (clist, column) +
6910 clist->column[column].area.width +
6911 CELL_SPACING + COLUMN_INSET - 1 <= clist->clist_window_width &&
6912 column < last_column)
6913 column++;
6914 break;
6915 case GTK_SCROLL_PAGE_BACKWARD:
6916 case GTK_SCROLL_PAGE_FORWARD:
6917 return;
6918 case GTK_SCROLL_JUMP:
6919 if (position >= 0 && position <= 1)
6921 gint vis_columns = 0;
6922 gint i;
6924 for (i = 0; i <= last_column; i++)
6925 if (clist->column[i].visible)
6926 vis_columns++;
6928 column = position * vis_columns;
6930 for (i = 0; i <= last_column && column > 0; i++)
6931 if (clist->column[i].visible)
6932 column--;
6934 column = i;
6936 else
6937 return;
6938 break;
6939 default:
6940 break;
6943 if (COLUMN_LEFT_XPIXEL (clist, column) < CELL_SPACING + COLUMN_INSET)
6944 gtk_cmclist_moveto (clist, -1, column, 0, 0);
6945 else if (COLUMN_LEFT_XPIXEL (clist, column) + CELL_SPACING + COLUMN_INSET - 1
6946 + clist->column[column].area.width > clist->clist_window_width)
6948 if (column == last_column)
6949 gtk_cmclist_moveto (clist, -1, column, 0, 0);
6950 else
6951 gtk_cmclist_moveto (clist, -1, column, 0, 1);
6955 static void
6956 scroll_vertical (GtkCMCList *clist,
6957 GtkScrollType scroll_type,
6958 gfloat position)
6960 gint old_focus_row;
6962 cm_return_if_fail (GTK_IS_CMCLIST (clist));
6964 if (clist_has_grab (clist))
6965 return;
6967 switch (clist->selection_mode)
6969 case GTK_SELECTION_MULTIPLE:
6970 if (clist->anchor >= 0)
6971 return;
6972 case GTK_SELECTION_BROWSE:
6974 old_focus_row = clist->focus_row;
6975 move_focus_row (clist, scroll_type, position);
6977 if (old_focus_row != clist->focus_row)
6979 if (clist->selection_mode == GTK_SELECTION_BROWSE)
6980 g_signal_emit (G_OBJECT (clist), clist_signals[UNSELECT_ROW], 0,
6981 old_focus_row, -1, NULL);
6982 else if (!GTK_CMCLIST_ADD_MODE(clist))
6984 gtk_cmclist_unselect_all (clist);
6985 clist->undo_anchor = old_focus_row;
6989 switch (gtk_cmclist_row_is_visible (clist, clist->focus_row))
6991 case GTK_VISIBILITY_NONE:
6992 if (old_focus_row != clist->focus_row &&
6993 !(clist->selection_mode == GTK_SELECTION_MULTIPLE &&
6994 GTK_CMCLIST_ADD_MODE(clist)))
6995 g_signal_emit (G_OBJECT (clist), clist_signals[SELECT_ROW], 0,
6996 clist->focus_row, -1, NULL);
6997 switch (scroll_type)
6999 case GTK_SCROLL_PAGE_UP:
7000 case GTK_SCROLL_STEP_UP:
7001 case GTK_SCROLL_STEP_BACKWARD:
7002 case GTK_SCROLL_PAGE_BACKWARD:
7003 gtk_cmclist_moveto (clist, clist->focus_row, -1, 0, 0);
7004 break;
7005 case GTK_SCROLL_PAGE_DOWN:
7006 case GTK_SCROLL_STEP_DOWN:
7007 case GTK_SCROLL_STEP_FORWARD:
7008 case GTK_SCROLL_PAGE_FORWARD:
7009 gtk_cmclist_moveto (clist, clist->focus_row, -1, 1, 0);
7010 break;
7011 case GTK_SCROLL_JUMP:
7012 gtk_cmclist_moveto (clist, clist->focus_row, -1, 0.5, 0);
7013 break;
7014 default:
7015 break;
7017 break;
7018 case GTK_VISIBILITY_PARTIAL:
7019 switch (scroll_type)
7021 case GTK_SCROLL_STEP_BACKWARD:
7022 case GTK_SCROLL_PAGE_BACKWARD:
7023 gtk_cmclist_moveto (clist, clist->focus_row, -1, 0, 0);
7024 break;
7025 case GTK_SCROLL_STEP_FORWARD:
7026 case GTK_SCROLL_PAGE_FORWARD:
7027 gtk_cmclist_moveto (clist, clist->focus_row, -1, 1, 0);
7028 break;
7029 case GTK_SCROLL_JUMP:
7030 gtk_cmclist_moveto (clist, clist->focus_row, -1, 0.5, 0);
7031 break;
7032 default:
7033 break;
7035 /* fallback is intentional */
7036 default:
7037 if (old_focus_row != clist->focus_row &&
7038 !(clist->selection_mode == GTK_SELECTION_MULTIPLE &&
7039 GTK_CMCLIST_ADD_MODE(clist)))
7040 g_signal_emit (G_OBJECT (clist), clist_signals[SELECT_ROW], 0,
7041 clist->focus_row, -1, NULL);
7042 break;
7044 break;
7045 default:
7046 move_focus_row (clist, scroll_type, position);
7048 if (ROW_TOP_YPIXEL (clist, clist->focus_row) + clist->row_height >
7049 clist->clist_window_height)
7050 gtk_cmclist_moveto (clist, clist->focus_row, -1, 1, 0);
7051 else if (ROW_TOP_YPIXEL (clist, clist->focus_row) < 0)
7052 gtk_cmclist_moveto (clist, clist->focus_row, -1, 0, 0);
7053 break;
7057 static void
7058 move_horizontal (GtkCMCList *clist,
7059 gint diff)
7061 gdouble value;
7063 if (!clist->hadjustment)
7064 return;
7066 value = CLAMP (gtk_adjustment_get_value (clist->hadjustment) + diff, 0.0,
7067 gtk_adjustment_get_upper (clist->hadjustment) -
7068 gtk_adjustment_get_page_size (clist->hadjustment));
7069 gtk_adjustment_set_value (clist->hadjustment, value);
7072 static void
7073 move_vertical (GtkCMCList *clist,
7074 gint row,
7075 gfloat align)
7077 gdouble value;
7078 gdouble upper;
7079 gdouble page_size;
7081 if (!clist->vadjustment)
7082 return;
7084 value = (ROW_TOP_YPIXEL (clist, row) - clist->voffset -
7085 align * (clist->clist_window_height - clist->row_height) +
7086 (2 * align - 1) * CELL_SPACING);
7088 upper = gtk_adjustment_get_upper (clist->vadjustment);
7089 page_size = gtk_adjustment_get_page_size (clist->vadjustment);
7090 if ((value + page_size) > upper)
7091 value = upper - page_size;
7093 gtk_adjustment_set_value (clist->vadjustment, value);
7096 static void
7097 do_fake_motion (GtkWidget *widget)
7099 GdkEvent *event = gdk_event_new (GDK_MOTION_NOTIFY);
7101 event->motion.send_event = TRUE;
7103 gtk_cmclist_motion (widget, (GdkEventMotion *)event);
7104 gdk_event_free (event);
7107 static gint
7108 horizontal_timeout (GtkCMCList *clist)
7110 clist->htimer = 0;
7111 do_fake_motion (GTK_WIDGET (clist));
7113 return FALSE;
7116 static gint
7117 vertical_timeout (GtkCMCList *clist)
7119 clist->vtimer = 0;
7120 do_fake_motion (GTK_WIDGET (clist));
7122 return FALSE;
7125 static void
7126 remove_grab (GtkCMCList *clist)
7128 GtkWidget *widget = GTK_WIDGET (clist);
7130 if (gtk_widget_has_grab (widget))
7132 GdkDisplay *display = gtk_widget_get_display (widget);
7134 gtk_grab_remove (widget);
7135 if (gtkut_pointer_is_grabbed (widget))
7136 gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
7139 if (clist->htimer)
7141 g_source_remove (clist->htimer);
7142 clist->htimer = 0;
7145 if (clist->vtimer)
7147 g_source_remove (clist->vtimer);
7148 clist->vtimer = 0;
7152 /* PUBLIC SORTING FUNCTIONS
7153 * gtk_cmclist_sort
7154 * gtk_cmclist_set_compare_func
7155 * gtk_cmclist_set_auto_sort
7156 * gtk_cmclist_set_sort_type
7157 * gtk_cmclist_set_sort_column
7159 void
7160 gtk_cmclist_sort (GtkCMCList *clist)
7162 cm_return_if_fail (GTK_IS_CMCLIST (clist));
7164 GTK_CMCLIST_GET_CLASS (clist)->sort_list (clist);
7167 void
7168 gtk_cmclist_set_compare_func (GtkCMCList *clist,
7169 GtkCMCListCompareFunc cmp_func)
7171 cm_return_if_fail (GTK_IS_CMCLIST (clist));
7173 clist->compare = (cmp_func) ? cmp_func : default_compare;
7176 void
7177 gtk_cmclist_set_auto_sort (GtkCMCList *clist,
7178 gboolean auto_sort)
7180 cm_return_if_fail (GTK_IS_CMCLIST (clist));
7182 if (GTK_CMCLIST_AUTO_SORT(clist) && !auto_sort)
7183 GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_AUTO_SORT);
7184 else if (!GTK_CMCLIST_AUTO_SORT(clist) && auto_sort)
7186 GTK_CMCLIST_SET_FLAG (clist, CMCLIST_AUTO_SORT);
7187 gtk_cmclist_sort (clist);
7191 void
7192 gtk_cmclist_set_sort_type (GtkCMCList *clist,
7193 GtkSortType sort_type)
7195 cm_return_if_fail (GTK_IS_CMCLIST (clist));
7197 clist->sort_type = sort_type;
7200 void
7201 gtk_cmclist_set_sort_column (GtkCMCList *clist,
7202 gint column)
7204 cm_return_if_fail (GTK_IS_CMCLIST (clist));
7206 if (column < 0 || column >= clist->columns)
7207 return;
7209 clist->sort_column = column;
7212 /* PRIVATE SORTING FUNCTIONS
7213 * default_compare
7214 * real_sort_list
7215 * gtk_cmclist_merge
7216 * gtk_cmclist_mergesort
7218 static gint
7219 default_compare (GtkCMCList *clist,
7220 gconstpointer ptr1,
7221 gconstpointer ptr2)
7223 char *text1 = NULL;
7224 char *text2 = NULL;
7226 GtkCMCListRow *row1 = (GtkCMCListRow *) ptr1;
7227 GtkCMCListRow *row2 = (GtkCMCListRow *) ptr2;
7229 switch (row1->cell[clist->sort_column].type)
7231 case GTK_CMCELL_TEXT:
7232 text1 = GTK_CMCELL_TEXT (row1->cell[clist->sort_column])->text;
7233 break;
7234 case GTK_CMCELL_PIXTEXT:
7235 text1 = GTK_CMCELL_PIXTEXT (row1->cell[clist->sort_column])->text;
7236 break;
7237 default:
7238 break;
7241 switch (row2->cell[clist->sort_column].type)
7243 case GTK_CMCELL_TEXT:
7244 text2 = GTK_CMCELL_TEXT (row2->cell[clist->sort_column])->text;
7245 break;
7246 case GTK_CMCELL_PIXTEXT:
7247 text2 = GTK_CMCELL_PIXTEXT (row2->cell[clist->sort_column])->text;
7248 break;
7249 default:
7250 break;
7253 if (!text2)
7254 return (text1 != NULL);
7256 if (!text1)
7257 return -1;
7259 return strcmp (text1, text2);
7262 static void
7263 real_sort_list (GtkCMCList *clist)
7265 GList *list;
7266 GList *work;
7267 gint i;
7269 cm_return_if_fail (GTK_IS_CMCLIST (clist));
7271 if (clist->rows <= 1)
7272 return;
7274 if (clist_has_grab (clist))
7275 return;
7277 gtk_cmclist_freeze (clist);
7279 if (clist->anchor != -1 && clist->selection_mode == GTK_SELECTION_MULTIPLE)
7281 GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
7282 g_list_free (clist->undo_selection);
7283 g_list_free (clist->undo_unselection);
7284 clist->undo_selection = NULL;
7285 clist->undo_unselection = NULL;
7288 clist->row_list = gtk_cmclist_mergesort (clist, clist->row_list, clist->rows);
7290 work = clist->selection;
7292 for (i = 0, list = clist->row_list; i < clist->rows; i++, list = list->next)
7294 if (GTK_CMCLIST_ROW (list)->state == GTK_STATE_SELECTED)
7296 work->data = GINT_TO_POINTER (i);
7297 work = work->next;
7300 if (i == clist->rows - 1)
7301 clist->row_list_end = list;
7304 gtk_cmclist_thaw (clist);
7307 static GList *
7308 gtk_cmclist_merge (GtkCMCList *clist,
7309 GList *a, /* first list to merge */
7310 GList *b) /* second list to merge */
7312 GList z = { 0 }; /* auxiliary node */
7313 GList *c;
7314 gint cmp;
7316 c = &z;
7318 if (!a && !b)
7319 return NULL;
7321 while (a || b)
7323 if (a && !b)
7325 c->next = a;
7326 a->prev = c;
7327 c = a;
7328 a = a->next;
7329 break;
7331 else if (!a && b)
7333 c->next = b;
7334 b->prev = c;
7335 c = b;
7336 b = b->next;
7337 break;
7339 else /* a && b */
7341 cmp = clist->compare (clist, GTK_CMCLIST_ROW (a), GTK_CMCLIST_ROW (b));
7342 if ((cmp >= 0 && clist->sort_type == GTK_SORT_DESCENDING) ||
7343 (cmp <= 0 && clist->sort_type == GTK_SORT_ASCENDING))
7345 c->next = a;
7346 a->prev = c;
7347 c = a;
7348 a = a->next;
7350 else
7352 c->next = b;
7353 b->prev = c;
7354 c = b;
7355 b = b->next;
7360 if (z.next)
7361 z.next->prev = NULL;
7363 return z.next;
7366 static GList *
7367 gtk_cmclist_mergesort (GtkCMCList *clist,
7368 GList *list, /* the list to sort */
7369 gint num) /* the list's length */
7371 GList *half;
7372 gint i;
7374 if (num <= 1)
7376 return list;
7378 else
7380 /* move "half" to the middle */
7381 half = list;
7382 for (i = 0; i < num / 2; i++)
7383 half = half->next;
7385 /* cut the list in two */
7386 half->prev->next = NULL;
7387 half->prev = NULL;
7389 /* recursively sort both lists */
7390 return gtk_cmclist_merge (clist,
7391 gtk_cmclist_mergesort (clist, list, num / 2),
7392 gtk_cmclist_mergesort (clist, half, num - num / 2));
7396 /************************/
7398 static void
7399 drag_source_info_destroy (gpointer data)
7401 GtkCMCListCellInfo *info = data;
7403 g_free (info);
7406 static void
7407 drag_dest_info_destroy (gpointer data)
7409 GtkCMCListDestInfo *info = data;
7411 g_free (info);
7414 static void
7415 drag_dest_cell (GtkCMCList *clist,
7416 gint x,
7417 gint y,
7418 GtkCMCListDestInfo *dest_info)
7420 GtkWidget *widget;
7421 GtkStyle *style;
7422 guint border_width;
7424 widget = GTK_WIDGET (clist);
7425 style = gtk_widget_get_style (widget);
7427 dest_info->insert_pos = GTK_CMCLIST_DRAG_NONE;
7429 border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
7430 y -= (border_width +
7431 style->ythickness +
7432 clist->column_title_area.height);
7434 dest_info->cell.row = ROW_FROM_YPIXEL (clist, y);
7435 if (dest_info->cell.row >= clist->rows)
7437 dest_info->cell.row = clist->rows - 1;
7438 y = ROW_TOP_YPIXEL (clist, dest_info->cell.row) + clist->row_height;
7440 if (dest_info->cell.row < -1)
7441 dest_info->cell.row = -1;
7443 x -= border_width + style->xthickness;
7445 dest_info->cell.column = COLUMN_FROM_XPIXEL (clist, x);
7447 if (dest_info->cell.row >= 0)
7449 gint y_delta;
7450 gint h = 0;
7452 y_delta = y - ROW_TOP_YPIXEL (clist, dest_info->cell.row);
7454 if (GTK_CMCLIST_DRAW_DRAG_RECT(clist))
7456 dest_info->insert_pos = GTK_CMCLIST_DRAG_INTO;
7457 h = clist->row_height / 4;
7459 else if (GTK_CMCLIST_DRAW_DRAG_LINE(clist))
7461 dest_info->insert_pos = GTK_CMCLIST_DRAG_BEFORE;
7462 h = clist->row_height / 2;
7465 if (GTK_CMCLIST_DRAW_DRAG_LINE(clist))
7467 if (y_delta < h)
7468 dest_info->insert_pos = GTK_CMCLIST_DRAG_BEFORE;
7469 else if (clist->row_height - y_delta < h)
7470 dest_info->insert_pos = GTK_CMCLIST_DRAG_AFTER;
7475 static void
7476 gtk_cmclist_drag_begin (GtkWidget *widget,
7477 GdkDragContext *context)
7479 GtkCMCList *clist;
7480 GtkCMCListCellInfo *info;
7482 cm_return_if_fail (GTK_IS_CMCLIST (widget));
7483 cm_return_if_fail (context != NULL);
7485 clist = GTK_CMCLIST (widget);
7487 clist->drag_button = 0;
7488 remove_grab (clist);
7490 switch (clist->selection_mode)
7492 case GTK_SELECTION_MULTIPLE:
7493 update_extended_selection (clist, clist->focus_row);
7494 GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
7495 break;
7496 case GTK_SELECTION_SINGLE:
7497 clist->anchor = -1;
7498 case GTK_SELECTION_BROWSE:
7499 break;
7500 default:
7501 g_assert_not_reached ();
7504 info = g_dataset_get_data (context, "gtk-clist-drag-source");
7506 if (!info)
7508 info = g_new (GtkCMCListCellInfo, 1);
7510 if (clist->click_cell.row < 0)
7511 clist->click_cell.row = 0;
7512 else if (clist->click_cell.row >= clist->rows)
7513 clist->click_cell.row = clist->rows - 1;
7514 info->row = clist->click_cell.row;
7515 info->column = clist->click_cell.column;
7517 g_dataset_set_data_full (context, "gtk-clist-drag-source", info,
7518 drag_source_info_destroy);
7521 if (GTK_CMCLIST_USE_DRAG_ICONS (clist))
7522 gtk_drag_set_icon_default (context);
7525 static void
7526 gtk_cmclist_drag_end (GtkWidget *widget,
7527 GdkDragContext *context)
7529 GtkCMCList *clist;
7531 cm_return_if_fail (GTK_IS_CMCLIST (widget));
7532 cm_return_if_fail (context != NULL);
7534 clist = GTK_CMCLIST (widget);
7536 clist->click_cell.row = -1;
7537 clist->click_cell.column = -1;
7540 static void
7541 gtk_cmclist_drag_leave (GtkWidget *widget,
7542 GdkDragContext *context,
7543 guint time)
7545 GtkCMCList *clist;
7546 GtkCMCListDestInfo *dest_info;
7548 cm_return_if_fail (GTK_IS_CMCLIST (widget));
7549 cm_return_if_fail (context != NULL);
7551 clist = GTK_CMCLIST (widget);
7553 dest_info = g_dataset_get_data (context, "gtk-clist-drag-dest");
7555 if (dest_info)
7557 if (dest_info->cell.row >= 0 &&
7558 GTK_CMCLIST_REORDERABLE(clist) &&
7559 gtk_drag_get_source_widget (context) == widget)
7561 GdkAtom atom = gdk_atom_intern_static_string ("gtk-clist-drag-reorder");
7562 GdkAtom found = gtk_drag_dest_find_target(widget, context, NULL);
7564 if (atom == found)
7566 clist->drag_highlight_row = -1;
7569 g_dataset_remove_data (context, "gtk-clist-drag-dest");
7573 static gint
7574 gtk_cmclist_drag_motion (GtkWidget *widget,
7575 GdkDragContext *context,
7576 gint x,
7577 gint y,
7578 guint time)
7580 GtkCMCList *clist;
7581 GtkCMCListDestInfo new_info;
7582 GtkCMCListDestInfo *dest_info;
7584 cm_return_val_if_fail (GTK_IS_CMCLIST (widget), FALSE);
7586 clist = GTK_CMCLIST (widget);
7588 dest_info = g_dataset_get_data (context, "gtk-clist-drag-dest");
7590 if (!dest_info)
7592 dest_info = g_new (GtkCMCListDestInfo, 1);
7594 dest_info->insert_pos = GTK_CMCLIST_DRAG_NONE;
7595 dest_info->cell.row = -1;
7596 dest_info->cell.column = -1;
7598 g_dataset_set_data_full (context, "gtk-clist-drag-dest", dest_info,
7599 drag_dest_info_destroy);
7602 drag_dest_cell (clist, x, y, &new_info);
7604 if (GTK_CMCLIST_REORDERABLE (clist))
7606 GdkAtom atom = gdk_atom_intern_static_string ("gtk-clist-drag-reorder");
7607 GdkAtom found = gtk_drag_dest_find_target(widget, context, NULL);
7609 if (atom == found)
7611 if (gtk_drag_get_source_widget (context) != widget ||
7612 new_info.insert_pos == GTK_CMCLIST_DRAG_NONE ||
7613 new_info.cell.row == clist->click_cell.row ||
7614 (new_info.cell.row == clist->click_cell.row - 1 &&
7615 new_info.insert_pos == GTK_CMCLIST_DRAG_AFTER) ||
7616 (new_info.cell.row == clist->click_cell.row + 1 &&
7617 new_info.insert_pos == GTK_CMCLIST_DRAG_BEFORE))
7619 if (dest_info->cell.row < 0)
7621 gdk_drag_status (context, GDK_ACTION_DEFAULT, time);
7622 return FALSE;
7624 return TRUE;
7627 if (new_info.cell.row != dest_info->cell.row ||
7628 (new_info.cell.row == dest_info->cell.row &&
7629 dest_info->insert_pos != new_info.insert_pos))
7632 dest_info->insert_pos = new_info.insert_pos;
7633 dest_info->cell.row = new_info.cell.row;
7634 dest_info->cell.column = new_info.cell.column;
7636 clist->drag_highlight_row = dest_info->cell.row;
7637 clist->drag_highlight_pos = dest_info->insert_pos;
7639 gdk_drag_status (context,
7640 gdk_drag_context_get_suggested_action(context), time);
7642 return TRUE;
7646 dest_info->insert_pos = new_info.insert_pos;
7647 dest_info->cell.row = new_info.cell.row;
7648 dest_info->cell.column = new_info.cell.column;
7649 return TRUE;
7652 static gboolean
7653 gtk_cmclist_drag_drop (GtkWidget *widget,
7654 GdkDragContext *context,
7655 gint x,
7656 gint y,
7657 guint time)
7659 cm_return_val_if_fail (GTK_IS_CMCLIST (widget), FALSE);
7660 cm_return_val_if_fail (context != NULL, FALSE);
7662 if (GTK_CMCLIST_REORDERABLE (widget) &&
7663 gtk_drag_get_source_widget (context) == widget)
7665 GdkAtom atom = gdk_atom_intern_static_string ("gtk-clist-drag-reorder");
7666 GdkAtom found = gtk_drag_dest_find_target(widget, context, NULL);
7668 if (atom == found)
7669 return TRUE;
7671 return FALSE;
7674 static void
7675 gtk_cmclist_drag_data_received (GtkWidget *widget,
7676 GdkDragContext *context,
7677 gint x,
7678 gint y,
7679 GtkSelectionData *selection_data,
7680 guint info,
7681 guint time)
7683 GtkCMCList *clist;
7685 cm_return_if_fail (GTK_IS_CMCLIST (widget));
7686 cm_return_if_fail (context != NULL);
7687 cm_return_if_fail (selection_data != NULL);
7689 clist = GTK_CMCLIST (widget);
7691 if (GTK_CMCLIST_REORDERABLE (clist) &&
7692 gtk_drag_get_source_widget (context) == widget &&
7693 gtk_selection_data_get_target (selection_data) ==
7694 gdk_atom_intern_static_string ("gtk-clist-drag-reorder") &&
7695 gtk_selection_data_get_format (selection_data) == 8 &&
7696 gtk_selection_data_get_length (selection_data) == sizeof (GtkCMCListCellInfo))
7698 GtkCMCListCellInfo *source_info;
7700 source_info = (GtkCMCListCellInfo *)(gtk_selection_data_get_data (selection_data));
7701 if (source_info)
7703 GtkCMCListDestInfo dest_info;
7705 drag_dest_cell (clist, x, y, &dest_info);
7707 if (dest_info.insert_pos == GTK_CMCLIST_DRAG_AFTER)
7708 dest_info.cell.row++;
7709 if (source_info->row < dest_info.cell.row)
7710 dest_info.cell.row--;
7711 if (dest_info.cell.row != source_info->row)
7712 gtk_cmclist_row_move (clist, source_info->row, dest_info.cell.row);
7714 g_dataset_remove_data (context, "gtk-clist-drag-dest");
7719 static void
7720 gtk_cmclist_drag_data_get (GtkWidget *widget,
7721 GdkDragContext *context,
7722 GtkSelectionData *selection_data,
7723 guint info,
7724 guint time)
7726 GdkAtom target;
7727 cm_return_if_fail (GTK_IS_CMCLIST (widget));
7728 cm_return_if_fail (context != NULL);
7729 cm_return_if_fail (selection_data != NULL);
7731 target = gtk_selection_data_get_target (selection_data);
7732 if (target == gdk_atom_intern_static_string ("gtk-clist-drag-reorder"))
7734 GtkCMCListCellInfo *info;
7736 info = g_dataset_get_data (context, "gtk-clist-drag-source");
7738 if (info)
7740 GtkCMCListCellInfo ret_info;
7742 ret_info.row = info->row;
7743 ret_info.column = info->column;
7745 gtk_selection_data_set (selection_data, target,
7746 8, (guchar *) &ret_info,
7747 sizeof (GtkCMCListCellInfo));
7752 void
7753 gtk_cmclist_set_reorderable (GtkCMCList *clist,
7754 gboolean reorderable)
7756 GtkWidget *widget;
7758 cm_return_if_fail (GTK_IS_CMCLIST (clist));
7760 if ((GTK_CMCLIST_REORDERABLE(clist) != 0) == reorderable)
7761 return;
7763 widget = GTK_WIDGET (clist);
7765 if (reorderable)
7767 GTK_CMCLIST_SET_FLAG (clist, CMCLIST_REORDERABLE);
7768 gtk_drag_dest_set (widget,
7769 GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
7770 &clist_target_table, 1, GDK_ACTION_MOVE);
7772 else
7774 GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_REORDERABLE);
7775 gtk_drag_dest_unset (GTK_WIDGET (clist));
7779 void
7780 gtk_cmclist_set_use_drag_icons (GtkCMCList *clist,
7781 gboolean use_icons)
7783 cm_return_if_fail (GTK_IS_CMCLIST (clist));
7785 if (use_icons != 0)
7786 GTK_CMCLIST_SET_FLAG (clist, CMCLIST_USE_DRAG_ICONS);
7787 else
7788 GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_USE_DRAG_ICONS);
7791 void
7792 gtk_cmclist_set_button_actions (GtkCMCList *clist,
7793 guint button,
7794 guint8 button_actions)
7796 cm_return_if_fail (GTK_IS_CMCLIST (clist));
7798 if (button < MAX_BUTTON)
7800 if (gtkut_pointer_is_grabbed (GTK_WIDGET(clist)) ||
7801 gtk_widget_has_grab (GTK_WIDGET(clist)))
7803 remove_grab (clist);
7804 clist->drag_button = 0;
7807 GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
7809 clist->button_actions[button] = button_actions;
7813 static gboolean
7814 gtk_cmclist_get_border (GtkScrollable *scrollable,
7815 GtkBorder *border)
7817 GtkCMCList *cmclist = GTK_CMCLIST(scrollable);
7819 border->top = cmclist->column_title_area.height;
7820 return TRUE;
7823 static void
7824 gtk_cmclist_scrollable_init (GtkScrollableInterface *iface)
7826 iface->get_border = gtk_cmclist_get_border;