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>
4  *
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.
9  *
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.
14  *
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.
19  */
20 
21 /*
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/.
26  */
27 
28 #include "config.h"
29 
30 #include <stdlib.h>
31 #include <string.h>
32 
33 #undef GDK_DISABLE_DEPRECATED
34 #undef GTK_DISABLE_DEPRECATED
35 #define __GTK_CLIST_C__
36 
37 #include <gdk/gdkkeysyms.h>
38 
39 #include "gtkmain.h"
40 #include "gtkobject.h"
41 #include "gtkctree.h"
42 #include "gtkclist.h"
43 #include "gtkbindings.h"
44 #include "gtkdnd.h"
45 #include "gtkmarshalers.h"
46 #include "gtkintl.h"
47 
48 #include "gtkalias.h"
49 
50 /* length of button_actions array */
51 #define MAX_BUTTON 5
52 
53 /* the width of the column resize windows */
54 #define DRAG_WIDTH  6
55 
56 /* minimum allowed width of a column */
57 #define COLUMN_MIN_WIDTH 5
58 
59 /* this defigns the base grid spacing */
60 #define CELL_SPACING 1
61 
62 /* added the horizontal space at the beginning and end of a row*/
63 #define COLUMN_INSET 3
64 
65 /* used for auto-scrolling */
66 #define SCROLL_TIME  100
67 
68 /* gives the top pixel of the given row in context of
69  * the clist's voffset */
70 #define ROW_TOP_YPIXEL(clist, row) (((clist)->row_height * (row)) + \
71 				    (((row) + 1) * CELL_SPACING) + \
72 				    (clist)->voffset)
73 
74 /* returns the row index from a y pixel location in the
75  * context of the clist's voffset */
76 #define ROW_FROM_YPIXEL(clist, y)  (((y) - (clist)->voffset) / \
77 				    ((clist)->row_height + CELL_SPACING))
78 
79 /* gives the left pixel of the given column in context of
80  * the clist's hoffset */
81 #define COLUMN_LEFT_XPIXEL(clist, colnum)  ((clist)->column[(colnum)].area.x + \
82 					    (clist)->hoffset)
83 
84 /* returns the column index from a x pixel location in the
85  * context of the clist's hoffset */
86 static inline gint
COLUMN_FROM_XPIXEL(GtkCList * clist,gint x)87 COLUMN_FROM_XPIXEL (GtkCList * clist,
88 		    gint x)
89 {
90   gint i, cx;
91 
92   for (i = 0; i < clist->columns; i++)
93     if (clist->column[i].visible)
94       {
95 	cx = clist->column[i].area.x + clist->hoffset;
96 
97 	if (x >= (cx - (COLUMN_INSET + CELL_SPACING)) &&
98 	    x <= (cx + clist->column[i].area.width + COLUMN_INSET))
99 	  return i;
100       }
101 
102   /* no match */
103   return -1;
104 }
105 
106 /* returns the top pixel of the given row in the context of
107  * the list height */
108 #define ROW_TOP(clist, row)        (((clist)->row_height + CELL_SPACING) * (row))
109 
110 /* returns the left pixel of the given column in the context of
111  * the list width */
112 #define COLUMN_LEFT(clist, colnum) ((clist)->column[(colnum)].area.x)
113 
114 /* returns the total height of the list */
115 #define LIST_HEIGHT(clist)         (((clist)->row_height * ((clist)->rows)) + \
116 				    (CELL_SPACING * ((clist)->rows + 1)))
117 
118 
119 /* returns the total width of the list */
120 static inline gint
LIST_WIDTH(GtkCList * clist)121 LIST_WIDTH (GtkCList * clist)
122 {
123   gint last_column;
124 
125   for (last_column = clist->columns - 1;
126        last_column >= 0 && !clist->column[last_column].visible; last_column--);
127 
128   if (last_column >= 0)
129     return (clist->column[last_column].area.x +
130 	    clist->column[last_column].area.width +
131 	    COLUMN_INSET + CELL_SPACING);
132   return 0;
133 }
134 
135 /* returns the GList item for the nth row */
136 #define	ROW_ELEMENT(clist, row)	(((row) == (clist)->rows - 1) ? \
137 				 (clist)->row_list_end : \
138 				 g_list_nth ((clist)->row_list, (row)))
139 
140 
141 /* redraw the list if it's not frozen */
142 #define CLIST_UNFROZEN(clist)     (((GtkCList*) (clist))->freeze_count == 0)
143 #define	CLIST_REFRESH(clist)	G_STMT_START { \
144   if (CLIST_UNFROZEN (clist)) \
145     GTK_CLIST_GET_CLASS (clist)->refresh ((GtkCList*) (clist)); \
146 } G_STMT_END
147 
148 
149 /* Signals */
150 enum {
151   SELECT_ROW,
152   UNSELECT_ROW,
153   ROW_MOVE,
154   CLICK_COLUMN,
155   RESIZE_COLUMN,
156   TOGGLE_FOCUS_ROW,
157   SELECT_ALL,
158   UNSELECT_ALL,
159   UNDO_SELECTION,
160   START_SELECTION,
161   END_SELECTION,
162   TOGGLE_ADD_MODE,
163   EXTEND_SELECTION,
164   SCROLL_VERTICAL,
165   SCROLL_HORIZONTAL,
166   ABORT_COLUMN_RESIZE,
167   LAST_SIGNAL
168 };
169 
170 enum {
171   SYNC_REMOVE,
172   SYNC_INSERT
173 };
174 
175 enum {
176   ARG_0,
177   ARG_N_COLUMNS,
178   ARG_SHADOW_TYPE,
179   ARG_SELECTION_MODE,
180   ARG_ROW_HEIGHT,
181   ARG_TITLES_ACTIVE,
182   ARG_REORDERABLE,
183   ARG_USE_DRAG_ICONS,
184   ARG_SORT_TYPE
185 };
186 
187 /* GtkCList Methods */
188 static void     gtk_clist_class_init  (GtkCListClass         *klass);
189 static void     gtk_clist_init        (GtkCList              *clist);
190 static GObject* gtk_clist_constructor (GType                  type,
191 				       guint                  n_construct_properties,
192 				       GObjectConstructParam *construct_params);
193 
194 /* GtkObject Methods */
195 static void gtk_clist_destroy  (GtkObject *object);
196 static void gtk_clist_finalize (GObject   *object);
197 static void gtk_clist_set_arg  (GtkObject *object,
198 				GtkArg    *arg,
199 				guint      arg_id);
200 static void gtk_clist_get_arg  (GtkObject *object,
201 				GtkArg    *arg,
202 				guint      arg_id);
203 
204 /* GtkWidget Methods */
205 static void gtk_clist_set_scroll_adjustments (GtkCList      *clist,
206 					      GtkAdjustment *hadjustment,
207 					      GtkAdjustment *vadjustment);
208 static void gtk_clist_realize         (GtkWidget        *widget);
209 static void gtk_clist_unrealize       (GtkWidget        *widget);
210 static void gtk_clist_map             (GtkWidget        *widget);
211 static void gtk_clist_unmap           (GtkWidget        *widget);
212 static gint gtk_clist_expose          (GtkWidget        *widget,
213 			               GdkEventExpose   *event);
214 static gint gtk_clist_button_press    (GtkWidget        *widget,
215 				       GdkEventButton   *event);
216 static gint gtk_clist_button_release  (GtkWidget        *widget,
217 				       GdkEventButton   *event);
218 static gint gtk_clist_motion          (GtkWidget        *widget,
219 			               GdkEventMotion   *event);
220 static void gtk_clist_size_request    (GtkWidget        *widget,
221 				       GtkRequisition   *requisition);
222 static void gtk_clist_size_allocate   (GtkWidget        *widget,
223 				       GtkAllocation    *allocation);
224 static void gtk_clist_draw_focus      (GtkWidget        *widget);
225 static gint gtk_clist_focus_in        (GtkWidget        *widget,
226 				       GdkEventFocus    *event);
227 static gint gtk_clist_focus_out       (GtkWidget        *widget,
228 				       GdkEventFocus    *event);
229 static gint gtk_clist_focus           (GtkWidget        *widget,
230 				       GtkDirectionType  direction);
231 static void gtk_clist_set_focus_child (GtkContainer     *container,
232 				       GtkWidget        *child);
233 static void gtk_clist_style_set       (GtkWidget        *widget,
234 				       GtkStyle         *previous_style);
235 static void gtk_clist_drag_begin      (GtkWidget        *widget,
236 				       GdkDragContext   *context);
237 static gint gtk_clist_drag_motion     (GtkWidget        *widget,
238 				       GdkDragContext   *context,
239 				       gint              x,
240 				       gint              y,
241 				       guint             time);
242 static void gtk_clist_drag_leave      (GtkWidget        *widget,
243 				       GdkDragContext   *context,
244 				       guint             time);
245 static void gtk_clist_drag_end        (GtkWidget        *widget,
246 				       GdkDragContext   *context);
247 static gboolean gtk_clist_drag_drop   (GtkWidget      *widget,
248 				       GdkDragContext *context,
249 				       gint            x,
250 				       gint            y,
251 				       guint           time);
252 static void gtk_clist_drag_data_get   (GtkWidget        *widget,
253 				       GdkDragContext   *context,
254 				       GtkSelectionData *selection_data,
255 				       guint             info,
256 				       guint             time);
257 static void gtk_clist_drag_data_received (GtkWidget        *widget,
258 					  GdkDragContext   *context,
259 					  gint              x,
260 					  gint              y,
261 					  GtkSelectionData *selection_data,
262 					  guint             info,
263 					  guint             time);
264 
265 /* GtkContainer Methods */
266 static void gtk_clist_forall          (GtkContainer  *container,
267 			               gboolean       include_internals,
268 			               GtkCallback    callback,
269 			               gpointer       callback_data);
270 
271 /* Selection */
272 static void toggle_row                (GtkCList      *clist,
273 			               gint           row,
274 			               gint           column,
275 			               GdkEvent      *event);
276 static void real_select_row           (GtkCList      *clist,
277 			               gint           row,
278 			               gint           column,
279 			               GdkEvent      *event);
280 static void real_unselect_row         (GtkCList      *clist,
281 			               gint           row,
282 			               gint           column,
283 			               GdkEvent      *event);
284 static void update_extended_selection (GtkCList      *clist,
285 				       gint           row);
286 static GList *selection_find          (GtkCList      *clist,
287 			               gint           row_number,
288 			               GList         *row_list_element);
289 static void real_select_all           (GtkCList      *clist);
290 static void real_unselect_all         (GtkCList      *clist);
291 static void move_vertical             (GtkCList      *clist,
292 			               gint           row,
293 			               gfloat         align);
294 static void move_horizontal           (GtkCList      *clist,
295 			               gint           diff);
296 static void real_undo_selection       (GtkCList      *clist);
297 static void fake_unselect_all         (GtkCList      *clist,
298 			               gint           row);
299 static void fake_toggle_row           (GtkCList      *clist,
300 			               gint           row);
301 static void resync_selection          (GtkCList      *clist,
302 			               GdkEvent      *event);
303 static void sync_selection            (GtkCList      *clist,
304 	                               gint           row,
305                                        gint           mode);
306 static void set_anchor                (GtkCList      *clist,
307 			               gboolean       add_mode,
308 			               gint           anchor,
309 			               gint           undo_anchor);
310 static void start_selection           (GtkCList      *clist);
311 static void end_selection             (GtkCList      *clist);
312 static void toggle_add_mode           (GtkCList      *clist);
313 static void toggle_focus_row          (GtkCList      *clist);
314 static void extend_selection          (GtkCList      *clist,
315 			               GtkScrollType  scroll_type,
316 			               gfloat         position,
317 			               gboolean       auto_start_selection);
318 static gint get_selection_info        (GtkCList       *clist,
319 				       gint            x,
320 				       gint            y,
321 				       gint           *row,
322 				       gint           *column);
323 
324 /* Scrolling */
325 static void move_focus_row     (GtkCList      *clist,
326 			        GtkScrollType  scroll_type,
327 			        gfloat         position);
328 static void scroll_horizontal  (GtkCList      *clist,
329 			        GtkScrollType  scroll_type,
330 			        gfloat         position);
331 static void scroll_vertical    (GtkCList      *clist,
332 			        GtkScrollType  scroll_type,
333 			        gfloat         position);
334 static void move_horizontal    (GtkCList      *clist,
335 				gint           diff);
336 static void move_vertical      (GtkCList      *clist,
337 				gint           row,
338 				gfloat         align);
339 static gint horizontal_timeout (GtkCList      *clist);
340 static gint vertical_timeout   (GtkCList      *clist);
341 static void remove_grab        (GtkCList      *clist);
342 
343 
344 /* Resize Columns */
345 static void draw_xor_line             (GtkCList       *clist);
346 static gint new_column_width          (GtkCList       *clist,
347 			               gint            column,
348 			               gint           *x);
349 static void column_auto_resize        (GtkCList       *clist,
350 				       GtkCListRow    *clist_row,
351 				       gint            column,
352 				       gint            old_width);
353 static void real_resize_column        (GtkCList       *clist,
354 				       gint            column,
355 				       gint            width);
356 static void abort_column_resize       (GtkCList       *clist);
357 static void cell_size_request         (GtkCList       *clist,
358 			               GtkCListRow    *clist_row,
359 			               gint            column,
360 				       GtkRequisition *requisition);
361 
362 /* Buttons */
363 static void column_button_create      (GtkCList       *clist,
364 				       gint            column);
365 static void column_button_clicked     (GtkWidget      *widget,
366 				       gpointer        data);
367 
368 /* Adjustments */
369 static void adjust_adjustments        (GtkCList       *clist,
370 				       gboolean        block_resize);
371 static void vadjustment_changed       (GtkAdjustment  *adjustment,
372 				       gpointer        data);
373 static void vadjustment_value_changed (GtkAdjustment  *adjustment,
374 				       gpointer        data);
375 static void hadjustment_changed       (GtkAdjustment  *adjustment,
376 				       gpointer        data);
377 static void hadjustment_value_changed (GtkAdjustment  *adjustment,
378 				       gpointer        data);
379 
380 /* Drawing */
381 static void get_cell_style   (GtkCList      *clist,
382 			      GtkCListRow   *clist_row,
383 			      gint           state,
384 			      gint           column,
385 			      GtkStyle     **style,
386 			      GdkGC        **fg_gc,
387 			      GdkGC        **bg_gc);
388 static gint draw_cell_pixmap (GdkWindow     *window,
389 			      GdkRectangle  *clip_rectangle,
390 			      GdkGC         *fg_gc,
391 			      GdkPixmap     *pixmap,
392 			      GdkBitmap     *mask,
393 			      gint           x,
394 			      gint           y,
395 			      gint           width,
396 			      gint           height);
397 static void draw_row         (GtkCList      *clist,
398 			      GdkRectangle  *area,
399 			      gint           row,
400 			      GtkCListRow   *clist_row);
401 static void draw_rows        (GtkCList      *clist,
402 			      GdkRectangle  *area);
403 static void clist_refresh    (GtkCList      *clist);
404 static void draw_drag_highlight (GtkCList        *clist,
405 				 GtkCListRow     *dest_row,
406 				 gint             dest_row_number,
407 				 GtkCListDragPos  drag_pos);
408 
409 /* Size Allocation / Requisition */
410 static void size_allocate_title_buttons (GtkCList *clist);
411 static void size_allocate_columns       (GtkCList *clist,
412 					 gboolean  block_resize);
413 static gint list_requisition_width      (GtkCList *clist);
414 
415 /* Memory Allocation/Distruction Routines */
416 static GtkCListColumn *columns_new (GtkCList      *clist);
417 static void column_title_new       (GtkCList      *clist,
418 			            gint           column,
419 			            const gchar   *title);
420 static void columns_delete         (GtkCList      *clist);
421 static GtkCListRow *row_new        (GtkCList      *clist);
422 static void row_delete             (GtkCList      *clist,
423 			            GtkCListRow   *clist_row);
424 static void set_cell_contents      (GtkCList      *clist,
425 			            GtkCListRow   *clist_row,
426 				    gint           column,
427 				    GtkCellType    type,
428 				    const gchar   *text,
429 				    guint8         spacing,
430 				    GdkPixmap     *pixmap,
431 				    GdkBitmap     *mask);
432 static gint real_insert_row        (GtkCList      *clist,
433 				    gint           row,
434 				    gchar         *text[]);
435 static void real_remove_row        (GtkCList      *clist,
436 				    gint           row);
437 static void real_clear             (GtkCList      *clist);
438 
439 /* Sorting */
440 static gint default_compare        (GtkCList      *clist,
441 			            gconstpointer  row1,
442 			            gconstpointer  row2);
443 static void real_sort_list         (GtkCList      *clist);
444 static GList *gtk_clist_merge      (GtkCList      *clist,
445 				    GList         *a,
446 				    GList         *b);
447 static GList *gtk_clist_mergesort  (GtkCList      *clist,
448 				    GList         *list,
449 				    gint           num);
450 /* Misc */
451 static gboolean title_focus_in   (GtkCList *clist,
452 				  gint      dir);
453 static gboolean title_focus_move (GtkCList *clist,
454 				  gint      dir);
455 
456 static void real_row_move             (GtkCList  *clist,
457 			               gint       source_row,
458 			               gint       dest_row);
459 static gint column_title_passive_func (GtkWidget *widget,
460 				       GdkEvent  *event,
461 				       gpointer   data);
462 static void drag_dest_cell            (GtkCList         *clist,
463 				       gint              x,
464 				       gint              y,
465 				       GtkCListDestInfo *dest_info);
466 
467 
468 
469 static GtkContainerClass *parent_class = NULL;
470 static guint clist_signals[LAST_SIGNAL] = {0};
471 
472 static const GtkTargetEntry clist_target_table = { "gtk-clist-drag-reorder", 0, 0};
473 
474 GtkType
gtk_clist_get_type(void)475 gtk_clist_get_type (void)
476 {
477   static GtkType clist_type = 0;
478 
479   if (!clist_type)
480     {
481       static const GtkTypeInfo clist_info =
482       {
483 	"GtkCList",
484 	sizeof (GtkCList),
485 	sizeof (GtkCListClass),
486 	(GtkClassInitFunc) gtk_clist_class_init,
487 	(GtkObjectInitFunc) gtk_clist_init,
488 	/* reserved_1 */ NULL,
489 	/* reserved_2 */ NULL,
490 	(GtkClassInitFunc) NULL,
491       };
492 
493       I_("GtkCList");
494       clist_type = gtk_type_unique (GTK_TYPE_CONTAINER, &clist_info);
495     }
496 
497   return clist_type;
498 }
499 
500 static void
gtk_clist_class_init(GtkCListClass * klass)501 gtk_clist_class_init (GtkCListClass *klass)
502 {
503   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
504   GtkObjectClass *object_class;
505   GtkWidgetClass *widget_class;
506   GtkContainerClass *container_class;
507   GtkBindingSet *binding_set;
508 
509   gobject_class->constructor = gtk_clist_constructor;
510 
511   object_class = (GtkObjectClass *) klass;
512   widget_class = (GtkWidgetClass *) klass;
513   container_class = (GtkContainerClass *) klass;
514 
515   parent_class = gtk_type_class (GTK_TYPE_CONTAINER);
516 
517   gobject_class->finalize = gtk_clist_finalize;
518 
519   object_class->set_arg = gtk_clist_set_arg;
520   object_class->get_arg = gtk_clist_get_arg;
521   object_class->destroy = gtk_clist_destroy;
522 
523 
524   widget_class->realize = gtk_clist_realize;
525   widget_class->unrealize = gtk_clist_unrealize;
526   widget_class->map = gtk_clist_map;
527   widget_class->unmap = gtk_clist_unmap;
528   widget_class->button_press_event = gtk_clist_button_press;
529   widget_class->button_release_event = gtk_clist_button_release;
530   widget_class->motion_notify_event = gtk_clist_motion;
531   widget_class->expose_event = gtk_clist_expose;
532   widget_class->size_request = gtk_clist_size_request;
533   widget_class->size_allocate = gtk_clist_size_allocate;
534   widget_class->focus_in_event = gtk_clist_focus_in;
535   widget_class->focus_out_event = gtk_clist_focus_out;
536   widget_class->style_set = gtk_clist_style_set;
537   widget_class->drag_begin = gtk_clist_drag_begin;
538   widget_class->drag_end = gtk_clist_drag_end;
539   widget_class->drag_motion = gtk_clist_drag_motion;
540   widget_class->drag_leave = gtk_clist_drag_leave;
541   widget_class->drag_drop = gtk_clist_drag_drop;
542   widget_class->drag_data_get = gtk_clist_drag_data_get;
543   widget_class->drag_data_received = gtk_clist_drag_data_received;
544   widget_class->focus = gtk_clist_focus;
545 
546   /* container_class->add = NULL; use the default GtkContainerClass warning */
547   /* container_class->remove=NULL; use the default GtkContainerClass warning */
548 
549   container_class->forall = gtk_clist_forall;
550   container_class->set_focus_child = gtk_clist_set_focus_child;
551 
552   klass->set_scroll_adjustments = gtk_clist_set_scroll_adjustments;
553   klass->refresh = clist_refresh;
554   klass->select_row = real_select_row;
555   klass->unselect_row = real_unselect_row;
556   klass->row_move = real_row_move;
557   klass->undo_selection = real_undo_selection;
558   klass->resync_selection = resync_selection;
559   klass->selection_find = selection_find;
560   klass->click_column = NULL;
561   klass->resize_column = real_resize_column;
562   klass->draw_row = draw_row;
563   klass->draw_drag_highlight = draw_drag_highlight;
564   klass->insert_row = real_insert_row;
565   klass->remove_row = real_remove_row;
566   klass->clear = real_clear;
567   klass->sort_list = real_sort_list;
568   klass->select_all = real_select_all;
569   klass->unselect_all = real_unselect_all;
570   klass->fake_unselect_all = fake_unselect_all;
571   klass->scroll_horizontal = scroll_horizontal;
572   klass->scroll_vertical = scroll_vertical;
573   klass->extend_selection = extend_selection;
574   klass->toggle_focus_row = toggle_focus_row;
575   klass->toggle_add_mode = toggle_add_mode;
576   klass->start_selection = start_selection;
577   klass->end_selection = end_selection;
578   klass->abort_column_resize = abort_column_resize;
579   klass->set_cell_contents = set_cell_contents;
580   klass->cell_size_request = cell_size_request;
581 
582   gtk_object_add_arg_type ("GtkCList::n-columns",
583 			   GTK_TYPE_UINT,
584 			   GTK_ARG_READWRITE | GTK_ARG_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME,
585 			   ARG_N_COLUMNS);
586   gtk_object_add_arg_type ("GtkCList::shadow-type",
587 			   GTK_TYPE_SHADOW_TYPE,
588 			   GTK_ARG_READWRITE | G_PARAM_STATIC_NAME,
589 			   ARG_SHADOW_TYPE);
590   gtk_object_add_arg_type ("GtkCList::selection-mode",
591 			   GTK_TYPE_SELECTION_MODE,
592 			   GTK_ARG_READWRITE | G_PARAM_STATIC_NAME,
593 			   ARG_SELECTION_MODE);
594   gtk_object_add_arg_type ("GtkCList::row-height",
595 			   GTK_TYPE_UINT,
596 			   GTK_ARG_READWRITE | G_PARAM_STATIC_NAME,
597 			   ARG_ROW_HEIGHT);
598   gtk_object_add_arg_type ("GtkCList::reorderable",
599 			   GTK_TYPE_BOOL,
600 			   GTK_ARG_READWRITE | G_PARAM_STATIC_NAME,
601 			   ARG_REORDERABLE);
602   gtk_object_add_arg_type ("GtkCList::titles-active",
603 			   GTK_TYPE_BOOL,
604 			   GTK_ARG_READWRITE | G_PARAM_STATIC_NAME,
605 			   ARG_TITLES_ACTIVE);
606   gtk_object_add_arg_type ("GtkCList::use-drag-icons",
607 			   GTK_TYPE_BOOL,
608 			   GTK_ARG_READWRITE | G_PARAM_STATIC_NAME,
609 			   ARG_USE_DRAG_ICONS);
610   gtk_object_add_arg_type ("GtkCList::sort-type",
611 			   GTK_TYPE_SORT_TYPE,
612 			   GTK_ARG_READWRITE | G_PARAM_STATIC_NAME,
613 			   ARG_SORT_TYPE);
614 
615   widget_class->set_scroll_adjustments_signal =
616     gtk_signal_new (I_("set-scroll-adjustments"),
617 		    GTK_RUN_LAST,
618 		    GTK_CLASS_TYPE (object_class),
619 		    GTK_SIGNAL_OFFSET (GtkCListClass, set_scroll_adjustments),
620 		    _gtk_marshal_VOID__OBJECT_OBJECT,
621 		    GTK_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
622 
623   clist_signals[SELECT_ROW] =
624     gtk_signal_new (I_("select-row"),
625 		    GTK_RUN_FIRST,
626 		    GTK_CLASS_TYPE (object_class),
627 		    GTK_SIGNAL_OFFSET (GtkCListClass, select_row),
628 		    _gtk_marshal_VOID__INT_INT_BOXED,
629 		    GTK_TYPE_NONE, 3,
630 		    GTK_TYPE_INT,
631 		    GTK_TYPE_INT,
632 		    GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
633   clist_signals[UNSELECT_ROW] =
634     gtk_signal_new (I_("unselect-row"),
635 		    GTK_RUN_FIRST,
636 		    GTK_CLASS_TYPE (object_class),
637 		    GTK_SIGNAL_OFFSET (GtkCListClass, unselect_row),
638 		    _gtk_marshal_VOID__INT_INT_BOXED,
639 		    GTK_TYPE_NONE, 3, GTK_TYPE_INT,
640 		    GTK_TYPE_INT, GDK_TYPE_EVENT);
641   clist_signals[ROW_MOVE] =
642     gtk_signal_new (I_("row-move"),
643 		    GTK_RUN_LAST,
644 		    GTK_CLASS_TYPE (object_class),
645 		    GTK_SIGNAL_OFFSET (GtkCListClass, row_move),
646 		    _gtk_marshal_VOID__INT_INT,
647 		    GTK_TYPE_NONE, 2, GTK_TYPE_INT, GTK_TYPE_INT);
648   clist_signals[CLICK_COLUMN] =
649     gtk_signal_new (I_("click-column"),
650 		    GTK_RUN_FIRST,
651 		    GTK_CLASS_TYPE (object_class),
652 		    GTK_SIGNAL_OFFSET (GtkCListClass, click_column),
653 		    _gtk_marshal_VOID__INT,
654 		    GTK_TYPE_NONE, 1, GTK_TYPE_INT);
655   clist_signals[RESIZE_COLUMN] =
656     gtk_signal_new (I_("resize-column"),
657 		    GTK_RUN_LAST,
658 		    GTK_CLASS_TYPE (object_class),
659 		    GTK_SIGNAL_OFFSET (GtkCListClass, resize_column),
660 		    _gtk_marshal_VOID__INT_INT,
661 		    GTK_TYPE_NONE, 2, GTK_TYPE_INT, GTK_TYPE_INT);
662 
663   clist_signals[TOGGLE_FOCUS_ROW] =
664     gtk_signal_new (I_("toggle-focus-row"),
665                     GTK_RUN_LAST | GTK_RUN_ACTION,
666                     GTK_CLASS_TYPE (object_class),
667                     GTK_SIGNAL_OFFSET (GtkCListClass, toggle_focus_row),
668                     _gtk_marshal_VOID__VOID,
669                     GTK_TYPE_NONE, 0);
670   clist_signals[SELECT_ALL] =
671     gtk_signal_new (I_("select-all"),
672                     GTK_RUN_LAST | GTK_RUN_ACTION,
673                     GTK_CLASS_TYPE (object_class),
674                     GTK_SIGNAL_OFFSET (GtkCListClass, select_all),
675                     _gtk_marshal_VOID__VOID,
676                     GTK_TYPE_NONE, 0);
677   clist_signals[UNSELECT_ALL] =
678     gtk_signal_new (I_("unselect-all"),
679                     GTK_RUN_LAST | GTK_RUN_ACTION,
680                     GTK_CLASS_TYPE (object_class),
681                     GTK_SIGNAL_OFFSET (GtkCListClass, unselect_all),
682                     _gtk_marshal_VOID__VOID,
683                     GTK_TYPE_NONE, 0);
684   clist_signals[UNDO_SELECTION] =
685     gtk_signal_new (I_("undo-selection"),
686 		    GTK_RUN_LAST | GTK_RUN_ACTION,
687 		    GTK_CLASS_TYPE (object_class),
688 		    GTK_SIGNAL_OFFSET (GtkCListClass, undo_selection),
689 		    _gtk_marshal_VOID__VOID,
690 		    GTK_TYPE_NONE, 0);
691   clist_signals[START_SELECTION] =
692     gtk_signal_new (I_("start-selection"),
693 		    GTK_RUN_LAST | GTK_RUN_ACTION,
694 		    GTK_CLASS_TYPE (object_class),
695 		    GTK_SIGNAL_OFFSET (GtkCListClass, start_selection),
696 		    _gtk_marshal_VOID__VOID,
697 		    GTK_TYPE_NONE, 0);
698   clist_signals[END_SELECTION] =
699     gtk_signal_new (I_("end-selection"),
700 		    GTK_RUN_LAST | GTK_RUN_ACTION,
701 		    GTK_CLASS_TYPE (object_class),
702 		    GTK_SIGNAL_OFFSET (GtkCListClass, end_selection),
703 		    _gtk_marshal_VOID__VOID,
704 		    GTK_TYPE_NONE, 0);
705   clist_signals[TOGGLE_ADD_MODE] =
706     gtk_signal_new (I_("toggle-add-mode"),
707 		    GTK_RUN_LAST | GTK_RUN_ACTION,
708 		    GTK_CLASS_TYPE (object_class),
709 		    GTK_SIGNAL_OFFSET (GtkCListClass, toggle_add_mode),
710 		    _gtk_marshal_VOID__VOID,
711 		    GTK_TYPE_NONE, 0);
712   clist_signals[EXTEND_SELECTION] =
713     gtk_signal_new (I_("extend-selection"),
714                     GTK_RUN_LAST | GTK_RUN_ACTION,
715                     GTK_CLASS_TYPE (object_class),
716                     GTK_SIGNAL_OFFSET (GtkCListClass, extend_selection),
717                     _gtk_marshal_VOID__ENUM_FLOAT_BOOLEAN,
718                     GTK_TYPE_NONE, 3,
719 		    GTK_TYPE_SCROLL_TYPE, GTK_TYPE_FLOAT, GTK_TYPE_BOOL);
720   clist_signals[SCROLL_VERTICAL] =
721     gtk_signal_new (I_("scroll-vertical"),
722                     GTK_RUN_LAST | GTK_RUN_ACTION,
723                     GTK_CLASS_TYPE (object_class),
724                     GTK_SIGNAL_OFFSET (GtkCListClass, scroll_vertical),
725                     _gtk_marshal_VOID__ENUM_FLOAT,
726                     GTK_TYPE_NONE, 2, GTK_TYPE_SCROLL_TYPE, GTK_TYPE_FLOAT);
727   clist_signals[SCROLL_HORIZONTAL] =
728     gtk_signal_new (I_("scroll-horizontal"),
729                     GTK_RUN_LAST | GTK_RUN_ACTION,
730                     GTK_CLASS_TYPE (object_class),
731                     GTK_SIGNAL_OFFSET (GtkCListClass, scroll_horizontal),
732                     _gtk_marshal_VOID__ENUM_FLOAT,
733                     GTK_TYPE_NONE, 2, GTK_TYPE_SCROLL_TYPE, GTK_TYPE_FLOAT);
734   clist_signals[ABORT_COLUMN_RESIZE] =
735     gtk_signal_new (I_("abort-column-resize"),
736                     GTK_RUN_LAST | GTK_RUN_ACTION,
737                     GTK_CLASS_TYPE (object_class),
738                     GTK_SIGNAL_OFFSET (GtkCListClass, abort_column_resize),
739                     _gtk_marshal_VOID__VOID,
740                     GTK_TYPE_NONE, 0);
741 
742   binding_set = gtk_binding_set_by_class (klass);
743   gtk_binding_entry_add_signal (binding_set, GDK_Up, 0,
744 			        "scroll-vertical", 2,
745 				GTK_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
746 				GTK_TYPE_FLOAT, 0.0);
747   gtk_binding_entry_add_signal (binding_set, GDK_KP_Up, 0,
748 				"scroll-vertical", 2,
749 				GTK_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
750 				GTK_TYPE_FLOAT, 0.0);
751   gtk_binding_entry_add_signal (binding_set, GDK_Down, 0,
752 				"scroll-vertical", 2,
753 				GTK_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
754 				GTK_TYPE_FLOAT, 0.0);
755   gtk_binding_entry_add_signal (binding_set, GDK_KP_Down, 0,
756 				"scroll-vertical", 2,
757 				GTK_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
758 				GTK_TYPE_FLOAT, 0.0);
759   gtk_binding_entry_add_signal (binding_set, GDK_Page_Up, 0,
760 				"scroll-vertical", 2,
761 				GTK_TYPE_ENUM, GTK_SCROLL_PAGE_BACKWARD,
762 				GTK_TYPE_FLOAT, 0.0);
763   gtk_binding_entry_add_signal (binding_set, GDK_KP_Page_Up, 0,
764 				"scroll-vertical", 2,
765 				GTK_TYPE_ENUM, GTK_SCROLL_PAGE_BACKWARD,
766 				GTK_TYPE_FLOAT, 0.0);
767   gtk_binding_entry_add_signal (binding_set, GDK_Page_Down, 0,
768 				"scroll-vertical", 2,
769 				GTK_TYPE_ENUM, GTK_SCROLL_PAGE_FORWARD,
770 				GTK_TYPE_FLOAT, 0.0);
771   gtk_binding_entry_add_signal (binding_set, GDK_KP_Page_Down, 0,
772 				"scroll-vertical", 2,
773 				GTK_TYPE_ENUM, GTK_SCROLL_PAGE_FORWARD,
774 				GTK_TYPE_FLOAT, 0.0);
775   gtk_binding_entry_add_signal (binding_set, GDK_Home, GDK_CONTROL_MASK,
776 				"scroll-vertical", 2,
777 				GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
778 				GTK_TYPE_FLOAT, 0.0);
779   gtk_binding_entry_add_signal (binding_set, GDK_KP_Home, GDK_CONTROL_MASK,
780 				"scroll-vertical", 2,
781 				GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
782 				GTK_TYPE_FLOAT, 0.0);
783   gtk_binding_entry_add_signal (binding_set, GDK_End, GDK_CONTROL_MASK,
784 				"scroll-vertical", 2,
785 				GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
786 				GTK_TYPE_FLOAT, 1.0);
787   gtk_binding_entry_add_signal (binding_set, GDK_KP_End, GDK_CONTROL_MASK,
788 				"scroll-vertical", 2,
789 				GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
790 				GTK_TYPE_FLOAT, 1.0);
791 
792   gtk_binding_entry_add_signal (binding_set, GDK_Up, GDK_SHIFT_MASK,
793 				"extend-selection", 3,
794 				GTK_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
795 				GTK_TYPE_FLOAT, 0.0, GTK_TYPE_BOOL, TRUE);
796   gtk_binding_entry_add_signal (binding_set, GDK_KP_Up, GDK_SHIFT_MASK,
797 				"extend-selection", 3,
798 				GTK_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
799 				GTK_TYPE_FLOAT, 0.0, GTK_TYPE_BOOL, TRUE);
800   gtk_binding_entry_add_signal (binding_set, GDK_Down, GDK_SHIFT_MASK,
801 				"extend-selection", 3,
802 				GTK_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
803 				GTK_TYPE_FLOAT, 0.0, GTK_TYPE_BOOL, TRUE);
804   gtk_binding_entry_add_signal (binding_set, GDK_KP_Down, GDK_SHIFT_MASK,
805 				"extend-selection", 3,
806 				GTK_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
807 				GTK_TYPE_FLOAT, 0.0, GTK_TYPE_BOOL, TRUE);
808   gtk_binding_entry_add_signal (binding_set, GDK_Page_Up, GDK_SHIFT_MASK,
809 				"extend-selection", 3,
810 				GTK_TYPE_ENUM, GTK_SCROLL_PAGE_BACKWARD,
811 				GTK_TYPE_FLOAT, 0.0, GTK_TYPE_BOOL, TRUE);
812   gtk_binding_entry_add_signal (binding_set, GDK_KP_Page_Up, GDK_SHIFT_MASK,
813 				"extend-selection", 3,
814 				GTK_TYPE_ENUM, GTK_SCROLL_PAGE_BACKWARD,
815 				GTK_TYPE_FLOAT, 0.0, GTK_TYPE_BOOL, TRUE);
816   gtk_binding_entry_add_signal (binding_set, GDK_Page_Down, GDK_SHIFT_MASK,
817 				"extend-selection", 3,
818 				GTK_TYPE_ENUM, GTK_SCROLL_PAGE_FORWARD,
819 				GTK_TYPE_FLOAT, 0.0, GTK_TYPE_BOOL, TRUE);
820   gtk_binding_entry_add_signal (binding_set, GDK_KP_Page_Down, GDK_SHIFT_MASK,
821 				"extend-selection", 3,
822 				GTK_TYPE_ENUM, GTK_SCROLL_PAGE_FORWARD,
823 				GTK_TYPE_FLOAT, 0.0, GTK_TYPE_BOOL, TRUE);
824   gtk_binding_entry_add_signal (binding_set, GDK_Home,
825 				GDK_SHIFT_MASK | GDK_CONTROL_MASK,
826 				"extend-selection", 3,
827 				GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
828 				GTK_TYPE_FLOAT, 0.0, GTK_TYPE_BOOL, TRUE);
829   gtk_binding_entry_add_signal (binding_set, GDK_KP_Home,
830                                 GDK_SHIFT_MASK | GDK_CONTROL_MASK,
831 				"extend-selection", 3,
832 				GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
833 				GTK_TYPE_FLOAT, 0.0, GTK_TYPE_BOOL, TRUE);
834   gtk_binding_entry_add_signal (binding_set, GDK_End,
835 				GDK_SHIFT_MASK | GDK_CONTROL_MASK,
836 				"extend-selection", 3,
837 				GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
838 				GTK_TYPE_FLOAT, 1.0, GTK_TYPE_BOOL, TRUE);
839   gtk_binding_entry_add_signal (binding_set, GDK_KP_End,
840 				GDK_SHIFT_MASK | GDK_CONTROL_MASK,
841 				"extend-selection", 3,
842 				GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
843 				GTK_TYPE_FLOAT, 1.0, GTK_TYPE_BOOL, TRUE);
844 
845 
846   gtk_binding_entry_add_signal (binding_set, GDK_Left, 0,
847 				"scroll-horizontal", 2,
848 				GTK_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
849 				GTK_TYPE_FLOAT, 0.0);
850   gtk_binding_entry_add_signal (binding_set, GDK_KP_Left, 0,
851 				"scroll-horizontal", 2,
852 				GTK_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
853 				GTK_TYPE_FLOAT, 0.0);
854 
855   gtk_binding_entry_add_signal (binding_set, GDK_Right, 0,
856 				"scroll-horizontal", 2,
857 				GTK_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
858 				GTK_TYPE_FLOAT, 0.0);
859   gtk_binding_entry_add_signal (binding_set, GDK_KP_Right, 0,
860 				"scroll-horizontal", 2,
861 				GTK_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
862 				GTK_TYPE_FLOAT, 0.0);
863 
864   gtk_binding_entry_add_signal (binding_set, GDK_Home, 0,
865 				"scroll-horizontal", 2,
866 				GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
867 				GTK_TYPE_FLOAT, 0.0);
868   gtk_binding_entry_add_signal (binding_set, GDK_KP_Home, 0,
869 				"scroll-horizontal", 2,
870 				GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
871 				GTK_TYPE_FLOAT, 0.0);
872 
873   gtk_binding_entry_add_signal (binding_set, GDK_End, 0,
874 				"scroll-horizontal", 2,
875 				GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
876 				GTK_TYPE_FLOAT, 1.0);
877 
878   gtk_binding_entry_add_signal (binding_set, GDK_KP_End, 0,
879 				"scroll-horizontal", 2,
880 				GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
881 				GTK_TYPE_FLOAT, 1.0);
882 
883   gtk_binding_entry_add_signal (binding_set, GDK_Escape, 0,
884 				"undo-selection", 0);
885   gtk_binding_entry_add_signal (binding_set, GDK_Escape, 0,
886 				"abort-column-resize", 0);
887   gtk_binding_entry_add_signal (binding_set, GDK_space, 0,
888 				"toggle-focus-row", 0);
889   gtk_binding_entry_add_signal (binding_set, GDK_KP_Space, 0,
890 				"toggle-focus-row", 0);
891   gtk_binding_entry_add_signal (binding_set, GDK_space, GDK_CONTROL_MASK,
892 				"toggle-add-mode", 0);
893   gtk_binding_entry_add_signal (binding_set, GDK_KP_Space, GDK_CONTROL_MASK,
894 				"toggle-add-mode", 0);
895   gtk_binding_entry_add_signal (binding_set, GDK_slash, GDK_CONTROL_MASK,
896 				"select-all", 0);
897   gtk_binding_entry_add_signal (binding_set, GDK_KP_Divide, GDK_CONTROL_MASK,
898 				"select-all", 0);
899   gtk_binding_entry_add_signal (binding_set, '\\', GDK_CONTROL_MASK,
900 				"unselect-all", 0);
901   gtk_binding_entry_add_signal (binding_set, GDK_Shift_L,
902 				GDK_RELEASE_MASK | GDK_SHIFT_MASK,
903 				"end-selection", 0);
904   gtk_binding_entry_add_signal (binding_set, GDK_Shift_R,
905 				GDK_RELEASE_MASK | GDK_SHIFT_MASK,
906 				"end-selection", 0);
907   gtk_binding_entry_add_signal (binding_set, GDK_Shift_L,
908 				GDK_RELEASE_MASK | GDK_SHIFT_MASK |
909 				GDK_CONTROL_MASK,
910 				"end-selection", 0);
911   gtk_binding_entry_add_signal (binding_set, GDK_Shift_R,
912 				GDK_RELEASE_MASK | GDK_SHIFT_MASK |
913 				GDK_CONTROL_MASK,
914 				"end-selection", 0);
915 }
916 
917 static void
gtk_clist_set_arg(GtkObject * object,GtkArg * arg,guint arg_id)918 gtk_clist_set_arg (GtkObject      *object,
919 		   GtkArg         *arg,
920 		   guint           arg_id)
921 {
922   GtkCList *clist;
923 
924   clist = GTK_CLIST (object);
925 
926   switch (arg_id)
927     {
928     case ARG_N_COLUMNS: /* only set at construction time */
929       clist->columns = MAX (1, GTK_VALUE_UINT (*arg));
930       break;
931     case ARG_SHADOW_TYPE:
932       gtk_clist_set_shadow_type (clist, GTK_VALUE_ENUM (*arg));
933       break;
934     case ARG_SELECTION_MODE:
935       gtk_clist_set_selection_mode (clist, GTK_VALUE_ENUM (*arg));
936       break;
937     case ARG_ROW_HEIGHT:
938       gtk_clist_set_row_height (clist, GTK_VALUE_UINT (*arg));
939       break;
940     case ARG_REORDERABLE:
941       gtk_clist_set_reorderable (clist, GTK_VALUE_BOOL (*arg));
942       break;
943     case ARG_TITLES_ACTIVE:
944       if (GTK_VALUE_BOOL (*arg))
945 	gtk_clist_column_titles_active (clist);
946       else
947 	gtk_clist_column_titles_passive (clist);
948       break;
949     case ARG_USE_DRAG_ICONS:
950       gtk_clist_set_use_drag_icons (clist, GTK_VALUE_BOOL (*arg));
951       break;
952     case ARG_SORT_TYPE:
953       gtk_clist_set_sort_type (clist, GTK_VALUE_ENUM (*arg));
954       break;
955     }
956 }
957 
958 static void
gtk_clist_get_arg(GtkObject * object,GtkArg * arg,guint arg_id)959 gtk_clist_get_arg (GtkObject      *object,
960 		   GtkArg         *arg,
961 		   guint           arg_id)
962 {
963   GtkCList *clist;
964 
965   clist = GTK_CLIST (object);
966 
967   switch (arg_id)
968     {
969       guint i;
970 
971     case ARG_N_COLUMNS:
972       GTK_VALUE_UINT (*arg) = clist->columns;
973       break;
974     case ARG_SHADOW_TYPE:
975       GTK_VALUE_ENUM (*arg) = clist->shadow_type;
976       break;
977     case ARG_SELECTION_MODE:
978       GTK_VALUE_ENUM (*arg) = clist->selection_mode;
979       break;
980     case ARG_ROW_HEIGHT:
981       GTK_VALUE_UINT (*arg) = GTK_CLIST_ROW_HEIGHT_SET(clist) ? clist->row_height : 0;
982       break;
983     case ARG_REORDERABLE:
984       GTK_VALUE_BOOL (*arg) = GTK_CLIST_REORDERABLE (clist);
985       break;
986     case ARG_TITLES_ACTIVE:
987       GTK_VALUE_BOOL (*arg) = TRUE;
988       for (i = 0; i < clist->columns; i++)
989 	if (clist->column[i].button &&
990 	    !gtk_widget_get_sensitive (clist->column[i].button))
991 	  {
992 	    GTK_VALUE_BOOL (*arg) = FALSE;
993 	    break;
994 	  }
995       break;
996     case ARG_USE_DRAG_ICONS:
997       GTK_VALUE_BOOL (*arg) = GTK_CLIST_USE_DRAG_ICONS (clist);
998       break;
999     case ARG_SORT_TYPE:
1000       GTK_VALUE_ENUM (*arg) = clist->sort_type;
1001       break;
1002     default:
1003       arg->type = GTK_TYPE_INVALID;
1004       break;
1005     }
1006 }
1007 
1008 static void
gtk_clist_init(GtkCList * clist)1009 gtk_clist_init (GtkCList *clist)
1010 {
1011   clist->flags = 0;
1012 
1013   gtk_widget_set_has_window (GTK_WIDGET (clist), TRUE);
1014   gtk_widget_set_can_focus (GTK_WIDGET (clist), TRUE);
1015   GTK_CLIST_SET_FLAG (clist, CLIST_DRAW_DRAG_LINE);
1016   GTK_CLIST_SET_FLAG (clist, CLIST_USE_DRAG_ICONS);
1017 
1018   clist->freeze_count = 0;
1019 
1020   clist->rows = 0;
1021   clist->row_height = 0;
1022   clist->row_list = NULL;
1023   clist->row_list_end = NULL;
1024 
1025   clist->columns = 0;
1026 
1027   clist->title_window = NULL;
1028   clist->column_title_area.x = 0;
1029   clist->column_title_area.y = 0;
1030   clist->column_title_area.width = 1;
1031   clist->column_title_area.height = 1;
1032 
1033   clist->clist_window = NULL;
1034   clist->clist_window_width = 1;
1035   clist->clist_window_height = 1;
1036 
1037   clist->hoffset = 0;
1038   clist->voffset = 0;
1039 
1040   clist->shadow_type = GTK_SHADOW_IN;
1041   clist->vadjustment = NULL;
1042   clist->hadjustment = NULL;
1043 
1044   clist->button_actions[0] = GTK_BUTTON_SELECTS | GTK_BUTTON_DRAGS;
1045   clist->button_actions[1] = GTK_BUTTON_IGNORED;
1046   clist->button_actions[2] = GTK_BUTTON_IGNORED;
1047   clist->button_actions[3] = GTK_BUTTON_IGNORED;
1048   clist->button_actions[4] = GTK_BUTTON_IGNORED;
1049 
1050   clist->cursor_drag = NULL;
1051   clist->xor_gc = NULL;
1052   clist->fg_gc = NULL;
1053   clist->bg_gc = NULL;
1054   clist->x_drag = 0;
1055 
1056   clist->selection_mode = GTK_SELECTION_SINGLE;
1057   clist->selection = NULL;
1058   clist->selection_end = NULL;
1059   clist->undo_selection = NULL;
1060   clist->undo_unselection = NULL;
1061 
1062   clist->focus_row = -1;
1063   clist->focus_header_column = -1;
1064   clist->undo_anchor = -1;
1065 
1066   clist->anchor = -1;
1067   clist->anchor_state = GTK_STATE_SELECTED;
1068   clist->drag_pos = -1;
1069   clist->htimer = 0;
1070   clist->vtimer = 0;
1071 
1072   clist->click_cell.row = -1;
1073   clist->click_cell.column = -1;
1074 
1075   clist->compare = default_compare;
1076   clist->sort_type = GTK_SORT_ASCENDING;
1077   clist->sort_column = 0;
1078 
1079   clist->drag_highlight_row = -1;
1080 }
1081 
1082 /* Constructor */
1083 static GObject*
gtk_clist_constructor(GType type,guint n_construct_properties,GObjectConstructParam * construct_properties)1084 gtk_clist_constructor (GType                  type,
1085 		       guint                  n_construct_properties,
1086 		       GObjectConstructParam *construct_properties)
1087 {
1088   GObject *object = G_OBJECT_CLASS (parent_class)->constructor (type,
1089 								n_construct_properties,
1090 								construct_properties);
1091   GtkCList *clist = GTK_CLIST (object);
1092 
1093   /* allocate memory for columns */
1094   clist->column = columns_new (clist);
1095 
1096   /* there needs to be at least one column button
1097    * because there is alot of code that will break if it
1098    * isn't there
1099    */
1100   column_button_create (clist, 0);
1101 
1102   return object;
1103 }
1104 
1105 /* GTKCLIST PUBLIC INTERFACE
1106  *   gtk_clist_new
1107  *   gtk_clist_new_with_titles
1108  *   gtk_clist_set_hadjustment
1109  *   gtk_clist_set_vadjustment
1110  *   gtk_clist_get_hadjustment
1111  *   gtk_clist_get_vadjustment
1112  *   gtk_clist_set_shadow_type
1113  *   gtk_clist_set_selection_mode
1114  *   gtk_clist_freeze
1115  *   gtk_clist_thaw
1116  */
1117 GtkWidget*
gtk_clist_new(gint columns)1118 gtk_clist_new (gint columns)
1119 {
1120   return gtk_clist_new_with_titles (columns, NULL);
1121 }
1122 
1123 GtkWidget*
gtk_clist_new_with_titles(gint columns,gchar * titles[])1124 gtk_clist_new_with_titles (gint   columns,
1125 			   gchar *titles[])
1126 {
1127   GtkCList *clist;
1128 
1129   clist = g_object_new (GTK_TYPE_CLIST,
1130 			"n_columns", columns,
1131 			NULL);
1132   if (titles)
1133     {
1134       guint i;
1135 
1136       for (i = 0; i < clist->columns; i++)
1137 	gtk_clist_set_column_title (clist, i, titles[i]);
1138       gtk_clist_column_titles_show (clist);
1139     }
1140   else
1141     gtk_clist_column_titles_hide (clist);
1142 
1143   return GTK_WIDGET (clist);
1144 }
1145 
1146 void
gtk_clist_set_hadjustment(GtkCList * clist,GtkAdjustment * adjustment)1147 gtk_clist_set_hadjustment (GtkCList      *clist,
1148 			   GtkAdjustment *adjustment)
1149 {
1150   GtkAdjustment *old_adjustment;
1151 
1152   g_return_if_fail (GTK_IS_CLIST (clist));
1153   if (adjustment)
1154     g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
1155 
1156   if (clist->hadjustment == adjustment)
1157     return;
1158 
1159   old_adjustment = clist->hadjustment;
1160 
1161   if (clist->hadjustment)
1162     {
1163       gtk_signal_disconnect_by_data (GTK_OBJECT (clist->hadjustment), clist);
1164       g_object_unref (clist->hadjustment);
1165     }
1166 
1167   clist->hadjustment = adjustment;
1168 
1169   if (clist->hadjustment)
1170     {
1171       g_object_ref_sink (clist->hadjustment);
1172 
1173       gtk_signal_connect (GTK_OBJECT (clist->hadjustment), "changed",
1174 			  G_CALLBACK (hadjustment_changed),
1175 			  (gpointer) clist);
1176       gtk_signal_connect (GTK_OBJECT (clist->hadjustment), "value-changed",
1177 			  G_CALLBACK (hadjustment_value_changed),
1178 			  (gpointer) clist);
1179     }
1180 
1181   if (!clist->hadjustment || !old_adjustment)
1182     gtk_widget_queue_resize (GTK_WIDGET (clist));
1183 }
1184 
1185 GtkAdjustment *
gtk_clist_get_hadjustment(GtkCList * clist)1186 gtk_clist_get_hadjustment (GtkCList *clist)
1187 {
1188   g_return_val_if_fail (GTK_IS_CLIST (clist), NULL);
1189 
1190   return clist->hadjustment;
1191 }
1192 
1193 void
gtk_clist_set_vadjustment(GtkCList * clist,GtkAdjustment * adjustment)1194 gtk_clist_set_vadjustment (GtkCList      *clist,
1195 			   GtkAdjustment *adjustment)
1196 {
1197   GtkAdjustment *old_adjustment;
1198 
1199   g_return_if_fail (GTK_IS_CLIST (clist));
1200   if (adjustment)
1201     g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
1202 
1203   if (clist->vadjustment == adjustment)
1204     return;
1205 
1206   old_adjustment = clist->vadjustment;
1207 
1208   if (clist->vadjustment)
1209     {
1210       gtk_signal_disconnect_by_data (GTK_OBJECT (clist->vadjustment), clist);
1211       g_object_unref (clist->vadjustment);
1212     }
1213 
1214   clist->vadjustment = adjustment;
1215 
1216   if (clist->vadjustment)
1217     {
1218       g_object_ref_sink (clist->vadjustment);
1219 
1220       gtk_signal_connect (GTK_OBJECT (clist->vadjustment), "changed",
1221 			  G_CALLBACK (vadjustment_changed),
1222 			  (gpointer) clist);
1223       gtk_signal_connect (GTK_OBJECT (clist->vadjustment), "value-changed",
1224 			  G_CALLBACK (vadjustment_value_changed),
1225 			  (gpointer) clist);
1226     }
1227 
1228   if (!clist->vadjustment || !old_adjustment)
1229     gtk_widget_queue_resize (GTK_WIDGET (clist));
1230 }
1231 
1232 GtkAdjustment *
gtk_clist_get_vadjustment(GtkCList * clist)1233 gtk_clist_get_vadjustment (GtkCList *clist)
1234 {
1235   g_return_val_if_fail (GTK_IS_CLIST (clist), NULL);
1236 
1237   return clist->vadjustment;
1238 }
1239 
1240 static void
gtk_clist_set_scroll_adjustments(GtkCList * clist,GtkAdjustment * hadjustment,GtkAdjustment * vadjustment)1241 gtk_clist_set_scroll_adjustments (GtkCList      *clist,
1242 				  GtkAdjustment *hadjustment,
1243 				  GtkAdjustment *vadjustment)
1244 {
1245   if (clist->hadjustment != hadjustment)
1246     gtk_clist_set_hadjustment (clist, hadjustment);
1247   if (clist->vadjustment != vadjustment)
1248     gtk_clist_set_vadjustment (clist, vadjustment);
1249 }
1250 
1251 void
gtk_clist_set_shadow_type(GtkCList * clist,GtkShadowType type)1252 gtk_clist_set_shadow_type (GtkCList      *clist,
1253 			   GtkShadowType  type)
1254 {
1255   g_return_if_fail (GTK_IS_CLIST (clist));
1256 
1257   clist->shadow_type = type;
1258 
1259   if (gtk_widget_get_visible (GTK_WIDGET (clist)))
1260     gtk_widget_queue_resize (GTK_WIDGET (clist));
1261 }
1262 
1263 void
gtk_clist_set_selection_mode(GtkCList * clist,GtkSelectionMode mode)1264 gtk_clist_set_selection_mode (GtkCList         *clist,
1265 			      GtkSelectionMode  mode)
1266 {
1267   g_return_if_fail (GTK_IS_CLIST (clist));
1268   g_return_if_fail (mode != GTK_SELECTION_NONE);
1269 
1270   if (mode == clist->selection_mode)
1271     return;
1272 
1273   clist->selection_mode = mode;
1274   clist->anchor = -1;
1275   clist->anchor_state = GTK_STATE_SELECTED;
1276   clist->drag_pos = -1;
1277   clist->undo_anchor = clist->focus_row;
1278 
1279   g_list_free (clist->undo_selection);
1280   g_list_free (clist->undo_unselection);
1281   clist->undo_selection = NULL;
1282   clist->undo_unselection = NULL;
1283 
1284   switch (mode)
1285     {
1286     case GTK_SELECTION_MULTIPLE:
1287       return;
1288     case GTK_SELECTION_BROWSE:
1289     case GTK_SELECTION_SINGLE:
1290       gtk_clist_unselect_all (clist);
1291       break;
1292     default:
1293       /* Someone set it by hand */
1294       g_assert_not_reached ();
1295     }
1296 }
1297 
1298 void
gtk_clist_freeze(GtkCList * clist)1299 gtk_clist_freeze (GtkCList *clist)
1300 {
1301   g_return_if_fail (GTK_IS_CLIST (clist));
1302 
1303   clist->freeze_count++;
1304 }
1305 
1306 void
gtk_clist_thaw(GtkCList * clist)1307 gtk_clist_thaw (GtkCList *clist)
1308 {
1309   g_return_if_fail (GTK_IS_CLIST (clist));
1310 
1311   if (clist->freeze_count)
1312     {
1313       clist->freeze_count--;
1314       CLIST_REFRESH (clist);
1315     }
1316 }
1317 
1318 /* PUBLIC COLUMN FUNCTIONS
1319  *   gtk_clist_column_titles_show
1320  *   gtk_clist_column_titles_hide
1321  *   gtk_clist_column_title_active
1322  *   gtk_clist_column_title_passive
1323  *   gtk_clist_column_titles_active
1324  *   gtk_clist_column_titles_passive
1325  *   gtk_clist_set_column_title
1326  *   gtk_clist_get_column_title
1327  *   gtk_clist_set_column_widget
1328  *   gtk_clist_set_column_justification
1329  *   gtk_clist_set_column_visibility
1330  *   gtk_clist_set_column_resizeable
1331  *   gtk_clist_set_column_auto_resize
1332  *   gtk_clist_optimal_column_width
1333  *   gtk_clist_set_column_width
1334  *   gtk_clist_set_column_min_width
1335  *   gtk_clist_set_column_max_width
1336  */
1337 void
gtk_clist_column_titles_show(GtkCList * clist)1338 gtk_clist_column_titles_show (GtkCList *clist)
1339 {
1340   g_return_if_fail (GTK_IS_CLIST (clist));
1341 
1342   if (!GTK_CLIST_SHOW_TITLES(clist))
1343     {
1344       GTK_CLIST_SET_FLAG (clist, CLIST_SHOW_TITLES);
1345       if (clist->title_window)
1346 	gdk_window_show (clist->title_window);
1347       gtk_widget_queue_resize (GTK_WIDGET (clist));
1348     }
1349 }
1350 
1351 void
gtk_clist_column_titles_hide(GtkCList * clist)1352 gtk_clist_column_titles_hide (GtkCList *clist)
1353 {
1354   g_return_if_fail (GTK_IS_CLIST (clist));
1355 
1356   if (GTK_CLIST_SHOW_TITLES(clist))
1357     {
1358       GTK_CLIST_UNSET_FLAG (clist, CLIST_SHOW_TITLES);
1359       if (clist->title_window)
1360 	gdk_window_hide (clist->title_window);
1361       gtk_widget_queue_resize (GTK_WIDGET (clist));
1362     }
1363 }
1364 
1365 void
gtk_clist_column_title_active(GtkCList * clist,gint column)1366 gtk_clist_column_title_active (GtkCList *clist,
1367 			       gint      column)
1368 {
1369   g_return_if_fail (GTK_IS_CLIST (clist));
1370 
1371   if (column < 0 || column >= clist->columns)
1372     return;
1373   if (!clist->column[column].button || !clist->column[column].button_passive)
1374     return;
1375 
1376   clist->column[column].button_passive = FALSE;
1377 
1378   gtk_signal_disconnect_by_func (GTK_OBJECT (clist->column[column].button),
1379 				 G_CALLBACK (column_title_passive_func),
1380 				 NULL);
1381 
1382   gtk_widget_set_can_focus (clist->column[column].button, TRUE);
1383   if (gtk_widget_get_visible (GTK_WIDGET (clist)))
1384     gtk_widget_queue_draw (clist->column[column].button);
1385 }
1386 
1387 void
gtk_clist_column_title_passive(GtkCList * clist,gint column)1388 gtk_clist_column_title_passive (GtkCList *clist,
1389 				gint      column)
1390 {
1391   GtkButton *button;
1392 
1393   g_return_if_fail (GTK_IS_CLIST (clist));
1394 
1395   if (column < 0 || column >= clist->columns)
1396     return;
1397   if (!clist->column[column].button || clist->column[column].button_passive)
1398     return;
1399 
1400   button = GTK_BUTTON (clist->column[column].button);
1401 
1402   clist->column[column].button_passive = TRUE;
1403 
1404   if (button->button_down)
1405     g_signal_emit_by_name (button, "released");
1406   if (button->in_button)
1407     g_signal_emit_by_name (button, "leave");
1408 
1409   gtk_signal_connect (GTK_OBJECT (clist->column[column].button), "event",
1410 		      G_CALLBACK (column_title_passive_func),
1411                       NULL);
1412 
1413   gtk_widget_set_can_focus (clist->column[column].button, FALSE);
1414   if (gtk_widget_get_visible (GTK_WIDGET (clist)))
1415     gtk_widget_queue_draw (clist->column[column].button);
1416 }
1417 
1418 void
gtk_clist_column_titles_active(GtkCList * clist)1419 gtk_clist_column_titles_active (GtkCList *clist)
1420 {
1421   gint i;
1422 
1423   g_return_if_fail (GTK_IS_CLIST (clist));
1424 
1425   for (i = 0; i < clist->columns; i++)
1426     gtk_clist_column_title_active (clist, i);
1427 }
1428 
1429 void
gtk_clist_column_titles_passive(GtkCList * clist)1430 gtk_clist_column_titles_passive (GtkCList *clist)
1431 {
1432   gint i;
1433 
1434   g_return_if_fail (GTK_IS_CLIST (clist));
1435 
1436   for (i = 0; i < clist->columns; i++)
1437     gtk_clist_column_title_passive (clist, i);
1438 }
1439 
1440 void
gtk_clist_set_column_title(GtkCList * clist,gint column,const gchar * title)1441 gtk_clist_set_column_title (GtkCList    *clist,
1442 			    gint         column,
1443 			    const gchar *title)
1444 {
1445   gint new_button = 0;
1446   GtkWidget *old_widget;
1447   GtkWidget *alignment = NULL;
1448   GtkWidget *label;
1449 
1450   g_return_if_fail (GTK_IS_CLIST (clist));
1451 
1452   if (column < 0 || column >= clist->columns)
1453     return;
1454 
1455   /* if the column button doesn't currently exist,
1456    * it has to be created first */
1457   if (!clist->column[column].button)
1458     {
1459       column_button_create (clist, column);
1460       new_button = 1;
1461     }
1462 
1463   column_title_new (clist, column, title);
1464 
1465   /* remove and destroy the old widget */
1466   old_widget = GTK_BIN (clist->column[column].button)->child;
1467   if (old_widget)
1468     gtk_container_remove (GTK_CONTAINER (clist->column[column].button), old_widget);
1469 
1470   /* create new alignment based no column justification */
1471   switch (clist->column[column].justification)
1472     {
1473     case GTK_JUSTIFY_LEFT:
1474       alignment = gtk_alignment_new (0.0, 0.5, 0.0, 0.0);
1475       break;
1476 
1477     case GTK_JUSTIFY_RIGHT:
1478       alignment = gtk_alignment_new (1.0, 0.5, 0.0, 0.0);
1479       break;
1480 
1481     case GTK_JUSTIFY_CENTER:
1482       alignment = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
1483       break;
1484 
1485     case GTK_JUSTIFY_FILL:
1486       alignment = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
1487       break;
1488     }
1489 
1490   gtk_widget_push_composite_child ();
1491   label = gtk_label_new (clist->column[column].title);
1492   gtk_widget_pop_composite_child ();
1493   gtk_container_add (GTK_CONTAINER (alignment), label);
1494   gtk_container_add (GTK_CONTAINER (clist->column[column].button), alignment);
1495   gtk_widget_show (label);
1496   gtk_widget_show (alignment);
1497 
1498   /* if this button didn't previously exist, then the
1499    * column button positions have to be re-computed */
1500   if (gtk_widget_get_visible (GTK_WIDGET (clist)) && new_button)
1501     size_allocate_title_buttons (clist);
1502 }
1503 
1504 gchar *
gtk_clist_get_column_title(GtkCList * clist,gint column)1505 gtk_clist_get_column_title (GtkCList *clist,
1506 			    gint      column)
1507 {
1508   g_return_val_if_fail (GTK_IS_CLIST (clist), NULL);
1509 
1510   if (column < 0 || column >= clist->columns)
1511     return NULL;
1512 
1513   return clist->column[column].title;
1514 }
1515 
1516 void
gtk_clist_set_column_widget(GtkCList * clist,gint column,GtkWidget * widget)1517 gtk_clist_set_column_widget (GtkCList  *clist,
1518 			     gint       column,
1519 			     GtkWidget *widget)
1520 {
1521   gint new_button = 0;
1522   GtkWidget *old_widget;
1523 
1524   g_return_if_fail (GTK_IS_CLIST (clist));
1525 
1526   if (column < 0 || column >= clist->columns)
1527     return;
1528 
1529   /* if the column button doesn't currently exist,
1530    * it has to be created first */
1531   if (!clist->column[column].button)
1532     {
1533       column_button_create (clist, column);
1534       new_button = 1;
1535     }
1536 
1537   column_title_new (clist, column, NULL);
1538 
1539   /* remove and destroy the old widget */
1540   old_widget = GTK_BIN (clist->column[column].button)->child;
1541   if (old_widget)
1542     gtk_container_remove (GTK_CONTAINER (clist->column[column].button),
1543 			  old_widget);
1544 
1545   /* add and show the widget */
1546   if (widget)
1547     {
1548       gtk_container_add (GTK_CONTAINER (clist->column[column].button), widget);
1549       gtk_widget_show (widget);
1550     }
1551 
1552   /* if this button didn't previously exist, then the
1553    * column button positions have to be re-computed */
1554   if (gtk_widget_get_visible (GTK_WIDGET (clist)) && new_button)
1555     size_allocate_title_buttons (clist);
1556 }
1557 
1558 GtkWidget *
gtk_clist_get_column_widget(GtkCList * clist,gint column)1559 gtk_clist_get_column_widget (GtkCList *clist,
1560 			     gint      column)
1561 {
1562   g_return_val_if_fail (GTK_IS_CLIST (clist), NULL);
1563 
1564   if (column < 0 || column >= clist->columns)
1565     return NULL;
1566 
1567   if (clist->column[column].button)
1568     return GTK_BIN (clist->column[column].button)->child;
1569 
1570   return NULL;
1571 }
1572 
1573 void
gtk_clist_set_column_justification(GtkCList * clist,gint column,GtkJustification justification)1574 gtk_clist_set_column_justification (GtkCList         *clist,
1575 				    gint              column,
1576 				    GtkJustification  justification)
1577 {
1578   GtkWidget *alignment;
1579 
1580   g_return_if_fail (GTK_IS_CLIST (clist));
1581 
1582   if (column < 0 || column >= clist->columns)
1583     return;
1584 
1585   clist->column[column].justification = justification;
1586 
1587   /* change the alinment of the button title if it's not a
1588    * custom widget */
1589   if (clist->column[column].title)
1590     {
1591       alignment = GTK_BIN (clist->column[column].button)->child;
1592 
1593       switch (clist->column[column].justification)
1594 	{
1595 	case GTK_JUSTIFY_LEFT:
1596 	  gtk_alignment_set (GTK_ALIGNMENT (alignment), 0.0, 0.5, 0.0, 0.0);
1597 	  break;
1598 
1599 	case GTK_JUSTIFY_RIGHT:
1600 	  gtk_alignment_set (GTK_ALIGNMENT (alignment), 1.0, 0.5, 0.0, 0.0);
1601 	  break;
1602 
1603 	case GTK_JUSTIFY_CENTER:
1604 	  gtk_alignment_set (GTK_ALIGNMENT (alignment), 0.5, 0.5, 0.0, 0.0);
1605 	  break;
1606 
1607 	case GTK_JUSTIFY_FILL:
1608 	  gtk_alignment_set (GTK_ALIGNMENT (alignment), 0.5, 0.5, 0.0, 0.0);
1609 	  break;
1610 
1611 	default:
1612 	  break;
1613 	}
1614     }
1615 
1616   if (CLIST_UNFROZEN (clist))
1617     draw_rows (clist, NULL);
1618 }
1619 
1620 void
gtk_clist_set_column_visibility(GtkCList * clist,gint column,gboolean visible)1621 gtk_clist_set_column_visibility (GtkCList *clist,
1622 				 gint      column,
1623 				 gboolean  visible)
1624 {
1625   g_return_if_fail (GTK_IS_CLIST (clist));
1626 
1627   if (column < 0 || column >= clist->columns)
1628     return;
1629   if (clist->column[column].visible == visible)
1630     return;
1631 
1632   /* don't hide last visible column */
1633   if (!visible)
1634     {
1635       gint i;
1636       gint vis_columns = 0;
1637 
1638       for (i = 0, vis_columns = 0; i < clist->columns && vis_columns < 2; i++)
1639 	if (clist->column[i].visible)
1640 	  vis_columns++;
1641 
1642       if (vis_columns < 2)
1643 	return;
1644     }
1645 
1646   clist->column[column].visible = visible;
1647 
1648   if (clist->column[column].button)
1649     {
1650       if (visible)
1651 	gtk_widget_show (clist->column[column].button);
1652       else
1653 	gtk_widget_hide (clist->column[column].button);
1654     }
1655 
1656   gtk_widget_queue_resize (GTK_WIDGET(clist));
1657 }
1658 
1659 void
gtk_clist_set_column_resizeable(GtkCList * clist,gint column,gboolean resizeable)1660 gtk_clist_set_column_resizeable (GtkCList *clist,
1661 				 gint      column,
1662 				 gboolean  resizeable)
1663 {
1664   g_return_if_fail (GTK_IS_CLIST (clist));
1665 
1666   if (column < 0 || column >= clist->columns)
1667     return;
1668   if (clist->column[column].resizeable == resizeable)
1669     return;
1670 
1671   clist->column[column].resizeable = resizeable;
1672   if (resizeable)
1673     clist->column[column].auto_resize = FALSE;
1674 
1675   if (gtk_widget_get_visible (GTK_WIDGET (clist)))
1676     size_allocate_title_buttons (clist);
1677 }
1678 
1679 void
gtk_clist_set_column_auto_resize(GtkCList * clist,gint column,gboolean auto_resize)1680 gtk_clist_set_column_auto_resize (GtkCList *clist,
1681 				  gint      column,
1682 				  gboolean  auto_resize)
1683 {
1684   g_return_if_fail (GTK_IS_CLIST (clist));
1685 
1686   if (column < 0 || column >= clist->columns)
1687     return;
1688   if (clist->column[column].auto_resize == auto_resize)
1689     return;
1690 
1691   clist->column[column].auto_resize = auto_resize;
1692   if (auto_resize)
1693     {
1694       clist->column[column].resizeable = FALSE;
1695       if (!GTK_CLIST_AUTO_RESIZE_BLOCKED (clist))
1696 	{
1697 	  gint width;
1698 
1699 	  width = gtk_clist_optimal_column_width (clist, column);
1700 	  gtk_clist_set_column_width (clist, column, width);
1701 	}
1702     }
1703 
1704   if (gtk_widget_get_visible (GTK_WIDGET (clist)))
1705     size_allocate_title_buttons (clist);
1706 }
1707 
1708 gint
gtk_clist_columns_autosize(GtkCList * clist)1709 gtk_clist_columns_autosize (GtkCList *clist)
1710 {
1711   gint i;
1712   gint width;
1713 
1714   g_return_val_if_fail (GTK_IS_CLIST (clist), 0);
1715 
1716   gtk_clist_freeze (clist);
1717   width = 0;
1718   for (i = 0; i < clist->columns; i++)
1719     {
1720       gtk_clist_set_column_width (clist, i,
1721 				  gtk_clist_optimal_column_width (clist, i));
1722 
1723       width += clist->column[i].width;
1724     }
1725 
1726   gtk_clist_thaw (clist);
1727   return width;
1728 }
1729 
1730 gint
gtk_clist_optimal_column_width(GtkCList * clist,gint column)1731 gtk_clist_optimal_column_width (GtkCList *clist,
1732 				gint      column)
1733 {
1734   GtkRequisition requisition;
1735   GList *list;
1736   gint width;
1737 
1738   g_return_val_if_fail (GTK_CLIST (clist), 0);
1739 
1740   if (column < 0 || column >= clist->columns)
1741     return 0;
1742 
1743   if (GTK_CLIST_SHOW_TITLES(clist) && clist->column[column].button)
1744     width = (clist->column[column].button->requisition.width)
1745 #if 0
1746 	     (CELL_SPACING + (2 * COLUMN_INSET)))
1747 #endif
1748 		;
1749   else
1750     width = 0;
1751 
1752   for (list = clist->row_list; list; list = list->next)
1753     {
1754   GTK_CLIST_GET_CLASS (clist)->cell_size_request
1755 	(clist, GTK_CLIST_ROW (list), column, &requisition);
1756       width = MAX (width, requisition.width);
1757     }
1758 
1759   return width;
1760 }
1761 
1762 void
gtk_clist_set_column_width(GtkCList * clist,gint column,gint width)1763 gtk_clist_set_column_width (GtkCList *clist,
1764 			    gint      column,
1765 			    gint      width)
1766 {
1767   g_return_if_fail (GTK_IS_CLIST (clist));
1768 
1769   if (column < 0 || column >= clist->columns)
1770     return;
1771 
1772   gtk_signal_emit (GTK_OBJECT (clist), clist_signals[RESIZE_COLUMN],
1773 		   column, width);
1774 }
1775 
1776 void
gtk_clist_set_column_min_width(GtkCList * clist,gint column,gint min_width)1777 gtk_clist_set_column_min_width (GtkCList *clist,
1778 				gint      column,
1779 				gint      min_width)
1780 {
1781   g_return_if_fail (GTK_IS_CLIST (clist));
1782 
1783   if (column < 0 || column >= clist->columns)
1784     return;
1785   if (clist->column[column].min_width == min_width)
1786     return;
1787 
1788   if (clist->column[column].max_width >= 0  &&
1789       clist->column[column].max_width < min_width)
1790     clist->column[column].min_width = clist->column[column].max_width;
1791   else
1792     clist->column[column].min_width = min_width;
1793 
1794   if (clist->column[column].area.width < clist->column[column].min_width)
1795     gtk_clist_set_column_width (clist, column,clist->column[column].min_width);
1796 }
1797 
1798 void
gtk_clist_set_column_max_width(GtkCList * clist,gint column,gint max_width)1799 gtk_clist_set_column_max_width (GtkCList *clist,
1800 				gint      column,
1801 				gint      max_width)
1802 {
1803   g_return_if_fail (GTK_IS_CLIST (clist));
1804 
1805   if (column < 0 || column >= clist->columns)
1806     return;
1807   if (clist->column[column].max_width == max_width)
1808     return;
1809 
1810   if (clist->column[column].min_width >= 0 && max_width >= 0 &&
1811       clist->column[column].min_width > max_width)
1812     clist->column[column].max_width = clist->column[column].min_width;
1813   else
1814     clist->column[column].max_width = max_width;
1815 
1816   if (clist->column[column].area.width > clist->column[column].max_width)
1817     gtk_clist_set_column_width (clist, column,clist->column[column].max_width);
1818 }
1819 
1820 /* PRIVATE COLUMN FUNCTIONS
1821  *   column_auto_resize
1822  *   real_resize_column
1823  *   abort_column_resize
1824  *   size_allocate_title_buttons
1825  *   size_allocate_columns
1826  *   list_requisition_width
1827  *   new_column_width
1828  *   column_button_create
1829  *   column_button_clicked
1830  *   column_title_passive_func
1831  */
1832 static void
column_auto_resize(GtkCList * clist,GtkCListRow * clist_row,gint column,gint old_width)1833 column_auto_resize (GtkCList    *clist,
1834 		    GtkCListRow *clist_row,
1835 		    gint         column,
1836 		    gint         old_width)
1837 {
1838   /* resize column if needed for auto_resize */
1839   GtkRequisition requisition;
1840 
1841   if (!clist->column[column].auto_resize ||
1842       GTK_CLIST_AUTO_RESIZE_BLOCKED (clist))
1843     return;
1844 
1845   if (clist_row)
1846     GTK_CLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row,
1847 						   column, &requisition);
1848   else
1849     requisition.width = 0;
1850 
1851   if (requisition.width > clist->column[column].width)
1852     gtk_clist_set_column_width (clist, column, requisition.width);
1853   else if (requisition.width < old_width &&
1854 	   old_width == clist->column[column].width)
1855     {
1856       GList *list;
1857       gint new_width = 0;
1858 
1859       /* run a "gtk_clist_optimal_column_width" but break, if
1860        * the column doesn't shrink */
1861       if (GTK_CLIST_SHOW_TITLES(clist) && clist->column[column].button)
1862 	new_width = (clist->column[column].button->requisition.width -
1863 		     (CELL_SPACING + (2 * COLUMN_INSET)));
1864       else
1865 	new_width = 0;
1866 
1867       for (list = clist->row_list; list; list = list->next)
1868 	{
1869 	  GTK_CLIST_GET_CLASS (clist)->cell_size_request
1870 	    (clist, GTK_CLIST_ROW (list), column, &requisition);
1871 	  new_width = MAX (new_width, requisition.width);
1872 	  if (new_width == clist->column[column].width)
1873 	    break;
1874 	}
1875       if (new_width < clist->column[column].width)
1876 	gtk_clist_set_column_width
1877 	  (clist, column, MAX (new_width, clist->column[column].min_width));
1878     }
1879 }
1880 
1881 static void
real_resize_column(GtkCList * clist,gint column,gint width)1882 real_resize_column (GtkCList *clist,
1883 		    gint      column,
1884 		    gint      width)
1885 {
1886   g_return_if_fail (GTK_IS_CLIST (clist));
1887 
1888   if (column < 0 || column >= clist->columns)
1889     return;
1890 
1891   if (width < MAX (COLUMN_MIN_WIDTH, clist->column[column].min_width))
1892     width = MAX (COLUMN_MIN_WIDTH, clist->column[column].min_width);
1893   if (clist->column[column].max_width >= 0 &&
1894       width > clist->column[column].max_width)
1895     width = clist->column[column].max_width;
1896 
1897   clist->column[column].width = width;
1898   clist->column[column].width_set = TRUE;
1899 
1900   /* FIXME: this is quite expensive to do if the widget hasn't
1901    *        been size_allocated yet, and pointless. Should
1902    *        a flag be kept
1903    */
1904   size_allocate_columns (clist, TRUE);
1905   size_allocate_title_buttons (clist);
1906 
1907   CLIST_REFRESH (clist);
1908 }
1909 
1910 static void
abort_column_resize(GtkCList * clist)1911 abort_column_resize (GtkCList *clist)
1912 {
1913   g_return_if_fail (GTK_IS_CLIST (clist));
1914 
1915   if (!GTK_CLIST_IN_DRAG(clist))
1916     return;
1917 
1918   GTK_CLIST_UNSET_FLAG (clist, CLIST_IN_DRAG);
1919   gtk_grab_remove (GTK_WIDGET (clist));
1920   gdk_display_pointer_ungrab (gtk_widget_get_display (GTK_WIDGET (clist)),
1921 			      GDK_CURRENT_TIME);
1922   clist->drag_pos = -1;
1923 
1924   if (clist->x_drag >= 0 && clist->x_drag <= clist->clist_window_width - 1)
1925     draw_xor_line (clist);
1926 
1927   if (GTK_CLIST_ADD_MODE(clist))
1928     {
1929       gint8 dashes[] = { 4, 4 };
1930 
1931       gdk_gc_set_line_attributes (clist->xor_gc, 1, GDK_LINE_ON_OFF_DASH, 0,0);
1932       gdk_gc_set_dashes (clist->xor_gc, 0, dashes, G_N_ELEMENTS (dashes));
1933     }
1934 }
1935 
1936 static void
size_allocate_title_buttons(GtkCList * clist)1937 size_allocate_title_buttons (GtkCList *clist)
1938 {
1939   GtkAllocation button_allocation;
1940   gint last_column;
1941   gint last_button = 0;
1942   gint i;
1943 
1944   if (!gtk_widget_get_realized (GTK_WIDGET (clist)))
1945     return;
1946 
1947   button_allocation.x = clist->hoffset;
1948   button_allocation.y = 0;
1949   button_allocation.width = 0;
1950   button_allocation.height = clist->column_title_area.height;
1951 
1952   /* find last visible column */
1953   for (last_column = clist->columns - 1; last_column >= 0; last_column--)
1954     if (clist->column[last_column].visible)
1955       break;
1956 
1957   for (i = 0; i < last_column; i++)
1958     {
1959       if (!clist->column[i].visible)
1960 	{
1961 	  last_button = i + 1;
1962 	  gdk_window_hide (clist->column[i].window);
1963 	  continue;
1964 	}
1965 
1966       button_allocation.width += (clist->column[i].area.width +
1967 				  CELL_SPACING + 2 * COLUMN_INSET);
1968 
1969       if (!clist->column[i + 1].button)
1970 	{
1971 	  gdk_window_hide (clist->column[i].window);
1972 	  continue;
1973 	}
1974 
1975       gtk_widget_size_allocate (clist->column[last_button].button,
1976 				&button_allocation);
1977       button_allocation.x += button_allocation.width;
1978       button_allocation.width = 0;
1979 
1980       if (clist->column[last_button].resizeable)
1981 	{
1982 	  gdk_window_show (clist->column[last_button].window);
1983 	  gdk_window_move_resize (clist->column[last_button].window,
1984 				  button_allocation.x - (DRAG_WIDTH / 2),
1985 				  0, DRAG_WIDTH,
1986 				  clist->column_title_area.height);
1987 	}
1988       else
1989 	gdk_window_hide (clist->column[last_button].window);
1990 
1991       last_button = i + 1;
1992     }
1993 
1994   button_allocation.width += (clist->column[last_column].area.width +
1995 			      2 * (CELL_SPACING + COLUMN_INSET));
1996   gtk_widget_size_allocate (clist->column[last_button].button,
1997 			    &button_allocation);
1998 
1999   if (clist->column[last_button].resizeable)
2000     {
2001       button_allocation.x += button_allocation.width;
2002 
2003       gdk_window_show (clist->column[last_button].window);
2004       gdk_window_move_resize (clist->column[last_button].window,
2005 			      button_allocation.x - (DRAG_WIDTH / 2),
2006 			      0, DRAG_WIDTH, clist->column_title_area.height);
2007     }
2008   else
2009     gdk_window_hide (clist->column[last_button].window);
2010 }
2011 
2012 static void
size_allocate_columns(GtkCList * clist,gboolean block_resize)2013 size_allocate_columns (GtkCList *clist,
2014 		       gboolean  block_resize)
2015 {
2016   gint xoffset = CELL_SPACING + COLUMN_INSET;
2017   gint last_column;
2018   gint i;
2019 
2020   /* find last visible column and calculate correct column width */
2021   for (last_column = clist->columns - 1;
2022        last_column >= 0 && !clist->column[last_column].visible; last_column--);
2023 
2024   if (last_column < 0)
2025     return;
2026 
2027   for (i = 0; i <= last_column; i++)
2028     {
2029       if (!clist->column[i].visible)
2030 	continue;
2031       clist->column[i].area.x = xoffset;
2032       if (clist->column[i].width_set)
2033 	{
2034 	  if (!block_resize && GTK_CLIST_SHOW_TITLES(clist) &&
2035 	      clist->column[i].auto_resize && clist->column[i].button)
2036 	    {
2037 	      gint width;
2038 
2039 	      width = (clist->column[i].button->requisition.width -
2040 		       (CELL_SPACING + (2 * COLUMN_INSET)));
2041 
2042 	      if (width > clist->column[i].width)
2043 		gtk_clist_set_column_width (clist, i, width);
2044 	    }
2045 
2046 	  clist->column[i].area.width = clist->column[i].width;
2047 	  xoffset += clist->column[i].width + CELL_SPACING + (2* COLUMN_INSET);
2048 	}
2049       else if (GTK_CLIST_SHOW_TITLES(clist) && clist->column[i].button)
2050 	{
2051 	  clist->column[i].area.width =
2052 	    clist->column[i].button->requisition.width -
2053 	    (CELL_SPACING + (2 * COLUMN_INSET));
2054 	  xoffset += clist->column[i].button->requisition.width;
2055 	}
2056     }
2057 
2058   clist->column[last_column].area.width = clist->column[last_column].area.width
2059     + MAX (0, clist->clist_window_width + COLUMN_INSET - xoffset);
2060 }
2061 
2062 static gint
list_requisition_width(GtkCList * clist)2063 list_requisition_width (GtkCList *clist)
2064 {
2065   gint width = CELL_SPACING;
2066   gint i;
2067 
2068   for (i = clist->columns - 1; i >= 0; i--)
2069     {
2070       if (!clist->column[i].visible)
2071 	continue;
2072 
2073       if (clist->column[i].width_set)
2074 	width += clist->column[i].width + CELL_SPACING + (2 * COLUMN_INSET);
2075       else if (GTK_CLIST_SHOW_TITLES(clist) && clist->column[i].button)
2076 	width += clist->column[i].button->requisition.width;
2077     }
2078 
2079   return width;
2080 }
2081 
2082 /* this function returns the new width of the column being resized given
2083  * the column and x position of the cursor; the x cursor position is passed
2084  * in as a pointer and automagicly corrected if it's beyond min/max limits */
2085 static gint
new_column_width(GtkCList * clist,gint column,gint * x)2086 new_column_width (GtkCList *clist,
2087 		  gint      column,
2088 		  gint     *x)
2089 {
2090   gint xthickness = GTK_WIDGET (clist)->style->xthickness;
2091   gint width;
2092   gint cx;
2093   gint dx;
2094   gint last_column;
2095 
2096   /* first translate the x position from widget->window
2097    * to clist->clist_window */
2098   cx = *x - xthickness;
2099 
2100   for (last_column = clist->columns - 1;
2101        last_column >= 0 && !clist->column[last_column].visible; last_column--);
2102 
2103   /* calculate new column width making sure it doesn't end up
2104    * less than the minimum width */
2105   dx = (COLUMN_LEFT_XPIXEL (clist, column) + COLUMN_INSET +
2106 	(column < last_column) * CELL_SPACING);
2107   width = cx - dx;
2108 
2109   if (width < MAX (COLUMN_MIN_WIDTH, clist->column[column].min_width))
2110     {
2111       width = MAX (COLUMN_MIN_WIDTH, clist->column[column].min_width);
2112       cx = dx + width;
2113       *x = cx + xthickness;
2114     }
2115   else if (clist->column[column].max_width >= COLUMN_MIN_WIDTH &&
2116 	   width > clist->column[column].max_width)
2117     {
2118       width = clist->column[column].max_width;
2119       cx = dx + clist->column[column].max_width;
2120       *x = cx + xthickness;
2121     }
2122 
2123   if (cx < 0 || cx > clist->clist_window_width)
2124     *x = -1;
2125 
2126   return width;
2127 }
2128 
2129 static void
column_button_create(GtkCList * clist,gint column)2130 column_button_create (GtkCList *clist,
2131 		      gint      column)
2132 {
2133   GtkWidget *button;
2134 
2135   gtk_widget_push_composite_child ();
2136   button = clist->column[column].button = gtk_button_new ();
2137   gtk_widget_pop_composite_child ();
2138 
2139   if (gtk_widget_get_realized (GTK_WIDGET (clist)) && clist->title_window)
2140     gtk_widget_set_parent_window (clist->column[column].button,
2141 				  clist->title_window);
2142   gtk_widget_set_parent (button, GTK_WIDGET (clist));
2143 
2144   gtk_signal_connect (GTK_OBJECT (button), "clicked",
2145 		      G_CALLBACK (column_button_clicked),
2146 		      (gpointer) clist);
2147   gtk_widget_show (button);
2148 }
2149 
2150 static void
column_button_clicked(GtkWidget * widget,gpointer data)2151 column_button_clicked (GtkWidget *widget,
2152 		       gpointer   data)
2153 {
2154   gint i;
2155   GtkCList *clist;
2156 
2157   g_return_if_fail (widget != NULL);
2158   g_return_if_fail (GTK_IS_CLIST (data));
2159 
2160   clist = GTK_CLIST (data);
2161 
2162   /* find the column who's button was pressed */
2163   for (i = 0; i < clist->columns; i++)
2164     if (clist->column[i].button == widget)
2165       break;
2166 
2167   gtk_signal_emit (GTK_OBJECT (clist), clist_signals[CLICK_COLUMN], i);
2168 }
2169 
2170 static gint
column_title_passive_func(GtkWidget * widget,GdkEvent * event,gpointer data)2171 column_title_passive_func (GtkWidget *widget,
2172 			   GdkEvent  *event,
2173 			   gpointer   data)
2174 {
2175   g_return_val_if_fail (event != NULL, FALSE);
2176 
2177   switch (event->type)
2178     {
2179     case GDK_MOTION_NOTIFY:
2180     case GDK_BUTTON_PRESS:
2181     case GDK_2BUTTON_PRESS:
2182     case GDK_3BUTTON_PRESS:
2183     case GDK_BUTTON_RELEASE:
2184     case GDK_ENTER_NOTIFY:
2185     case GDK_LEAVE_NOTIFY:
2186       return TRUE;
2187     default:
2188       break;
2189     }
2190   return FALSE;
2191 }
2192 
2193 
2194 /* PUBLIC CELL FUNCTIONS
2195  *   gtk_clist_get_cell_type
2196  *   gtk_clist_set_text
2197  *   gtk_clist_get_text
2198  *   gtk_clist_set_pixmap
2199  *   gtk_clist_get_pixmap
2200  *   gtk_clist_set_pixtext
2201  *   gtk_clist_get_pixtext
2202  *   gtk_clist_set_shift
2203  */
2204 GtkCellType
gtk_clist_get_cell_type(GtkCList * clist,gint row,gint column)2205 gtk_clist_get_cell_type (GtkCList *clist,
2206 			 gint      row,
2207 			 gint      column)
2208 {
2209   GtkCListRow *clist_row;
2210 
2211   g_return_val_if_fail (GTK_IS_CLIST (clist), -1);
2212 
2213   if (row < 0 || row >= clist->rows)
2214     return -1;
2215   if (column < 0 || column >= clist->columns)
2216     return -1;
2217 
2218   clist_row = ROW_ELEMENT (clist, row)->data;
2219 
2220   return clist_row->cell[column].type;
2221 }
2222 
2223 void
gtk_clist_set_text(GtkCList * clist,gint row,gint column,const gchar * text)2224 gtk_clist_set_text (GtkCList    *clist,
2225 		    gint         row,
2226 		    gint         column,
2227 		    const gchar *text)
2228 {
2229   GtkCListRow *clist_row;
2230 
2231   g_return_if_fail (GTK_IS_CLIST (clist));
2232 
2233   if (row < 0 || row >= clist->rows)
2234     return;
2235   if (column < 0 || column >= clist->columns)
2236     return;
2237 
2238   clist_row = ROW_ELEMENT (clist, row)->data;
2239 
2240   /* if text is null, then the cell is empty */
2241   GTK_CLIST_GET_CLASS (clist)->set_cell_contents
2242     (clist, clist_row, column, GTK_CELL_TEXT, text, 0, NULL, NULL);
2243 
2244   /* redraw the list if it's not frozen */
2245   if (CLIST_UNFROZEN (clist))
2246     {
2247       if (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
2248 	GTK_CLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
2249     }
2250 }
2251 
2252 gint
gtk_clist_get_text(GtkCList * clist,gint row,gint column,gchar ** text)2253 gtk_clist_get_text (GtkCList  *clist,
2254 		    gint       row,
2255 		    gint       column,
2256 		    gchar    **text)
2257 {
2258   GtkCListRow *clist_row;
2259 
2260   g_return_val_if_fail (GTK_IS_CLIST (clist), 0);
2261 
2262   if (row < 0 || row >= clist->rows)
2263     return 0;
2264   if (column < 0 || column >= clist->columns)
2265     return 0;
2266 
2267   clist_row = ROW_ELEMENT (clist, row)->data;
2268 
2269   if (clist_row->cell[column].type != GTK_CELL_TEXT)
2270     return 0;
2271 
2272   if (text)
2273     *text = GTK_CELL_TEXT (clist_row->cell[column])->text;
2274 
2275   return 1;
2276 }
2277 
2278 /**
2279  * gtk_clist_set_pixmap:
2280  * @mask: (allow-none):
2281  */
2282 void
gtk_clist_set_pixmap(GtkCList * clist,gint row,gint column,GdkPixmap * pixmap,GdkBitmap * mask)2283 gtk_clist_set_pixmap (GtkCList  *clist,
2284 		      gint       row,
2285 		      gint       column,
2286 		      GdkPixmap *pixmap,
2287 		      GdkBitmap *mask)
2288 {
2289   GtkCListRow *clist_row;
2290 
2291   g_return_if_fail (GTK_IS_CLIST (clist));
2292 
2293   if (row < 0 || row >= clist->rows)
2294     return;
2295   if (column < 0 || column >= clist->columns)
2296     return;
2297 
2298   clist_row = ROW_ELEMENT (clist, row)->data;
2299 
2300   g_object_ref (pixmap);
2301 
2302   if (mask) g_object_ref (mask);
2303 
2304   GTK_CLIST_GET_CLASS (clist)->set_cell_contents
2305     (clist, clist_row, column, GTK_CELL_PIXMAP, NULL, 0, pixmap, mask);
2306 
2307   /* redraw the list if it's not frozen */
2308   if (CLIST_UNFROZEN (clist))
2309     {
2310       if (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
2311 	GTK_CLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
2312     }
2313 }
2314 
2315 gint
gtk_clist_get_pixmap(GtkCList * clist,gint row,gint column,GdkPixmap ** pixmap,GdkBitmap ** mask)2316 gtk_clist_get_pixmap (GtkCList   *clist,
2317 		      gint        row,
2318 		      gint        column,
2319 		      GdkPixmap **pixmap,
2320 		      GdkBitmap **mask)
2321 {
2322   GtkCListRow *clist_row;
2323 
2324   g_return_val_if_fail (GTK_IS_CLIST (clist), 0);
2325 
2326   if (row < 0 || row >= clist->rows)
2327     return 0;
2328   if (column < 0 || column >= clist->columns)
2329     return 0;
2330 
2331   clist_row = ROW_ELEMENT (clist, row)->data;
2332 
2333   if (clist_row->cell[column].type != GTK_CELL_PIXMAP)
2334     return 0;
2335 
2336   if (pixmap)
2337   {
2338     *pixmap = GTK_CELL_PIXMAP (clist_row->cell[column])->pixmap;
2339     /* mask can be NULL */
2340     *mask = GTK_CELL_PIXMAP (clist_row->cell[column])->mask;
2341   }
2342 
2343   return 1;
2344 }
2345 
2346 void
gtk_clist_set_pixtext(GtkCList * clist,gint row,gint column,const gchar * text,guint8 spacing,GdkPixmap * pixmap,GdkBitmap * mask)2347 gtk_clist_set_pixtext (GtkCList    *clist,
2348 		       gint         row,
2349 		       gint         column,
2350 		       const gchar *text,
2351 		       guint8       spacing,
2352 		       GdkPixmap   *pixmap,
2353 		       GdkBitmap   *mask)
2354 {
2355   GtkCListRow *clist_row;
2356 
2357   g_return_if_fail (GTK_IS_CLIST (clist));
2358 
2359   if (row < 0 || row >= clist->rows)
2360     return;
2361   if (column < 0 || column >= clist->columns)
2362     return;
2363 
2364   clist_row = ROW_ELEMENT (clist, row)->data;
2365 
2366   g_object_ref (pixmap);
2367   if (mask) g_object_ref (mask);
2368   GTK_CLIST_GET_CLASS (clist)->set_cell_contents
2369     (clist, clist_row, column, GTK_CELL_PIXTEXT, text, spacing, pixmap, mask);
2370 
2371   /* redraw the list if it's not frozen */
2372   if (CLIST_UNFROZEN (clist))
2373     {
2374       if (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
2375 	GTK_CLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
2376     }
2377 }
2378 
2379 gint
gtk_clist_get_pixtext(GtkCList * clist,gint row,gint column,gchar ** text,guint8 * spacing,GdkPixmap ** pixmap,GdkBitmap ** mask)2380 gtk_clist_get_pixtext (GtkCList   *clist,
2381 		       gint        row,
2382 		       gint        column,
2383 		       gchar     **text,
2384 		       guint8     *spacing,
2385 		       GdkPixmap **pixmap,
2386 		       GdkBitmap **mask)
2387 {
2388   GtkCListRow *clist_row;
2389 
2390   g_return_val_if_fail (GTK_IS_CLIST (clist), 0);
2391 
2392   if (row < 0 || row >= clist->rows)
2393     return 0;
2394   if (column < 0 || column >= clist->columns)
2395     return 0;
2396 
2397   clist_row = ROW_ELEMENT (clist, row)->data;
2398 
2399   if (clist_row->cell[column].type != GTK_CELL_PIXTEXT)
2400     return 0;
2401 
2402   if (text)
2403     *text = GTK_CELL_PIXTEXT (clist_row->cell[column])->text;
2404   if (spacing)
2405     *spacing = GTK_CELL_PIXTEXT (clist_row->cell[column])->spacing;
2406   if (pixmap)
2407     *pixmap = GTK_CELL_PIXTEXT (clist_row->cell[column])->pixmap;
2408 
2409   /* mask can be NULL */
2410   if (mask)
2411     *mask = GTK_CELL_PIXTEXT (clist_row->cell[column])->mask;
2412 
2413   return 1;
2414 }
2415 
2416 void
gtk_clist_set_shift(GtkCList * clist,gint row,gint column,gint vertical,gint horizontal)2417 gtk_clist_set_shift (GtkCList *clist,
2418 		     gint      row,
2419 		     gint      column,
2420 		     gint      vertical,
2421 		     gint      horizontal)
2422 {
2423   GtkRequisition requisition = { 0 };
2424   GtkCListRow *clist_row;
2425 
2426   g_return_if_fail (GTK_IS_CLIST (clist));
2427 
2428   if (row < 0 || row >= clist->rows)
2429     return;
2430   if (column < 0 || column >= clist->columns)
2431     return;
2432 
2433   clist_row = ROW_ELEMENT (clist, row)->data;
2434 
2435   if (clist->column[column].auto_resize &&
2436       !GTK_CLIST_AUTO_RESIZE_BLOCKED (clist))
2437     GTK_CLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row,
2438 						   column, &requisition);
2439 
2440   clist_row->cell[column].vertical = vertical;
2441   clist_row->cell[column].horizontal = horizontal;
2442 
2443   column_auto_resize (clist, clist_row, column, requisition.width);
2444 
2445   if (CLIST_UNFROZEN (clist) && gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
2446     GTK_CLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
2447 }
2448 
2449 /* PRIVATE CELL FUNCTIONS
2450  *   set_cell_contents
2451  *   cell_size_request
2452  */
2453 static void
set_cell_contents(GtkCList * clist,GtkCListRow * clist_row,gint column,GtkCellType type,const gchar * text,guint8 spacing,GdkPixmap * pixmap,GdkBitmap * mask)2454 set_cell_contents (GtkCList    *clist,
2455 		   GtkCListRow *clist_row,
2456 		   gint         column,
2457 		   GtkCellType  type,
2458 		   const gchar *text,
2459 		   guint8       spacing,
2460 		   GdkPixmap   *pixmap,
2461 		   GdkBitmap   *mask)
2462 {
2463   GtkRequisition requisition;
2464   gchar *old_text = NULL;
2465   GdkPixmap *old_pixmap = NULL;
2466   GdkBitmap *old_mask = NULL;
2467 
2468   g_return_if_fail (GTK_IS_CLIST (clist));
2469   g_return_if_fail (clist_row != NULL);
2470 
2471   if (clist->column[column].auto_resize &&
2472       !GTK_CLIST_AUTO_RESIZE_BLOCKED (clist))
2473     GTK_CLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row,
2474 						   column, &requisition);
2475 
2476   switch (clist_row->cell[column].type)
2477     {
2478     case GTK_CELL_EMPTY:
2479       break;
2480     case GTK_CELL_TEXT:
2481       old_text = GTK_CELL_TEXT (clist_row->cell[column])->text;
2482       break;
2483     case GTK_CELL_PIXMAP:
2484       old_pixmap = GTK_CELL_PIXMAP (clist_row->cell[column])->pixmap;
2485       old_mask = GTK_CELL_PIXMAP (clist_row->cell[column])->mask;
2486       break;
2487     case GTK_CELL_PIXTEXT:
2488       old_text = GTK_CELL_PIXTEXT (clist_row->cell[column])->text;
2489       old_pixmap = GTK_CELL_PIXTEXT (clist_row->cell[column])->pixmap;
2490       old_mask = GTK_CELL_PIXTEXT (clist_row->cell[column])->mask;
2491       break;
2492     case GTK_CELL_WIDGET:
2493       /* unimplemented */
2494       break;
2495     default:
2496       break;
2497     }
2498 
2499   clist_row->cell[column].type = GTK_CELL_EMPTY;
2500 
2501   /* Note that pixmap and mask were already ref'ed by the caller
2502    */
2503   switch (type)
2504     {
2505     case GTK_CELL_TEXT:
2506       if (text)
2507 	{
2508 	  clist_row->cell[column].type = GTK_CELL_TEXT;
2509 	  GTK_CELL_TEXT (clist_row->cell[column])->text = g_strdup (text);
2510 	}
2511       break;
2512     case GTK_CELL_PIXMAP:
2513       if (pixmap)
2514 	{
2515 	  clist_row->cell[column].type = GTK_CELL_PIXMAP;
2516 	  GTK_CELL_PIXMAP (clist_row->cell[column])->pixmap = pixmap;
2517 	  /* We set the mask even if it is NULL */
2518 	  GTK_CELL_PIXMAP (clist_row->cell[column])->mask = mask;
2519 	}
2520       break;
2521     case GTK_CELL_PIXTEXT:
2522       if (text && pixmap)
2523 	{
2524 	  clist_row->cell[column].type = GTK_CELL_PIXTEXT;
2525 	  GTK_CELL_PIXTEXT (clist_row->cell[column])->text = g_strdup (text);
2526 	  GTK_CELL_PIXTEXT (clist_row->cell[column])->spacing = spacing;
2527 	  GTK_CELL_PIXTEXT (clist_row->cell[column])->pixmap = pixmap;
2528 	  GTK_CELL_PIXTEXT (clist_row->cell[column])->mask = mask;
2529 	}
2530       break;
2531     default:
2532       break;
2533     }
2534 
2535   if (clist->column[column].auto_resize &&
2536       !GTK_CLIST_AUTO_RESIZE_BLOCKED (clist))
2537     column_auto_resize (clist, clist_row, column, requisition.width);
2538 
2539   g_free (old_text);
2540   if (old_pixmap)
2541     g_object_unref (old_pixmap);
2542   if (old_mask)
2543     g_object_unref (old_mask);
2544 }
2545 
2546 PangoLayout *
_gtk_clist_create_cell_layout(GtkCList * clist,GtkCListRow * clist_row,gint column)2547 _gtk_clist_create_cell_layout (GtkCList       *clist,
2548 			       GtkCListRow    *clist_row,
2549 			       gint            column)
2550 {
2551   PangoLayout *layout;
2552   GtkStyle *style;
2553   GtkCell *cell;
2554   gchar *text;
2555 
2556   get_cell_style (clist, clist_row, GTK_STATE_NORMAL, column, &style,
2557 		  NULL, NULL);
2558 
2559 
2560   cell = &clist_row->cell[column];
2561   switch (cell->type)
2562     {
2563     case GTK_CELL_TEXT:
2564     case GTK_CELL_PIXTEXT:
2565       text = ((cell->type == GTK_CELL_PIXTEXT) ?
2566 	      GTK_CELL_PIXTEXT (*cell)->text :
2567 	      GTK_CELL_TEXT (*cell)->text);
2568 
2569       if (!text)
2570 	return NULL;
2571 
2572       layout = gtk_widget_create_pango_layout (GTK_WIDGET (clist),
2573 					       ((cell->type == GTK_CELL_PIXTEXT) ?
2574 						GTK_CELL_PIXTEXT (*cell)->text :
2575 						GTK_CELL_TEXT (*cell)->text));
2576       pango_layout_set_font_description (layout, style->font_desc);
2577 
2578       return layout;
2579 
2580     default:
2581       return NULL;
2582     }
2583 }
2584 
2585 static void
cell_size_request(GtkCList * clist,GtkCListRow * clist_row,gint column,GtkRequisition * requisition)2586 cell_size_request (GtkCList       *clist,
2587 		   GtkCListRow    *clist_row,
2588 		   gint            column,
2589 		   GtkRequisition *requisition)
2590 {
2591   gint width;
2592   gint height;
2593   PangoLayout *layout;
2594   PangoRectangle logical_rect;
2595 
2596   g_return_if_fail (GTK_IS_CLIST (clist));
2597   g_return_if_fail (requisition != NULL);
2598 
2599   layout = _gtk_clist_create_cell_layout (clist, clist_row, column);
2600   if (layout)
2601     {
2602       pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2603 
2604       requisition->width = logical_rect.width;
2605       requisition->height = logical_rect.height;
2606 
2607       g_object_unref (layout);
2608     }
2609   else
2610     {
2611       requisition->width  = 0;
2612       requisition->height = 0;
2613     }
2614 
2615   if (layout && clist_row->cell[column].type == GTK_CELL_PIXTEXT)
2616     requisition->width += GTK_CELL_PIXTEXT (clist_row->cell[column])->spacing;
2617 
2618   switch (clist_row->cell[column].type)
2619     {
2620     case GTK_CELL_PIXTEXT:
2621       gdk_drawable_get_size (GTK_CELL_PIXTEXT (clist_row->cell[column])->pixmap,
2622                              &width, &height);
2623       requisition->width += width;
2624       requisition->height = MAX (requisition->height, height);
2625       break;
2626     case GTK_CELL_PIXMAP:
2627       gdk_drawable_get_size (GTK_CELL_PIXMAP (clist_row->cell[column])->pixmap,
2628                              &width, &height);
2629       requisition->width += width;
2630       requisition->height = MAX (requisition->height, height);
2631       break;
2632 
2633     default:
2634       break;
2635     }
2636 
2637   requisition->width  += clist_row->cell[column].horizontal;
2638   requisition->height += clist_row->cell[column].vertical;
2639 }
2640 
2641 /* PUBLIC INSERT/REMOVE ROW FUNCTIONS
2642  *   gtk_clist_prepend
2643  *   gtk_clist_append
2644  *   gtk_clist_insert
2645  *   gtk_clist_remove
2646  *   gtk_clist_clear
2647  */
2648 gint
gtk_clist_prepend(GtkCList * clist,gchar * text[])2649 gtk_clist_prepend (GtkCList    *clist,
2650 		   gchar       *text[])
2651 {
2652   g_return_val_if_fail (GTK_IS_CLIST (clist), -1);
2653   g_return_val_if_fail (text != NULL, -1);
2654 
2655   return GTK_CLIST_GET_CLASS (clist)->insert_row (clist, 0, text);
2656 }
2657 
2658 gint
gtk_clist_append(GtkCList * clist,gchar * text[])2659 gtk_clist_append (GtkCList    *clist,
2660 		  gchar       *text[])
2661 {
2662   g_return_val_if_fail (GTK_IS_CLIST (clist), -1);
2663   g_return_val_if_fail (text != NULL, -1);
2664 
2665   return GTK_CLIST_GET_CLASS (clist)->insert_row (clist, clist->rows, text);
2666 }
2667 
2668 gint
gtk_clist_insert(GtkCList * clist,gint row,gchar * text[])2669 gtk_clist_insert (GtkCList    *clist,
2670 		  gint         row,
2671 		  gchar       *text[])
2672 {
2673   g_return_val_if_fail (GTK_IS_CLIST (clist), -1);
2674   g_return_val_if_fail (text != NULL, -1);
2675 
2676   if (row < 0 || row > clist->rows)
2677     row = clist->rows;
2678 
2679   return GTK_CLIST_GET_CLASS (clist)->insert_row (clist, row, text);
2680 }
2681 
2682 void
gtk_clist_remove(GtkCList * clist,gint row)2683 gtk_clist_remove (GtkCList *clist,
2684 		  gint      row)
2685 {
2686   GTK_CLIST_GET_CLASS (clist)->remove_row (clist, row);
2687 }
2688 
2689 void
gtk_clist_clear(GtkCList * clist)2690 gtk_clist_clear (GtkCList *clist)
2691 {
2692   g_return_if_fail (GTK_IS_CLIST (clist));
2693 
2694   GTK_CLIST_GET_CLASS (clist)->clear (clist);
2695 }
2696 
2697 /* PRIVATE INSERT/REMOVE ROW FUNCTIONS
2698  *   real_insert_row
2699  *   real_remove_row
2700  *   real_clear
2701  *   real_row_move
2702  */
2703 static gint
real_insert_row(GtkCList * clist,gint row,gchar * text[])2704 real_insert_row (GtkCList *clist,
2705 		 gint      row,
2706 		 gchar    *text[])
2707 {
2708   gint i;
2709   GtkCListRow *clist_row;
2710 
2711   g_return_val_if_fail (GTK_IS_CLIST (clist), -1);
2712   g_return_val_if_fail (text != NULL, -1);
2713 
2714   /* return if out of bounds */
2715   if (row < 0 || row > clist->rows)
2716     return -1;
2717 
2718   /* create the row */
2719   clist_row = row_new (clist);
2720 
2721   /* set the text in the row's columns */
2722   for (i = 0; i < clist->columns; i++)
2723     if (text[i])
2724       GTK_CLIST_GET_CLASS (clist)->set_cell_contents
2725 	(clist, clist_row, i, GTK_CELL_TEXT, text[i], 0, NULL ,NULL);
2726 
2727   if (!clist->rows)
2728     {
2729       clist->row_list = g_list_append (clist->row_list, clist_row);
2730       clist->row_list_end = clist->row_list;
2731     }
2732   else
2733     {
2734       if (GTK_CLIST_AUTO_SORT(clist))   /* override insertion pos */
2735 	{
2736 	  GList *work;
2737 
2738 	  row = 0;
2739 	  work = clist->row_list;
2740 
2741 	  if (clist->sort_type == GTK_SORT_ASCENDING)
2742 	    {
2743 	      while (row < clist->rows &&
2744 		     clist->compare (clist, clist_row,
2745 				     GTK_CLIST_ROW (work)) > 0)
2746 		{
2747 		  row++;
2748 		  work = work->next;
2749 		}
2750 	    }
2751 	  else
2752 	    {
2753 	      while (row < clist->rows &&
2754 		     clist->compare (clist, clist_row,
2755 				     GTK_CLIST_ROW (work)) < 0)
2756 		{
2757 		  row++;
2758 		  work = work->next;
2759 		}
2760 	    }
2761 	}
2762 
2763       /* reset the row end pointer if we're inserting at the end of the list */
2764       if (row == clist->rows)
2765 	clist->row_list_end = (g_list_append (clist->row_list_end,
2766 					      clist_row))->next;
2767       else
2768 	clist->row_list = g_list_insert (clist->row_list, clist_row, row);
2769 
2770     }
2771   clist->rows++;
2772 
2773   if (row < ROW_FROM_YPIXEL (clist, 0))
2774     clist->voffset -= (clist->row_height + CELL_SPACING);
2775 
2776   /* syncronize the selection list */
2777   sync_selection (clist, row, SYNC_INSERT);
2778 
2779   if (clist->rows == 1)
2780     {
2781       clist->focus_row = 0;
2782       if (clist->selection_mode == GTK_SELECTION_BROWSE)
2783 	gtk_clist_select_row (clist, 0, -1);
2784     }
2785 
2786   /* redraw the list if it isn't frozen */
2787   if (CLIST_UNFROZEN (clist))
2788     {
2789       adjust_adjustments (clist, FALSE);
2790 
2791       if (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
2792 	draw_rows (clist, NULL);
2793     }
2794 
2795   return row;
2796 }
2797 
2798 static void
real_remove_row(GtkCList * clist,gint row)2799 real_remove_row (GtkCList *clist,
2800 		 gint      row)
2801 {
2802   gint was_visible;
2803   GList *list;
2804   GtkCListRow *clist_row;
2805 
2806   g_return_if_fail (GTK_IS_CLIST (clist));
2807 
2808   /* return if out of bounds */
2809   if (row < 0 || row > (clist->rows - 1))
2810     return;
2811 
2812   was_visible = (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE);
2813 
2814   /* get the row we're going to delete */
2815   list = ROW_ELEMENT (clist, row);
2816   g_assert (list != NULL);
2817   clist_row = list->data;
2818 
2819   /* if we're removing a selected row, we have to make sure
2820    * it's properly unselected, and then sync up the clist->selected
2821    * list to reflect the deincrimented indexies of rows after the
2822    * removal */
2823   if (clist_row->state == GTK_STATE_SELECTED)
2824     gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW],
2825 		     row, -1, NULL);
2826 
2827   sync_selection (clist, row, SYNC_REMOVE);
2828 
2829   /* reset the row end pointer if we're removing at the end of the list */
2830   clist->rows--;
2831   if (clist->row_list == list)
2832     clist->row_list = g_list_next (list);
2833   if (clist->row_list_end == list)
2834     clist->row_list_end = g_list_previous (list);
2835   list = g_list_remove (list, clist_row);
2836 
2837   if (row < ROW_FROM_YPIXEL (clist, 0))
2838     clist->voffset += clist->row_height + CELL_SPACING;
2839 
2840   if (clist->selection_mode == GTK_SELECTION_BROWSE && !clist->selection &&
2841       clist->focus_row >= 0)
2842     gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
2843 		     clist->focus_row, -1, NULL);
2844 
2845   /* toast the row */
2846   row_delete (clist, clist_row);
2847 
2848   /* redraw the row if it isn't frozen */
2849   if (CLIST_UNFROZEN (clist))
2850     {
2851       adjust_adjustments (clist, FALSE);
2852 
2853       if (was_visible)
2854 	draw_rows (clist, NULL);
2855     }
2856 }
2857 
2858 static void
real_clear(GtkCList * clist)2859 real_clear (GtkCList *clist)
2860 {
2861   GList *list;
2862   GList *free_list;
2863   gint i;
2864 
2865   g_return_if_fail (GTK_IS_CLIST (clist));
2866 
2867   /* free up the selection list */
2868   g_list_free (clist->selection);
2869   g_list_free (clist->undo_selection);
2870   g_list_free (clist->undo_unselection);
2871 
2872   clist->selection = NULL;
2873   clist->selection_end = NULL;
2874   clist->undo_selection = NULL;
2875   clist->undo_unselection = NULL;
2876   clist->voffset = 0;
2877   clist->focus_row = -1;
2878   clist->anchor = -1;
2879   clist->undo_anchor = -1;
2880   clist->anchor_state = GTK_STATE_SELECTED;
2881   clist->drag_pos = -1;
2882 
2883   /* remove all the rows */
2884   GTK_CLIST_SET_FLAG (clist, CLIST_AUTO_RESIZE_BLOCKED);
2885   free_list = clist->row_list;
2886   clist->row_list = NULL;
2887   clist->row_list_end = NULL;
2888   clist->rows = 0;
2889   for (list = free_list; list; list = list->next)
2890     row_delete (clist, GTK_CLIST_ROW (list));
2891   g_list_free (free_list);
2892   GTK_CLIST_UNSET_FLAG (clist, CLIST_AUTO_RESIZE_BLOCKED);
2893   for (i = 0; i < clist->columns; i++)
2894     if (clist->column[i].auto_resize)
2895       {
2896 	if (GTK_CLIST_SHOW_TITLES(clist) && clist->column[i].button)
2897 	  gtk_clist_set_column_width
2898 	    (clist, i, (clist->column[i].button->requisition.width -
2899 			(CELL_SPACING + (2 * COLUMN_INSET))));
2900 	else
2901 	  gtk_clist_set_column_width (clist, i, 0);
2902       }
2903   /* zero-out the scrollbars */
2904   if (clist->vadjustment)
2905     {
2906       gtk_adjustment_set_value (clist->vadjustment, 0.0);
2907       CLIST_REFRESH (clist);
2908     }
2909   else
2910     gtk_widget_queue_resize (GTK_WIDGET (clist));
2911 }
2912 
2913 static void
real_row_move(GtkCList * clist,gint source_row,gint dest_row)2914 real_row_move (GtkCList *clist,
2915 	       gint      source_row,
2916 	       gint      dest_row)
2917 {
2918   GtkCListRow *clist_row;
2919   GList *list;
2920   gint first, last;
2921   gint d;
2922 
2923   g_return_if_fail (GTK_IS_CLIST (clist));
2924 
2925   if (GTK_CLIST_AUTO_SORT(clist))
2926     return;
2927 
2928   if (source_row < 0 || source_row >= clist->rows ||
2929       dest_row   < 0 || dest_row   >= clist->rows ||
2930       source_row == dest_row)
2931     return;
2932 
2933   gtk_clist_freeze (clist);
2934 
2935   /* unlink source row */
2936   clist_row = ROW_ELEMENT (clist, source_row)->data;
2937   if (source_row == clist->rows - 1)
2938     clist->row_list_end = clist->row_list_end->prev;
2939   clist->row_list = g_list_remove (clist->row_list, clist_row);
2940   clist->rows--;
2941 
2942   /* relink source row */
2943   clist->row_list = g_list_insert (clist->row_list, clist_row, dest_row);
2944   if (dest_row == clist->rows)
2945     clist->row_list_end = clist->row_list_end->next;
2946   clist->rows++;
2947 
2948   /* sync selection */
2949   if (source_row > dest_row)
2950     {
2951       first = dest_row;
2952       last  = source_row;
2953       d = 1;
2954     }
2955   else
2956     {
2957       first = source_row;
2958       last  = dest_row;
2959       d = -1;
2960     }
2961 
2962   for (list = clist->selection; list; list = list->next)
2963     {
2964       if (list->data == GINT_TO_POINTER (source_row))
2965 	list->data = GINT_TO_POINTER (dest_row);
2966       else if (first <= GPOINTER_TO_INT (list->data) &&
2967 	       last >= GPOINTER_TO_INT (list->data))
2968 	list->data = GINT_TO_POINTER (GPOINTER_TO_INT (list->data) + d);
2969     }
2970 
2971   if (clist->focus_row == source_row)
2972     clist->focus_row = dest_row;
2973   else if (clist->focus_row > first)
2974     clist->focus_row += d;
2975 
2976   gtk_clist_thaw (clist);
2977 }
2978 
2979 /* PUBLIC ROW FUNCTIONS
2980  *   gtk_clist_moveto
2981  *   gtk_clist_set_row_height
2982  *   gtk_clist_set_row_data
2983  *   gtk_clist_set_row_data_full
2984  *   gtk_clist_get_row_data
2985  *   gtk_clist_find_row_from_data
2986  *   gtk_clist_swap_rows
2987  *   gtk_clist_row_move
2988  *   gtk_clist_row_is_visible
2989  *   gtk_clist_set_foreground
2990  *   gtk_clist_set_background
2991  */
2992 void
gtk_clist_moveto(GtkCList * clist,gint row,gint column,gfloat row_align,gfloat col_align)2993 gtk_clist_moveto (GtkCList *clist,
2994 		  gint      row,
2995 		  gint      column,
2996 		  gfloat    row_align,
2997 		  gfloat    col_align)
2998 {
2999   g_return_if_fail (GTK_IS_CLIST (clist));
3000 
3001   if (row < -1 || row >= clist->rows)
3002     return;
3003   if (column < -1 || column >= clist->columns)
3004     return;
3005 
3006   row_align = CLAMP (row_align, 0, 1);
3007   col_align = CLAMP (col_align, 0, 1);
3008 
3009   /* adjust horizontal scrollbar */
3010   if (clist->hadjustment && column >= 0)
3011     {
3012       gint x;
3013 
3014       x = (COLUMN_LEFT (clist, column) - CELL_SPACING - COLUMN_INSET -
3015 	   (col_align * (clist->clist_window_width - 2 * COLUMN_INSET -
3016 			 CELL_SPACING - clist->column[column].area.width)));
3017       if (x < 0)
3018 	gtk_adjustment_set_value (clist->hadjustment, 0.0);
3019       else if (x > LIST_WIDTH (clist) - clist->clist_window_width)
3020 	gtk_adjustment_set_value
3021 	  (clist->hadjustment, LIST_WIDTH (clist) - clist->clist_window_width);
3022       else
3023 	gtk_adjustment_set_value (clist->hadjustment, x);
3024     }
3025 
3026   /* adjust vertical scrollbar */
3027   if (clist->vadjustment && row >= 0)
3028     move_vertical (clist, row, row_align);
3029 }
3030 
3031 void
gtk_clist_set_row_height(GtkCList * clist,guint height)3032 gtk_clist_set_row_height (GtkCList *clist,
3033 			  guint     height)
3034 {
3035   GtkWidget *widget;
3036 
3037   g_return_if_fail (GTK_IS_CLIST (clist));
3038 
3039   widget = GTK_WIDGET (clist);
3040 
3041   if (height > 0)
3042     {
3043       clist->row_height = height;
3044       GTK_CLIST_SET_FLAG (clist, CLIST_ROW_HEIGHT_SET);
3045     }
3046   else
3047     {
3048       GTK_CLIST_UNSET_FLAG (clist, CLIST_ROW_HEIGHT_SET);
3049       clist->row_height = 0;
3050     }
3051 
3052   if (widget->style->font_desc)
3053     {
3054       PangoContext *context = gtk_widget_get_pango_context (widget);
3055       PangoFontMetrics *metrics;
3056 
3057       metrics = pango_context_get_metrics (context,
3058 					   widget->style->font_desc,
3059 					   pango_context_get_language (context));
3060 
3061       if (!GTK_CLIST_ROW_HEIGHT_SET(clist))
3062 	{
3063 	  clist->row_height = (pango_font_metrics_get_ascent (metrics) +
3064 			       pango_font_metrics_get_descent (metrics));
3065 	  clist->row_height = PANGO_PIXELS (clist->row_height);
3066 	}
3067 
3068       pango_font_metrics_unref (metrics);
3069     }
3070 
3071   CLIST_REFRESH (clist);
3072 }
3073 
3074 void
gtk_clist_set_row_data(GtkCList * clist,gint row,gpointer data)3075 gtk_clist_set_row_data (GtkCList *clist,
3076 			gint      row,
3077 			gpointer  data)
3078 {
3079   gtk_clist_set_row_data_full (clist, row, data, NULL);
3080 }
3081 
3082 void
gtk_clist_set_row_data_full(GtkCList * clist,gint row,gpointer data,GDestroyNotify destroy)3083 gtk_clist_set_row_data_full (GtkCList       *clist,
3084 			     gint            row,
3085 			     gpointer        data,
3086 			     GDestroyNotify  destroy)
3087 {
3088   GtkCListRow *clist_row;
3089 
3090   g_return_if_fail (GTK_IS_CLIST (clist));
3091 
3092   if (row < 0 || row > (clist->rows - 1))
3093     return;
3094 
3095   clist_row = ROW_ELEMENT (clist, row)->data;
3096 
3097   if (clist_row->destroy)
3098     clist_row->destroy (clist_row->data);
3099 
3100   clist_row->data = data;
3101   clist_row->destroy = destroy;
3102 }
3103 
3104 gpointer
gtk_clist_get_row_data(GtkCList * clist,gint row)3105 gtk_clist_get_row_data (GtkCList *clist,
3106 			gint      row)
3107 {
3108   GtkCListRow *clist_row;
3109 
3110   g_return_val_if_fail (GTK_IS_CLIST (clist), NULL);
3111 
3112   if (row < 0 || row > (clist->rows - 1))
3113     return NULL;
3114 
3115   clist_row = ROW_ELEMENT (clist, row)->data;
3116   return clist_row->data;
3117 }
3118 
3119 gint
gtk_clist_find_row_from_data(GtkCList * clist,gpointer data)3120 gtk_clist_find_row_from_data (GtkCList *clist,
3121 			      gpointer  data)
3122 {
3123   GList *list;
3124   gint n;
3125 
3126   g_return_val_if_fail (GTK_IS_CLIST (clist), -1);
3127 
3128   for (n = 0, list = clist->row_list; list; n++, list = list->next)
3129     if (GTK_CLIST_ROW (list)->data == data)
3130       return n;
3131 
3132   return -1;
3133 }
3134 
3135 void
gtk_clist_swap_rows(GtkCList * clist,gint row1,gint row2)3136 gtk_clist_swap_rows (GtkCList *clist,
3137 		     gint      row1,
3138 		     gint      row2)
3139 {
3140   gint first, last;
3141 
3142   g_return_if_fail (GTK_IS_CLIST (clist));
3143   g_return_if_fail (row1 != row2);
3144 
3145   if (GTK_CLIST_AUTO_SORT(clist))
3146     return;
3147 
3148   gtk_clist_freeze (clist);
3149 
3150   first = MIN (row1, row2);
3151   last  = MAX (row1, row2);
3152 
3153   gtk_clist_row_move (clist, last, first);
3154   gtk_clist_row_move (clist, first + 1, last);
3155 
3156   gtk_clist_thaw (clist);
3157 }
3158 
3159 void
gtk_clist_row_move(GtkCList * clist,gint source_row,gint dest_row)3160 gtk_clist_row_move (GtkCList *clist,
3161 		    gint      source_row,
3162 		    gint      dest_row)
3163 {
3164   g_return_if_fail (GTK_IS_CLIST (clist));
3165 
3166   if (GTK_CLIST_AUTO_SORT(clist))
3167     return;
3168 
3169   if (source_row < 0 || source_row >= clist->rows ||
3170       dest_row   < 0 || dest_row   >= clist->rows ||
3171       source_row == dest_row)
3172     return;
3173 
3174   gtk_signal_emit (GTK_OBJECT (clist), clist_signals[ROW_MOVE],
3175 		   source_row, dest_row);
3176 }
3177 
3178 GtkVisibility
gtk_clist_row_is_visible(GtkCList * clist,gint row)3179 gtk_clist_row_is_visible (GtkCList *clist,
3180 			  gint      row)
3181 {
3182   gint top;
3183 
3184   g_return_val_if_fail (GTK_IS_CLIST (clist), 0);
3185 
3186   if (row < 0 || row >= clist->rows)
3187     return GTK_VISIBILITY_NONE;
3188 
3189   if (clist->row_height == 0)
3190     return GTK_VISIBILITY_NONE;
3191 
3192   if (row < ROW_FROM_YPIXEL (clist, 0))
3193     return GTK_VISIBILITY_NONE;
3194 
3195   if (row > ROW_FROM_YPIXEL (clist, clist->clist_window_height))
3196     return GTK_VISIBILITY_NONE;
3197 
3198   top = ROW_TOP_YPIXEL (clist, row);
3199 
3200   if ((top < 0)
3201       || ((top + clist->row_height) >= clist->clist_window_height))
3202     return GTK_VISIBILITY_PARTIAL;
3203 
3204   return GTK_VISIBILITY_FULL;
3205 }
3206 
3207 void
gtk_clist_set_foreground(GtkCList * clist,gint row,const GdkColor * color)3208 gtk_clist_set_foreground (GtkCList       *clist,
3209 			  gint            row,
3210 			  const GdkColor *color)
3211 {
3212   GtkCListRow *clist_row;
3213 
3214   g_return_if_fail (GTK_IS_CLIST (clist));
3215 
3216   if (row < 0 || row >= clist->rows)
3217     return;
3218 
3219   clist_row = ROW_ELEMENT (clist, row)->data;
3220 
3221   if (color)
3222     {
3223       clist_row->foreground = *color;
3224       clist_row->fg_set = TRUE;
3225       if (gtk_widget_get_realized (GTK_WIDGET (clist)))
3226 	gdk_colormap_alloc_color (gtk_widget_get_colormap (GTK_WIDGET (clist)),
3227                                   &clist_row->foreground, FALSE, TRUE);
3228     }
3229   else
3230     clist_row->fg_set = FALSE;
3231 
3232   if (CLIST_UNFROZEN (clist) && gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
3233     GTK_CLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
3234 }
3235 
3236 void
gtk_clist_set_background(GtkCList * clist,gint row,const GdkColor * color)3237 gtk_clist_set_background (GtkCList       *clist,
3238 			  gint            row,
3239 			  const GdkColor *color)
3240 {
3241   GtkCListRow *clist_row;
3242 
3243   g_return_if_fail (GTK_IS_CLIST (clist));
3244 
3245   if (row < 0 || row >= clist->rows)
3246     return;
3247 
3248   clist_row = ROW_ELEMENT (clist, row)->data;
3249 
3250   if (color)
3251     {
3252       clist_row->background = *color;
3253       clist_row->bg_set = TRUE;
3254       if (gtk_widget_get_realized (GTK_WIDGET (clist)))
3255 	gdk_colormap_alloc_color (gtk_widget_get_colormap (GTK_WIDGET (clist)),
3256                                   &clist_row->background, FALSE, TRUE);
3257     }
3258   else
3259     clist_row->bg_set = FALSE;
3260 
3261   if (CLIST_UNFROZEN (clist)
3262       && (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE))
3263     GTK_CLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
3264 }
3265 
3266 /* PUBLIC ROW/CELL STYLE FUNCTIONS
3267  *   gtk_clist_set_cell_style
3268  *   gtk_clist_get_cell_style
3269  *   gtk_clist_set_row_style
3270  *   gtk_clist_get_row_style
3271  */
3272 void
gtk_clist_set_cell_style(GtkCList * clist,gint row,gint column,GtkStyle * style)3273 gtk_clist_set_cell_style (GtkCList *clist,
3274 			  gint      row,
3275 			  gint      column,
3276 			  GtkStyle *style)
3277 {
3278   GtkRequisition requisition = { 0 };
3279   GtkCListRow *clist_row;
3280 
3281   g_return_if_fail (GTK_IS_CLIST (clist));
3282 
3283   if (row < 0 || row >= clist->rows)
3284     return;
3285   if (column < 0 || column >= clist->columns)
3286     return;
3287 
3288   clist_row = ROW_ELEMENT (clist, row)->data;
3289 
3290   if (clist_row->cell[column].style == style)
3291     return;
3292 
3293   if (clist->column[column].auto_resize &&
3294       !GTK_CLIST_AUTO_RESIZE_BLOCKED (clist))
3295     GTK_CLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row,
3296 						   column, &requisition);
3297 
3298   if (clist_row->cell[column].style)
3299     {
3300       if (gtk_widget_get_realized (GTK_WIDGET (clist)))
3301         gtk_style_detach (clist_row->cell[column].style);
3302       g_object_unref (clist_row->cell[column].style);
3303     }
3304 
3305   clist_row->cell[column].style = style;
3306 
3307   if (clist_row->cell[column].style)
3308     {
3309       g_object_ref (clist_row->cell[column].style);
3310 
3311       if (gtk_widget_get_realized (GTK_WIDGET (clist)))
3312         clist_row->cell[column].style =
3313 	  gtk_style_attach (clist_row->cell[column].style,
3314 			    clist->clist_window);
3315     }
3316 
3317   column_auto_resize (clist, clist_row, column, requisition.width);
3318 
3319   /* redraw the list if it's not frozen */
3320   if (CLIST_UNFROZEN (clist))
3321     {
3322       if (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
3323 	GTK_CLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
3324     }
3325 }
3326 
3327 GtkStyle *
gtk_clist_get_cell_style(GtkCList * clist,gint row,gint column)3328 gtk_clist_get_cell_style (GtkCList *clist,
3329 			  gint      row,
3330 			  gint      column)
3331 {
3332   GtkCListRow *clist_row;
3333 
3334   g_return_val_if_fail (GTK_IS_CLIST (clist), NULL);
3335 
3336   if (row < 0 || row >= clist->rows || column < 0 || column >= clist->columns)
3337     return NULL;
3338 
3339   clist_row = ROW_ELEMENT (clist, row)->data;
3340 
3341   return clist_row->cell[column].style;
3342 }
3343 
3344 void
gtk_clist_set_row_style(GtkCList * clist,gint row,GtkStyle * style)3345 gtk_clist_set_row_style (GtkCList *clist,
3346 			 gint      row,
3347 			 GtkStyle *style)
3348 {
3349   GtkRequisition requisition;
3350   GtkCListRow *clist_row;
3351   gint *old_width;
3352   gint i;
3353 
3354   g_return_if_fail (GTK_IS_CLIST (clist));
3355 
3356   if (row < 0 || row >= clist->rows)
3357     return;
3358 
3359   clist_row = ROW_ELEMENT (clist, row)->data;
3360 
3361   if (clist_row->style == style)
3362     return;
3363 
3364   old_width = g_new (gint, clist->columns);
3365 
3366   if (!GTK_CLIST_AUTO_RESIZE_BLOCKED (clist))
3367     {
3368       for (i = 0; i < clist->columns; i++)
3369 	if (clist->column[i].auto_resize)
3370 	  {
3371 	    GTK_CLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row,
3372 							   i, &requisition);
3373 	    old_width[i] = requisition.width;
3374 	  }
3375     }
3376 
3377   if (clist_row->style)
3378     {
3379       if (gtk_widget_get_realized (GTK_WIDGET (clist)))
3380         gtk_style_detach (clist_row->style);
3381       g_object_unref (clist_row->style);
3382     }
3383 
3384   clist_row->style = style;
3385 
3386   if (clist_row->style)
3387     {
3388       g_object_ref (clist_row->style);
3389 
3390       if (gtk_widget_get_realized (GTK_WIDGET (clist)))
3391         clist_row->style = gtk_style_attach (clist_row->style,
3392 					     clist->clist_window);
3393     }
3394 
3395   if (GTK_CLIST_AUTO_RESIZE_BLOCKED (clist))
3396     for (i = 0; i < clist->columns; i++)
3397       column_auto_resize (clist, clist_row, i, old_width[i]);
3398 
3399   g_free (old_width);
3400 
3401   /* redraw the list if it's not frozen */
3402   if (CLIST_UNFROZEN (clist))
3403     {
3404       if (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
3405 	GTK_CLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
3406     }
3407 }
3408 
3409 GtkStyle *
gtk_clist_get_row_style(GtkCList * clist,gint row)3410 gtk_clist_get_row_style (GtkCList *clist,
3411 			 gint      row)
3412 {
3413   GtkCListRow *clist_row;
3414 
3415   g_return_val_if_fail (GTK_IS_CLIST (clist), NULL);
3416 
3417   if (row < 0 || row >= clist->rows)
3418     return NULL;
3419 
3420   clist_row = ROW_ELEMENT (clist, row)->data;
3421 
3422   return clist_row->style;
3423 }
3424 
3425 /* PUBLIC SELECTION FUNCTIONS
3426  *   gtk_clist_set_selectable
3427  *   gtk_clist_get_selectable
3428  *   gtk_clist_select_row
3429  *   gtk_clist_unselect_row
3430  *   gtk_clist_select_all
3431  *   gtk_clist_unselect_all
3432  *   gtk_clist_undo_selection
3433  */
3434 void
gtk_clist_set_selectable(GtkCList * clist,gint row,gboolean selectable)3435 gtk_clist_set_selectable (GtkCList *clist,
3436 			  gint      row,
3437 			  gboolean  selectable)
3438 {
3439   GtkCListRow *clist_row;
3440 
3441   g_return_if_fail (GTK_IS_CLIST (clist));
3442 
3443   if (row < 0 || row >= clist->rows)
3444     return;
3445 
3446   clist_row = ROW_ELEMENT (clist, row)->data;
3447 
3448   if (selectable == clist_row->selectable)
3449     return;
3450 
3451   clist_row->selectable = selectable;
3452 
3453   if (!selectable && clist_row->state == GTK_STATE_SELECTED)
3454     {
3455       if (clist->anchor >= 0 &&
3456 	  clist->selection_mode == GTK_SELECTION_MULTIPLE)
3457 	{
3458 	  clist->drag_button = 0;
3459 	  remove_grab (clist);
3460 	  GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
3461 	}
3462       gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW],
3463 		       row, -1, NULL);
3464     }
3465 }
3466 
3467 gboolean
gtk_clist_get_selectable(GtkCList * clist,gint row)3468 gtk_clist_get_selectable (GtkCList *clist,
3469 			  gint      row)
3470 {
3471   g_return_val_if_fail (GTK_IS_CLIST (clist), FALSE);
3472 
3473   if (row < 0 || row >= clist->rows)
3474     return FALSE;
3475 
3476   return GTK_CLIST_ROW (ROW_ELEMENT (clist, row))->selectable;
3477 }
3478 
3479 void
gtk_clist_select_row(GtkCList * clist,gint row,gint column)3480 gtk_clist_select_row (GtkCList *clist,
3481 		      gint      row,
3482 		      gint      column)
3483 {
3484   g_return_if_fail (GTK_IS_CLIST (clist));
3485 
3486   if (row < 0 || row >= clist->rows)
3487     return;
3488   if (column < -1 || column >= clist->columns)
3489     return;
3490 
3491   gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
3492 		   row, column, NULL);
3493 }
3494 
3495 void
gtk_clist_unselect_row(GtkCList * clist,gint row,gint column)3496 gtk_clist_unselect_row (GtkCList *clist,
3497 			gint      row,
3498 			gint      column)
3499 {
3500   g_return_if_fail (GTK_IS_CLIST (clist));
3501 
3502   if (row < 0 || row >= clist->rows)
3503     return;
3504   if (column < -1 || column >= clist->columns)
3505     return;
3506 
3507   gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW],
3508 		   row, column, NULL);
3509 }
3510 
3511 void
gtk_clist_select_all(GtkCList * clist)3512 gtk_clist_select_all (GtkCList *clist)
3513 {
3514   g_return_if_fail (GTK_IS_CLIST (clist));
3515 
3516   GTK_CLIST_GET_CLASS (clist)->select_all (clist);
3517 }
3518 
3519 void
gtk_clist_unselect_all(GtkCList * clist)3520 gtk_clist_unselect_all (GtkCList *clist)
3521 {
3522   g_return_if_fail (GTK_IS_CLIST (clist));
3523 
3524   GTK_CLIST_GET_CLASS (clist)->unselect_all (clist);
3525 }
3526 
3527 void
gtk_clist_undo_selection(GtkCList * clist)3528 gtk_clist_undo_selection (GtkCList *clist)
3529 {
3530   g_return_if_fail (GTK_IS_CLIST (clist));
3531 
3532   if (clist->selection_mode == GTK_SELECTION_MULTIPLE &&
3533       (clist->undo_selection || clist->undo_unselection))
3534     gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNDO_SELECTION]);
3535 }
3536 
3537 /* PRIVATE SELECTION FUNCTIONS
3538  *   selection_find
3539  *   toggle_row
3540  *   fake_toggle_row
3541  *   toggle_focus_row
3542  *   toggle_add_mode
3543  *   real_select_row
3544  *   real_unselect_row
3545  *   real_select_all
3546  *   real_unselect_all
3547  *   fake_unselect_all
3548  *   real_undo_selection
3549  *   set_anchor
3550  *   resync_selection
3551  *   update_extended_selection
3552  *   start_selection
3553  *   end_selection
3554  *   extend_selection
3555  *   sync_selection
3556  */
3557 static GList *
selection_find(GtkCList * clist,gint row_number,GList * row_list_element)3558 selection_find (GtkCList *clist,
3559 		gint      row_number,
3560 		GList    *row_list_element)
3561 {
3562   return g_list_find (clist->selection, GINT_TO_POINTER (row_number));
3563 }
3564 
3565 static void
toggle_row(GtkCList * clist,gint row,gint column,GdkEvent * event)3566 toggle_row (GtkCList *clist,
3567 	    gint      row,
3568 	    gint      column,
3569 	    GdkEvent *event)
3570 {
3571   GtkCListRow *clist_row;
3572 
3573   switch (clist->selection_mode)
3574     {
3575     case GTK_SELECTION_MULTIPLE:
3576     case GTK_SELECTION_SINGLE:
3577       clist_row = ROW_ELEMENT (clist, row)->data;
3578 
3579       if (!clist_row)
3580 	return;
3581 
3582       if (clist_row->state == GTK_STATE_SELECTED)
3583 	{
3584 	  gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW],
3585 			   row, column, event);
3586 	  return;
3587 	}
3588     case GTK_SELECTION_BROWSE:
3589       gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
3590 		       row, column, event);
3591       break;
3592     default:
3593       g_assert_not_reached ();
3594     }
3595 }
3596 
3597 static void
fake_toggle_row(GtkCList * clist,gint row)3598 fake_toggle_row (GtkCList *clist,
3599 		 gint      row)
3600 {
3601   GList *work;
3602 
3603   work = ROW_ELEMENT (clist, row);
3604 
3605   if (!work || !GTK_CLIST_ROW (work)->selectable)
3606     return;
3607 
3608   if (GTK_CLIST_ROW (work)->state == GTK_STATE_NORMAL)
3609     clist->anchor_state = GTK_CLIST_ROW (work)->state = GTK_STATE_SELECTED;
3610   else
3611     clist->anchor_state = GTK_CLIST_ROW (work)->state = GTK_STATE_NORMAL;
3612 
3613   if (CLIST_UNFROZEN (clist) &&
3614       gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
3615     GTK_CLIST_GET_CLASS (clist)->draw_row (clist, NULL, row,
3616 					  GTK_CLIST_ROW (work));
3617 }
3618 
3619 static gboolean
clist_has_grab(GtkCList * clist)3620 clist_has_grab (GtkCList *clist)
3621 {
3622   return (GTK_WIDGET_HAS_GRAB (clist) &&
3623 	  gdk_display_pointer_is_grabbed (gtk_widget_get_display (GTK_WIDGET (clist))));
3624 }
3625 
3626 static void
toggle_focus_row(GtkCList * clist)3627 toggle_focus_row (GtkCList *clist)
3628 {
3629   g_return_if_fail (clist != 0);
3630   g_return_if_fail (GTK_IS_CLIST (clist));
3631 
3632   if (clist_has_grab (clist) ||
3633       clist->focus_row < 0 || clist->focus_row >= clist->rows)
3634     return;
3635 
3636   switch (clist->selection_mode)
3637     {
3638     case  GTK_SELECTION_SINGLE:
3639       toggle_row (clist, clist->focus_row, 0, NULL);
3640       break;
3641     case GTK_SELECTION_MULTIPLE:
3642       g_list_free (clist->undo_selection);
3643       g_list_free (clist->undo_unselection);
3644       clist->undo_selection = NULL;
3645       clist->undo_unselection = NULL;
3646 
3647       clist->anchor = clist->focus_row;
3648       clist->drag_pos = clist->focus_row;
3649       clist->undo_anchor = clist->focus_row;
3650 
3651       if (GTK_CLIST_ADD_MODE(clist))
3652 	fake_toggle_row (clist, clist->focus_row);
3653       else
3654 	GTK_CLIST_GET_CLASS (clist)->fake_unselect_all (clist,clist->focus_row);
3655 
3656       GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
3657       break;
3658     default:
3659       break;
3660     }
3661 }
3662 
3663 static void
toggle_add_mode(GtkCList * clist)3664 toggle_add_mode (GtkCList *clist)
3665 {
3666   g_return_if_fail (clist != 0);
3667   g_return_if_fail (GTK_IS_CLIST (clist));
3668 
3669   if (clist_has_grab (clist) ||
3670       clist->selection_mode != GTK_SELECTION_MULTIPLE)
3671     return;
3672 
3673   gtk_clist_draw_focus (GTK_WIDGET (clist));
3674   if (!GTK_CLIST_ADD_MODE(clist))
3675     {
3676       gint8 dashes[] = { 4, 4 };
3677 
3678       GTK_CLIST_SET_FLAG (clist, CLIST_ADD_MODE);
3679       gdk_gc_set_line_attributes (clist->xor_gc, 1,
3680 				  GDK_LINE_ON_OFF_DASH, 0, 0);
3681       gdk_gc_set_dashes (clist->xor_gc, 0, dashes, G_N_ELEMENTS (dashes));
3682     }
3683   else
3684     {
3685       GTK_CLIST_UNSET_FLAG (clist, CLIST_ADD_MODE);
3686       gdk_gc_set_line_attributes (clist->xor_gc, 1, GDK_LINE_SOLID, 0, 0);
3687       clist->anchor_state = GTK_STATE_SELECTED;
3688     }
3689   gtk_clist_draw_focus (GTK_WIDGET (clist));
3690 }
3691 
3692 static void
real_select_row(GtkCList * clist,gint row,gint column,GdkEvent * event)3693 real_select_row (GtkCList *clist,
3694 		 gint      row,
3695 		 gint      column,
3696 		 GdkEvent *event)
3697 {
3698   GtkCListRow *clist_row;
3699   GList *list;
3700   gint sel_row;
3701   gboolean row_selected;
3702 
3703   g_return_if_fail (GTK_IS_CLIST (clist));
3704 
3705   if (row < 0 || row > (clist->rows - 1))
3706     return;
3707 
3708   switch (clist->selection_mode)
3709     {
3710     case GTK_SELECTION_SINGLE:
3711     case GTK_SELECTION_BROWSE:
3712 
3713       row_selected = FALSE;
3714       list = clist->selection;
3715 
3716       while (list)
3717 	{
3718 	  sel_row = GPOINTER_TO_INT (list->data);
3719 	  list = list->next;
3720 
3721 	  if (row == sel_row)
3722 	    row_selected = TRUE;
3723 	  else
3724 	    gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW],
3725 			     sel_row, column, event);
3726 	}
3727 
3728       if (row_selected)
3729 	return;
3730 
3731     default:
3732       break;
3733     }
3734 
3735   clist_row = ROW_ELEMENT (clist, row)->data;
3736 
3737   if (clist_row->state != GTK_STATE_NORMAL || !clist_row->selectable)
3738     return;
3739 
3740   clist_row->state = GTK_STATE_SELECTED;
3741   if (!clist->selection)
3742     {
3743       clist->selection = g_list_append (clist->selection,
3744 					GINT_TO_POINTER (row));
3745       clist->selection_end = clist->selection;
3746     }
3747   else
3748     clist->selection_end =
3749       g_list_append (clist->selection_end, GINT_TO_POINTER (row))->next;
3750 
3751   if (CLIST_UNFROZEN (clist)
3752       && (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE))
3753     GTK_CLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
3754 }
3755 
3756 static void
real_unselect_row(GtkCList * clist,gint row,gint column,GdkEvent * event)3757 real_unselect_row (GtkCList *clist,
3758 		   gint      row,
3759 		   gint      column,
3760 		   GdkEvent *event)
3761 {
3762   GtkCListRow *clist_row;
3763 
3764   g_return_if_fail (GTK_IS_CLIST (clist));
3765 
3766   if (row < 0 || row > (clist->rows - 1))
3767     return;
3768 
3769   clist_row = ROW_ELEMENT (clist, row)->data;
3770 
3771   if (clist_row->state == GTK_STATE_SELECTED)
3772     {
3773       clist_row->state = GTK_STATE_NORMAL;
3774 
3775       if (clist->selection_end &&
3776 	  clist->selection_end->data == GINT_TO_POINTER (row))
3777 	clist->selection_end = clist->selection_end->prev;
3778 
3779       clist->selection = g_list_remove (clist->selection,
3780 					GINT_TO_POINTER (row));
3781 
3782       if (CLIST_UNFROZEN (clist)
3783 	  && (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE))
3784 	GTK_CLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
3785     }
3786 }
3787 
3788 static void
real_select_all(GtkCList * clist)3789 real_select_all (GtkCList *clist)
3790 {
3791   g_return_if_fail (GTK_IS_CLIST (clist));
3792 
3793   if (clist_has_grab (clist))
3794     return;
3795 
3796   switch (clist->selection_mode)
3797     {
3798     case GTK_SELECTION_SINGLE:
3799     case GTK_SELECTION_BROWSE:
3800       return;
3801 
3802     case GTK_SELECTION_MULTIPLE:
3803       g_list_free (clist->undo_selection);
3804       g_list_free (clist->undo_unselection);
3805       clist->undo_selection = NULL;
3806       clist->undo_unselection = NULL;
3807 
3808       if (clist->rows &&
3809 	  ((GtkCListRow *) (clist->row_list->data))->state !=
3810 	  GTK_STATE_SELECTED)
3811 	fake_toggle_row (clist, 0);
3812 
3813       clist->anchor_state =  GTK_STATE_SELECTED;
3814       clist->anchor = 0;
3815       clist->drag_pos = 0;
3816       clist->undo_anchor = clist->focus_row;
3817       update_extended_selection (clist, clist->rows);
3818       GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
3819       return;
3820     default:
3821       g_assert_not_reached ();
3822     }
3823 }
3824 
3825 static void
real_unselect_all(GtkCList * clist)3826 real_unselect_all (GtkCList *clist)
3827 {
3828   GList *list;
3829   gint i;
3830 
3831   g_return_if_fail (GTK_IS_CLIST (clist));
3832 
3833   if (clist_has_grab (clist))
3834     return;
3835 
3836   switch (clist->selection_mode)
3837     {
3838     case GTK_SELECTION_BROWSE:
3839       if (clist->focus_row >= 0)
3840 	{
3841 	  gtk_signal_emit (GTK_OBJECT (clist),
3842 			   clist_signals[SELECT_ROW],
3843 			   clist->focus_row, -1, NULL);
3844 	  return;
3845 	}
3846       break;
3847     case GTK_SELECTION_MULTIPLE:
3848       g_list_free (clist->undo_selection);
3849       g_list_free (clist->undo_unselection);
3850       clist->undo_selection = NULL;
3851       clist->undo_unselection = NULL;
3852 
3853       clist->anchor = -1;
3854       clist->drag_pos = -1;
3855       clist->undo_anchor = clist->focus_row;
3856       break;
3857     default:
3858       break;
3859     }
3860 
3861   list = clist->selection;
3862   while (list)
3863     {
3864       i = GPOINTER_TO_INT (list->data);
3865       list = list->next;
3866       gtk_signal_emit (GTK_OBJECT (clist),
3867 		       clist_signals[UNSELECT_ROW], i, -1, NULL);
3868     }
3869 }
3870 
3871 static void
fake_unselect_all(GtkCList * clist,gint row)3872 fake_unselect_all (GtkCList *clist,
3873 		   gint      row)
3874 {
3875   GList *list;
3876   GList *work;
3877   gint i;
3878 
3879   if (row >= 0 && (work = ROW_ELEMENT (clist, row)))
3880     {
3881       if (GTK_CLIST_ROW (work)->state == GTK_STATE_NORMAL &&
3882 	  GTK_CLIST_ROW (work)->selectable)
3883 	{
3884 	  GTK_CLIST_ROW (work)->state = GTK_STATE_SELECTED;
3885 
3886 	  if (CLIST_UNFROZEN (clist) &&
3887 	      gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
3888 	    GTK_CLIST_GET_CLASS (clist)->draw_row (clist, NULL, row,
3889 						  GTK_CLIST_ROW (work));
3890 	}
3891     }
3892 
3893   clist->undo_selection = clist->selection;
3894   clist->selection = NULL;
3895   clist->selection_end = NULL;
3896 
3897   for (list = clist->undo_selection; list; list = list->next)
3898     {
3899       if ((i = GPOINTER_TO_INT (list->data)) == row ||
3900 	  !(work = g_list_nth (clist->row_list, i)))
3901 	continue;
3902 
3903       GTK_CLIST_ROW (work)->state = GTK_STATE_NORMAL;
3904       if (CLIST_UNFROZEN (clist) &&
3905 	  gtk_clist_row_is_visible (clist, i) != GTK_VISIBILITY_NONE)
3906 	GTK_CLIST_GET_CLASS (clist)->draw_row (clist, NULL, i,
3907 					      GTK_CLIST_ROW (work));
3908     }
3909 }
3910 
3911 static void
real_undo_selection(GtkCList * clist)3912 real_undo_selection (GtkCList *clist)
3913 {
3914   GList *work;
3915 
3916   g_return_if_fail (GTK_IS_CLIST (clist));
3917 
3918   if (clist_has_grab (clist) ||
3919       clist->selection_mode != GTK_SELECTION_MULTIPLE)
3920     return;
3921 
3922   GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
3923 
3924   if (!(clist->undo_selection || clist->undo_unselection))
3925     {
3926       gtk_clist_unselect_all (clist);
3927       return;
3928     }
3929 
3930   for (work = clist->undo_selection; work; work = work->next)
3931     gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
3932 		     GPOINTER_TO_INT (work->data), -1, NULL);
3933 
3934   for (work = clist->undo_unselection; work; work = work->next)
3935     {
3936       /* g_print ("unselect %d\n",GPOINTER_TO_INT (work->data)); */
3937       gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW],
3938 		       GPOINTER_TO_INT (work->data), -1, NULL);
3939     }
3940 
3941   if (gtk_widget_has_focus (GTK_WIDGET (clist)) && clist->focus_row != clist->undo_anchor)
3942     {
3943       gtk_clist_draw_focus (GTK_WIDGET (clist));
3944       clist->focus_row = clist->undo_anchor;
3945       gtk_clist_draw_focus (GTK_WIDGET (clist));
3946     }
3947   else
3948     clist->focus_row = clist->undo_anchor;
3949 
3950   clist->undo_anchor = -1;
3951 
3952   g_list_free (clist->undo_selection);
3953   g_list_free (clist->undo_unselection);
3954   clist->undo_selection = NULL;
3955   clist->undo_unselection = NULL;
3956 
3957   if (ROW_TOP_YPIXEL (clist, clist->focus_row) + clist->row_height >
3958       clist->clist_window_height)
3959     gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
3960   else if (ROW_TOP_YPIXEL (clist, clist->focus_row) < 0)
3961     gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
3962 }
3963 
3964 static void
set_anchor(GtkCList * clist,gboolean add_mode,gint anchor,gint undo_anchor)3965 set_anchor (GtkCList *clist,
3966 	    gboolean  add_mode,
3967 	    gint      anchor,
3968 	    gint      undo_anchor)
3969 {
3970   g_return_if_fail (GTK_IS_CLIST (clist));
3971 
3972   if (clist->selection_mode != GTK_SELECTION_MULTIPLE || clist->anchor >= 0)
3973     return;
3974 
3975   g_list_free (clist->undo_selection);
3976   g_list_free (clist->undo_unselection);
3977   clist->undo_selection = NULL;
3978   clist->undo_unselection = NULL;
3979 
3980   if (add_mode)
3981     fake_toggle_row (clist, anchor);
3982   else
3983     {
3984       GTK_CLIST_GET_CLASS (clist)->fake_unselect_all (clist, anchor);
3985       clist->anchor_state = GTK_STATE_SELECTED;
3986     }
3987 
3988   clist->anchor = anchor;
3989   clist->drag_pos = anchor;
3990   clist->undo_anchor = undo_anchor;
3991 }
3992 
3993 static void
resync_selection(GtkCList * clist,GdkEvent * event)3994 resync_selection (GtkCList *clist,
3995 		  GdkEvent *event)
3996 {
3997   gint i;
3998   gint e;
3999   gint row;
4000   GList *list;
4001   GtkCListRow *clist_row;
4002 
4003   if (clist->selection_mode != GTK_SELECTION_MULTIPLE)
4004     return;
4005 
4006   if (clist->anchor < 0 || clist->drag_pos < 0)
4007     return;
4008 
4009   gtk_clist_freeze (clist);
4010 
4011   i = MIN (clist->anchor, clist->drag_pos);
4012   e = MAX (clist->anchor, clist->drag_pos);
4013 
4014   if (clist->undo_selection)
4015     {
4016       list = clist->selection;
4017       clist->selection = clist->undo_selection;
4018       clist->selection_end = g_list_last (clist->selection);
4019       clist->undo_selection = list;
4020       list = clist->selection;
4021       while (list)
4022 	{
4023 	  row = GPOINTER_TO_INT (list->data);
4024 	  list = list->next;
4025 	  if (row < i || row > e)
4026 	    {
4027 	      clist_row = g_list_nth (clist->row_list, row)->data;
4028 	      if (clist_row->selectable)
4029 		{
4030 		  clist_row->state = GTK_STATE_SELECTED;
4031 		  gtk_signal_emit (GTK_OBJECT (clist),
4032 				   clist_signals[UNSELECT_ROW],
4033 				   row, -1, event);
4034 		  clist->undo_selection = g_list_prepend
4035 		    (clist->undo_selection, GINT_TO_POINTER (row));
4036 		}
4037 	    }
4038 	}
4039     }
4040 
4041   if (clist->anchor < clist->drag_pos)
4042     {
4043       for (list = g_list_nth (clist->row_list, i); i <= e;
4044 	   i++, list = list->next)
4045 	if (GTK_CLIST_ROW (list)->selectable)
4046 	  {
4047 	    if (g_list_find (clist->selection, GINT_TO_POINTER(i)))
4048 	      {
4049 		if (GTK_CLIST_ROW (list)->state == GTK_STATE_NORMAL)
4050 		  {
4051 		    GTK_CLIST_ROW (list)->state = GTK_STATE_SELECTED;
4052 		    gtk_signal_emit (GTK_OBJECT (clist),
4053 				     clist_signals[UNSELECT_ROW],
4054 				     i, -1, event);
4055 		    clist->undo_selection =
4056 		      g_list_prepend (clist->undo_selection,
4057 				      GINT_TO_POINTER (i));
4058 		  }
4059 	      }
4060 	    else if (GTK_CLIST_ROW (list)->state == GTK_STATE_SELECTED)
4061 	      {
4062 		GTK_CLIST_ROW (list)->state = GTK_STATE_NORMAL;
4063 		clist->undo_unselection =
4064 		  g_list_prepend (clist->undo_unselection,
4065 				  GINT_TO_POINTER (i));
4066 	      }
4067 	  }
4068     }
4069   else
4070     {
4071       for (list = g_list_nth (clist->row_list, e); i <= e;
4072 	   e--, list = list->prev)
4073 	if (GTK_CLIST_ROW (list)->selectable)
4074 	  {
4075 	    if (g_list_find (clist->selection, GINT_TO_POINTER(e)))
4076 	      {
4077 		if (GTK_CLIST_ROW (list)->state == GTK_STATE_NORMAL)
4078 		  {
4079 		    GTK_CLIST_ROW (list)->state = GTK_STATE_SELECTED;
4080 		    gtk_signal_emit (GTK_OBJECT (clist),
4081 				     clist_signals[UNSELECT_ROW],
4082 				     e, -1, event);
4083 		    clist->undo_selection =
4084 		      g_list_prepend (clist->undo_selection,
4085 				      GINT_TO_POINTER (e));
4086 		  }
4087 	      }
4088 	    else if (GTK_CLIST_ROW (list)->state == GTK_STATE_SELECTED)
4089 	      {
4090 		GTK_CLIST_ROW (list)->state = GTK_STATE_NORMAL;
4091 		clist->undo_unselection =
4092 		  g_list_prepend (clist->undo_unselection,
4093 				  GINT_TO_POINTER (e));
4094 	      }
4095 	  }
4096     }
4097 
4098   clist->undo_unselection = g_list_reverse (clist->undo_unselection);
4099   for (list = clist->undo_unselection; list; list = list->next)
4100     gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
4101 		     GPOINTER_TO_INT (list->data), -1, event);
4102 
4103   clist->anchor = -1;
4104   clist->drag_pos = -1;
4105 
4106   gtk_clist_thaw (clist);
4107 }
4108 
4109 static void
update_extended_selection(GtkCList * clist,gint row)4110 update_extended_selection (GtkCList *clist,
4111 			   gint      row)
4112 {
4113   gint i;
4114   GList *list;
4115   GdkRectangle area;
4116   gint s1 = -1;
4117   gint s2 = -1;
4118   gint e1 = -1;
4119   gint e2 = -1;
4120   gint y1 = clist->clist_window_height;
4121   gint y2 = clist->clist_window_height;
4122   gint h1 = 0;
4123   gint h2 = 0;
4124   gint top;
4125 
4126   if (clist->selection_mode != GTK_SELECTION_MULTIPLE || clist->anchor == -1)
4127     return;
4128 
4129   if (row < 0)
4130     row = 0;
4131   if (row >= clist->rows)
4132     row = clist->rows - 1;
4133 
4134   /* extending downwards */
4135   if (row > clist->drag_pos && clist->anchor <= clist->drag_pos)
4136     {
4137       s2 = clist->drag_pos + 1;
4138       e2 = row;
4139     }
4140   /* extending upwards */
4141   else if (row < clist->drag_pos && clist->anchor >= clist->drag_pos)
4142     {
4143       s2 = row;
4144       e2 = clist->drag_pos - 1;
4145     }
4146   else if (row < clist->drag_pos && clist->anchor < clist->drag_pos)
4147     {
4148       e1 = clist->drag_pos;
4149       /* row and drag_pos on different sides of anchor :
4150 	 take back the selection between anchor and drag_pos,
4151          select between anchor and row */
4152       if (row < clist->anchor)
4153 	{
4154 	  s1 = clist->anchor + 1;
4155 	  s2 = row;
4156 	  e2 = clist->anchor - 1;
4157 	}
4158       /* take back the selection between anchor and drag_pos */
4159       else
4160 	s1 = row + 1;
4161     }
4162   else if (row > clist->drag_pos && clist->anchor > clist->drag_pos)
4163     {
4164       s1 = clist->drag_pos;
4165       /* row and drag_pos on different sides of anchor :
4166 	 take back the selection between anchor and drag_pos,
4167          select between anchor and row */
4168       if (row > clist->anchor)
4169 	{
4170 	  e1 = clist->anchor - 1;
4171 	  s2 = clist->anchor + 1;
4172 	  e2 = row;
4173 	}
4174       /* take back the selection between anchor and drag_pos */
4175       else
4176 	e1 = row - 1;
4177     }
4178 
4179   clist->drag_pos = row;
4180 
4181   area.x = 0;
4182   area.width = clist->clist_window_width;
4183 
4184   /* restore the elements between s1 and e1 */
4185   if (s1 >= 0)
4186     {
4187       for (i = s1, list = g_list_nth (clist->row_list, i); i <= e1;
4188 	   i++, list = list->next)
4189 	if (GTK_CLIST_ROW (list)->selectable)
4190 	  {
4191 	    if (GTK_CLIST_GET_CLASS (clist)->selection_find (clist, i, list))
4192 	      GTK_CLIST_ROW (list)->state = GTK_STATE_SELECTED;
4193 	    else
4194 	      GTK_CLIST_ROW (list)->state = GTK_STATE_NORMAL;
4195 	  }
4196 
4197       top = ROW_TOP_YPIXEL (clist, clist->focus_row);
4198 
4199       if (top + clist->row_height <= 0)
4200 	{
4201 	  area.y = 0;
4202 	  area.height = ROW_TOP_YPIXEL (clist, e1) + clist->row_height;
4203 	  draw_rows (clist, &area);
4204 	  gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
4205 	}
4206       else if (top >= clist->clist_window_height)
4207 	{
4208 	  area.y = ROW_TOP_YPIXEL (clist, s1) - 1;
4209 	  area.height = clist->clist_window_height - area.y;
4210 	  draw_rows (clist, &area);
4211 	  gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
4212 	}
4213       else if (top < 0)
4214 	gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
4215       else if (top + clist->row_height > clist->clist_window_height)
4216 	gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
4217 
4218       y1 = ROW_TOP_YPIXEL (clist, s1) - 1;
4219       h1 = (e1 - s1 + 1) * (clist->row_height + CELL_SPACING);
4220     }
4221 
4222   /* extend the selection between s2 and e2 */
4223   if (s2 >= 0)
4224     {
4225       for (i = s2, list = g_list_nth (clist->row_list, i); i <= e2;
4226 	   i++, list = list->next)
4227 	if (GTK_CLIST_ROW (list)->selectable &&
4228 	    GTK_CLIST_ROW (list)->state != clist->anchor_state)
4229 	  GTK_CLIST_ROW (list)->state = clist->anchor_state;
4230 
4231       top = ROW_TOP_YPIXEL (clist, clist->focus_row);
4232 
4233       if (top + clist->row_height <= 0)
4234 	{
4235 	  area.y = 0;
4236 	  area.height = ROW_TOP_YPIXEL (clist, e2) + clist->row_height;
4237 	  draw_rows (clist, &area);
4238 	  gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
4239 	}
4240       else if (top >= clist->clist_window_height)
4241 	{
4242 	  area.y = ROW_TOP_YPIXEL (clist, s2) - 1;
4243 	  area.height = clist->clist_window_height - area.y;
4244 	  draw_rows (clist, &area);
4245 	  gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
4246 	}
4247       else if (top < 0)
4248 	gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
4249       else if (top + clist->row_height > clist->clist_window_height)
4250 	gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
4251 
4252       y2 = ROW_TOP_YPIXEL (clist, s2) - 1;
4253       h2 = (e2 - s2 + 1) * (clist->row_height + CELL_SPACING);
4254     }
4255 
4256   area.y = MAX (0, MIN (y1, y2));
4257   if (area.y > clist->clist_window_height)
4258     area.y = 0;
4259   area.height = MIN (clist->clist_window_height, h1 + h2);
4260   if (s1 >= 0 && s2 >= 0)
4261     area.height += (clist->row_height + CELL_SPACING);
4262   draw_rows (clist, &area);
4263 }
4264 
4265 static void
start_selection(GtkCList * clist)4266 start_selection (GtkCList *clist)
4267 {
4268   g_return_if_fail (GTK_IS_CLIST (clist));
4269 
4270   if (clist_has_grab (clist))
4271     return;
4272 
4273   set_anchor (clist, GTK_CLIST_ADD_MODE(clist), clist->focus_row,
4274 	      clist->focus_row);
4275 }
4276 
4277 static void
end_selection(GtkCList * clist)4278 end_selection (GtkCList *clist)
4279 {
4280   g_return_if_fail (GTK_IS_CLIST (clist));
4281 
4282   if (gdk_display_pointer_is_grabbed (gtk_widget_get_display (GTK_WIDGET (clist))) &&
4283       gtk_widget_has_focus (GTK_WIDGET (clist)))
4284     return;
4285 
4286   GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
4287 }
4288 
4289 static void
extend_selection(GtkCList * clist,GtkScrollType scroll_type,gfloat position,gboolean auto_start_selection)4290 extend_selection (GtkCList      *clist,
4291 		  GtkScrollType  scroll_type,
4292 		  gfloat         position,
4293 		  gboolean       auto_start_selection)
4294 {
4295   g_return_if_fail (GTK_IS_CLIST (clist));
4296 
4297   if (clist_has_grab (clist) ||
4298       clist->selection_mode != GTK_SELECTION_MULTIPLE)
4299     return;
4300 
4301   if (auto_start_selection)
4302     set_anchor (clist, GTK_CLIST_ADD_MODE(clist), clist->focus_row,
4303 		clist->focus_row);
4304   else if (clist->anchor == -1)
4305     return;
4306 
4307   move_focus_row (clist, scroll_type, position);
4308 
4309   if (ROW_TOP_YPIXEL (clist, clist->focus_row) + clist->row_height >
4310       clist->clist_window_height)
4311     gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
4312   else if (ROW_TOP_YPIXEL (clist, clist->focus_row) < 0)
4313     gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
4314 
4315   update_extended_selection (clist, clist->focus_row);
4316 }
4317 
4318 static void
sync_selection(GtkCList * clist,gint row,gint mode)4319 sync_selection (GtkCList *clist,
4320 		gint      row,
4321 		gint      mode)
4322 {
4323   GList *list;
4324   gint d;
4325 
4326   if (mode == SYNC_INSERT)
4327     d = 1;
4328   else
4329     d = -1;
4330 
4331   if (clist->focus_row >= row)
4332     {
4333       if (d > 0 || clist->focus_row > row)
4334 	clist->focus_row += d;
4335       if (clist->focus_row == -1 && clist->rows >= 1)
4336 	clist->focus_row = 0;
4337       else if (d < 0 && clist->focus_row >= clist->rows - 1)
4338 	clist->focus_row = clist->rows - 2;
4339       else if (clist->focus_row >= clist->rows)	/* Paranoia */
4340 	clist->focus_row = clist->rows - 1;
4341     }
4342 
4343   GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
4344 
4345   g_list_free (clist->undo_selection);
4346   g_list_free (clist->undo_unselection);
4347   clist->undo_selection = NULL;
4348   clist->undo_unselection = NULL;
4349 
4350   clist->anchor = -1;
4351   clist->drag_pos = -1;
4352   clist->undo_anchor = clist->focus_row;
4353 
4354   list = clist->selection;
4355 
4356   while (list)
4357     {
4358       if (GPOINTER_TO_INT (list->data) >= row)
4359 	list->data = ((gchar*) list->data) + d;
4360       list = list->next;
4361     }
4362 }
4363 
4364 /* GTKOBJECT
4365  *   gtk_clist_destroy
4366  *   gtk_clist_finalize
4367  */
4368 static void
gtk_clist_destroy(GtkObject * object)4369 gtk_clist_destroy (GtkObject *object)
4370 {
4371   gint i;
4372   GtkCList *clist;
4373 
4374   g_return_if_fail (GTK_IS_CLIST (object));
4375 
4376   clist = GTK_CLIST (object);
4377 
4378   /* freeze the list */
4379   clist->freeze_count++;
4380 
4381   /* get rid of all the rows */
4382   gtk_clist_clear (clist);
4383 
4384   /* Since we don't have a _remove method, unparent the children
4385    * instead of destroying them so the focus will be unset properly.
4386    * (For other containers, the _remove method takes care of the
4387    * unparent) The destroy will happen when the refcount drops
4388    * to zero.
4389    */
4390 
4391   /* unref adjustments */
4392   if (clist->hadjustment)
4393     {
4394       gtk_signal_disconnect_by_data (GTK_OBJECT (clist->hadjustment), clist);
4395       g_object_unref (clist->hadjustment);
4396       clist->hadjustment = NULL;
4397     }
4398   if (clist->vadjustment)
4399     {
4400       gtk_signal_disconnect_by_data (GTK_OBJECT (clist->vadjustment), clist);
4401       g_object_unref (clist->vadjustment);
4402       clist->vadjustment = NULL;
4403     }
4404 
4405   remove_grab (clist);
4406 
4407   /* destroy the column buttons */
4408   for (i = 0; i < clist->columns; i++)
4409     if (clist->column[i].button)
4410       {
4411 	gtk_widget_unparent (clist->column[i].button);
4412 	clist->column[i].button = NULL;
4413       }
4414 
4415   GTK_OBJECT_CLASS (parent_class)->destroy (object);
4416 }
4417 
4418 static void
gtk_clist_finalize(GObject * object)4419 gtk_clist_finalize (GObject *object)
4420 {
4421   GtkCList *clist;
4422 
4423   g_return_if_fail (GTK_IS_CLIST (object));
4424 
4425   clist = GTK_CLIST (object);
4426 
4427   columns_delete (clist);
4428 
4429   G_OBJECT_CLASS (parent_class)->finalize (object);
4430 }
4431 
4432 /* GTKWIDGET
4433  *   gtk_clist_realize
4434  *   gtk_clist_unrealize
4435  *   gtk_clist_map
4436  *   gtk_clist_unmap
4437  *   gtk_clist_expose
4438  *   gtk_clist_style_set
4439  *   gtk_clist_button_press
4440  *   gtk_clist_button_release
4441  *   gtk_clist_motion
4442  *   gtk_clist_size_request
4443  *   gtk_clist_size_allocate
4444  */
4445 static void
gtk_clist_realize(GtkWidget * widget)4446 gtk_clist_realize (GtkWidget *widget)
4447 {
4448   GtkCList *clist;
4449   GdkWindowAttr attributes;
4450   GdkGCValues values;
4451   GtkCListRow *clist_row;
4452   GList *list;
4453   gint attributes_mask;
4454   gint border_width;
4455   gint i;
4456   gint j;
4457 
4458   g_return_if_fail (GTK_IS_CLIST (widget));
4459 
4460   clist = GTK_CLIST (widget);
4461 
4462   gtk_widget_set_realized (widget, TRUE);
4463 
4464   border_width = GTK_CONTAINER (widget)->border_width;
4465 
4466   attributes.window_type = GDK_WINDOW_CHILD;
4467   attributes.x = widget->allocation.x + border_width;
4468   attributes.y = widget->allocation.y + border_width;
4469   attributes.width = widget->allocation.width - border_width * 2;
4470   attributes.height = widget->allocation.height - border_width * 2;
4471   attributes.wclass = GDK_INPUT_OUTPUT;
4472   attributes.visual = gtk_widget_get_visual (widget);
4473   attributes.colormap = gtk_widget_get_colormap (widget);
4474   attributes.event_mask = gtk_widget_get_events (widget);
4475   attributes.event_mask |= (GDK_EXPOSURE_MASK |
4476 			    GDK_BUTTON_PRESS_MASK |
4477 			    GDK_BUTTON_RELEASE_MASK |
4478 			    GDK_KEY_RELEASE_MASK);
4479   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
4480 
4481   /* main window */
4482   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
4483 				   &attributes, attributes_mask);
4484   gdk_window_set_user_data (widget->window, clist);
4485 
4486   widget->style = gtk_style_attach (widget->style, widget->window);
4487 
4488   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
4489 
4490   /* column-title window */
4491 
4492   attributes.x = clist->column_title_area.x;
4493   attributes.y = clist->column_title_area.y;
4494   attributes.width = clist->column_title_area.width;
4495   attributes.height = clist->column_title_area.height;
4496 
4497   clist->title_window = gdk_window_new (widget->window, &attributes,
4498 					attributes_mask);
4499   gdk_window_set_user_data (clist->title_window, clist);
4500 
4501   gtk_style_set_background (widget->style, clist->title_window,
4502 			    GTK_STATE_NORMAL);
4503   gdk_window_show (clist->title_window);
4504 
4505   /* set things up so column buttons are drawn in title window */
4506   for (i = 0; i < clist->columns; i++)
4507     if (clist->column[i].button)
4508       gtk_widget_set_parent_window (clist->column[i].button,
4509 				    clist->title_window);
4510 
4511   /* clist-window */
4512   attributes.x = (clist->internal_allocation.x +
4513 		  widget->style->xthickness);
4514   attributes.y = (clist->internal_allocation.y +
4515 		  widget->style->ythickness +
4516 		  clist->column_title_area.height);
4517   attributes.width = clist->clist_window_width;
4518   attributes.height = clist->clist_window_height;
4519 
4520   clist->clist_window = gdk_window_new (widget->window, &attributes,
4521 					attributes_mask);
4522   gdk_window_set_user_data (clist->clist_window, clist);
4523 
4524   gdk_window_set_background (clist->clist_window,
4525 			     &widget->style->base[GTK_STATE_NORMAL]);
4526   gdk_window_show (clist->clist_window);
4527   gdk_drawable_get_size (clist->clist_window, &clist->clist_window_width,
4528                          &clist->clist_window_height);
4529 
4530   /* create resize windows */
4531   attributes.wclass = GDK_INPUT_ONLY;
4532   attributes.event_mask = (GDK_BUTTON_PRESS_MASK |
4533 			   GDK_BUTTON_RELEASE_MASK |
4534 			   GDK_POINTER_MOTION_MASK |
4535 			   GDK_POINTER_MOTION_HINT_MASK);
4536   attributes_mask = GDK_WA_CURSOR;
4537   attributes.cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
4538 						  GDK_SB_H_DOUBLE_ARROW);
4539   clist->cursor_drag = attributes.cursor;
4540 
4541   attributes.x =  LIST_WIDTH (clist) + 1;
4542   attributes.y = 0;
4543   attributes.width = 0;
4544   attributes.height = 0;
4545 
4546   for (i = 0; i < clist->columns; i++)
4547     {
4548       clist->column[i].window = gdk_window_new (clist->title_window,
4549 						&attributes, attributes_mask);
4550       gdk_window_set_user_data (clist->column[i].window, clist);
4551     }
4552 
4553   /* This is slightly less efficient than creating them with the
4554    * right size to begin with, but easier
4555    */
4556   size_allocate_title_buttons (clist);
4557 
4558   /* GCs */
4559   clist->fg_gc = gdk_gc_new (widget->window);
4560   clist->bg_gc = gdk_gc_new (widget->window);
4561 
4562   /* We'll use this gc to do scrolling as well */
4563   gdk_gc_set_exposures (clist->fg_gc, TRUE);
4564 
4565   values.foreground = (widget->style->white.pixel==0 ?
4566 		       widget->style->black:widget->style->white);
4567   values.function = GDK_XOR;
4568   values.subwindow_mode = GDK_INCLUDE_INFERIORS;
4569   clist->xor_gc = gdk_gc_new_with_values (widget->window,
4570 					  &values,
4571 					  GDK_GC_FOREGROUND |
4572 					  GDK_GC_FUNCTION |
4573 					  GDK_GC_SUBWINDOW);
4574 
4575   /* attach optional row/cell styles, allocate foreground/background colors */
4576   list = clist->row_list;
4577   for (i = 0; i < clist->rows; i++)
4578     {
4579       clist_row = list->data;
4580       list = list->next;
4581 
4582       if (clist_row->style)
4583 	clist_row->style = gtk_style_attach (clist_row->style,
4584 					     clist->clist_window);
4585 
4586       if (clist_row->fg_set || clist_row->bg_set)
4587 	{
4588 	  GdkColormap *colormap;
4589 
4590 	  colormap = gtk_widget_get_colormap (widget);
4591 	  if (clist_row->fg_set)
4592 	    gdk_colormap_alloc_color (colormap, &clist_row->foreground,
4593                                       FALSE, TRUE);
4594 	  if (clist_row->bg_set)
4595 	    gdk_colormap_alloc_color (colormap, &clist_row->background,
4596                                       FALSE, TRUE);
4597 	}
4598 
4599       for (j = 0; j < clist->columns; j++)
4600 	if  (clist_row->cell[j].style)
4601 	  clist_row->cell[j].style =
4602 	    gtk_style_attach (clist_row->cell[j].style, clist->clist_window);
4603     }
4604 }
4605 
4606 static void
gtk_clist_unrealize(GtkWidget * widget)4607 gtk_clist_unrealize (GtkWidget *widget)
4608 {
4609   gint i;
4610   GtkCList *clist;
4611 
4612   g_return_if_fail (GTK_IS_CLIST (widget));
4613 
4614   clist = GTK_CLIST (widget);
4615 
4616   /* freeze the list */
4617   clist->freeze_count++;
4618 
4619   if (gtk_widget_get_mapped (widget))
4620     gtk_clist_unmap (widget);
4621 
4622   gtk_widget_set_mapped (widget, FALSE);
4623 
4624   /* detach optional row/cell styles */
4625   if (gtk_widget_get_realized (widget))
4626     {
4627       GtkCListRow *clist_row;
4628       GList *list;
4629       gint j;
4630 
4631       list = clist->row_list;
4632       for (i = 0; i < clist->rows; i++)
4633 	{
4634 	  clist_row = list->data;
4635 	  list = list->next;
4636 
4637 	  if (clist_row->style)
4638 	    gtk_style_detach (clist_row->style);
4639 	  for (j = 0; j < clist->columns; j++)
4640 	    if  (clist_row->cell[j].style)
4641 	      gtk_style_detach (clist_row->cell[j].style);
4642 	}
4643     }
4644 
4645   gdk_cursor_unref (clist->cursor_drag);
4646   g_object_unref (clist->xor_gc);
4647   g_object_unref (clist->fg_gc);
4648   g_object_unref (clist->bg_gc);
4649 
4650   for (i = 0; i < clist->columns; i++)
4651     {
4652       if (clist->column[i].button)
4653 	gtk_widget_unrealize (clist->column[i].button);
4654       if (clist->column[i].window)
4655 	{
4656 	  gdk_window_set_user_data (clist->column[i].window, NULL);
4657 	  gdk_window_destroy (clist->column[i].window);
4658 	  clist->column[i].window = NULL;
4659 	}
4660     }
4661 
4662   gdk_window_set_user_data (clist->clist_window, NULL);
4663   gdk_window_destroy (clist->clist_window);
4664   clist->clist_window = NULL;
4665 
4666   gdk_window_set_user_data (clist->title_window, NULL);
4667   gdk_window_destroy (clist->title_window);
4668   clist->title_window = NULL;
4669 
4670   clist->cursor_drag = NULL;
4671   clist->xor_gc = NULL;
4672   clist->fg_gc = NULL;
4673   clist->bg_gc = NULL;
4674 
4675   GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
4676 }
4677 
4678 static void
gtk_clist_map(GtkWidget * widget)4679 gtk_clist_map (GtkWidget *widget)
4680 {
4681   gint i;
4682   GtkCList *clist;
4683 
4684   g_return_if_fail (GTK_IS_CLIST (widget));
4685 
4686   clist = GTK_CLIST (widget);
4687 
4688   if (!gtk_widget_get_mapped (widget))
4689     {
4690       gtk_widget_set_mapped (widget, TRUE);
4691 
4692       /* map column buttons */
4693       for (i = 0; i < clist->columns; i++)
4694 	{
4695 	  if (clist->column[i].button &&
4696 	      gtk_widget_get_visible (clist->column[i].button) &&
4697 	      !gtk_widget_get_mapped (clist->column[i].button))
4698 	    gtk_widget_map (clist->column[i].button);
4699 	}
4700 
4701       for (i = 0; i < clist->columns; i++)
4702 	if (clist->column[i].window && clist->column[i].button)
4703 	  {
4704 	    gdk_window_raise (clist->column[i].window);
4705 	    gdk_window_show (clist->column[i].window);
4706 	  }
4707 
4708       gdk_window_show (clist->title_window);
4709       gdk_window_show (clist->clist_window);
4710       gdk_window_show (widget->window);
4711 
4712       /* unfreeze the list */
4713       clist->freeze_count = 0;
4714     }
4715 }
4716 
4717 static void
gtk_clist_unmap(GtkWidget * widget)4718 gtk_clist_unmap (GtkWidget *widget)
4719 {
4720   gint i;
4721   GtkCList *clist;
4722 
4723   g_return_if_fail (GTK_IS_CLIST (widget));
4724 
4725   clist = GTK_CLIST (widget);
4726 
4727   if (gtk_widget_get_mapped (widget))
4728     {
4729       gtk_widget_set_mapped (widget, FALSE);
4730 
4731       if (clist_has_grab (clist))
4732 	{
4733 	  remove_grab (clist);
4734 
4735 	  GTK_CLIST_GET_CLASS (widget)->resync_selection (clist, NULL);
4736 
4737 	  clist->click_cell.row = -1;
4738 	  clist->click_cell.column = -1;
4739 	  clist->drag_button = 0;
4740 
4741 	  if (GTK_CLIST_IN_DRAG(clist))
4742 	    {
4743 	      gpointer drag_data;
4744 
4745 	      GTK_CLIST_UNSET_FLAG (clist, CLIST_IN_DRAG);
4746 	      drag_data = gtk_object_get_data (GTK_OBJECT (clist),
4747 					       "gtk-site-data");
4748 	      if (drag_data)
4749 		gtk_signal_handler_unblock_by_data (GTK_OBJECT (clist),
4750 						    drag_data);
4751 	    }
4752 	}
4753 
4754       for (i = 0; i < clist->columns; i++)
4755 	if (clist->column[i].window)
4756 	  gdk_window_hide (clist->column[i].window);
4757 
4758       gdk_window_hide (clist->clist_window);
4759       gdk_window_hide (clist->title_window);
4760       gdk_window_hide (widget->window);
4761 
4762       /* unmap column buttons */
4763       for (i = 0; i < clist->columns; i++)
4764 	if (clist->column[i].button &&
4765 	    gtk_widget_get_mapped (clist->column[i].button))
4766 	  gtk_widget_unmap (clist->column[i].button);
4767 
4768       /* freeze the list */
4769       clist->freeze_count++;
4770     }
4771 }
4772 
4773 static gint
gtk_clist_expose(GtkWidget * widget,GdkEventExpose * event)4774 gtk_clist_expose (GtkWidget      *widget,
4775 		  GdkEventExpose *event)
4776 {
4777   GtkCList *clist;
4778 
4779   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
4780   g_return_val_if_fail (event != NULL, FALSE);
4781 
4782   if (GTK_WIDGET_DRAWABLE (widget))
4783     {
4784       clist = GTK_CLIST (widget);
4785 
4786       /* draw border */
4787       if (event->window == widget->window)
4788 	gtk_draw_shadow (widget->style, widget->window,
4789 			 GTK_STATE_NORMAL, clist->shadow_type,
4790 			 0, 0,
4791 			 clist->clist_window_width +
4792 			 (2 * widget->style->xthickness),
4793 			 clist->clist_window_height +
4794 			 (2 * widget->style->ythickness) +
4795 			 clist->column_title_area.height);
4796 
4797       /* exposure events on the list */
4798       if (event->window == clist->clist_window)
4799 	draw_rows (clist, &event->area);
4800 
4801       if (event->window == clist->clist_window &&
4802 	  clist->drag_highlight_row >= 0)
4803 	GTK_CLIST_GET_CLASS (clist)->draw_drag_highlight
4804 	  (clist, g_list_nth (clist->row_list,
4805 			      clist->drag_highlight_row)->data,
4806 	   clist->drag_highlight_row, clist->drag_highlight_pos);
4807 
4808       if (event->window == clist->title_window)
4809 	{
4810 	  gint i;
4811 
4812 	  for (i = 0; i < clist->columns; i++)
4813 	    {
4814 	      if (clist->column[i].button)
4815 		gtk_container_propagate_expose (GTK_CONTAINER (clist),
4816 						clist->column[i].button,
4817 						event);
4818 	    }
4819 	}
4820     }
4821 
4822   return FALSE;
4823 }
4824 
4825 static void
gtk_clist_style_set(GtkWidget * widget,GtkStyle * previous_style)4826 gtk_clist_style_set (GtkWidget *widget,
4827 		     GtkStyle  *previous_style)
4828 {
4829   GtkCList *clist = GTK_CLIST (widget);
4830 
4831   GTK_WIDGET_CLASS (parent_class)->style_set (widget, previous_style);
4832 
4833   if (gtk_widget_get_realized (widget))
4834     {
4835       gtk_style_set_background (widget->style, widget->window, widget->state);
4836       gtk_style_set_background (widget->style, clist->title_window, GTK_STATE_SELECTED);
4837       gdk_window_set_background (clist->clist_window, &widget->style->base[GTK_STATE_NORMAL]);
4838     }
4839 
4840   /* Fill in data after widget has correct style */
4841 
4842   /* text properties */
4843   if (!GTK_CLIST_ROW_HEIGHT_SET(clist))
4844     /* Reset clist->row_height */
4845     gtk_clist_set_row_height (clist, 0);
4846 
4847   /* Column widths */
4848   if (!GTK_CLIST_AUTO_RESIZE_BLOCKED (clist))
4849     {
4850       gint width;
4851       gint i;
4852 
4853       for (i = 0; i < clist->columns; i++)
4854 	if (clist->column[i].auto_resize)
4855 	  {
4856 	    width = gtk_clist_optimal_column_width (clist, i);
4857 	    if (width != clist->column[i].width)
4858 	      gtk_clist_set_column_width (clist, i, width);
4859 	  }
4860     }
4861 }
4862 
4863 static gint
gtk_clist_button_press(GtkWidget * widget,GdkEventButton * event)4864 gtk_clist_button_press (GtkWidget      *widget,
4865 			GdkEventButton *event)
4866 {
4867   gint i;
4868   GtkCList *clist;
4869   gint x;
4870   gint y;
4871   gint row;
4872   gint column;
4873   gint button_actions;
4874 
4875   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
4876   g_return_val_if_fail (event != NULL, FALSE);
4877 
4878   clist = GTK_CLIST (widget);
4879 
4880   button_actions = clist->button_actions[event->button - 1];
4881 
4882   if (button_actions == GTK_BUTTON_IGNORED)
4883     return FALSE;
4884 
4885   /* selections on the list */
4886   if (event->window == clist->clist_window)
4887     {
4888       x = event->x;
4889       y = event->y;
4890 
4891       if (get_selection_info (clist, x, y, &row, &column))
4892 	{
4893 	  gint old_row = clist->focus_row;
4894 
4895 	  if (clist->focus_row == -1)
4896 	    old_row = row;
4897 
4898 	  if (event->type == GDK_BUTTON_PRESS)
4899 	    {
4900 	      GdkEventMask mask = ((1 << (4 + event->button)) |
4901 				   GDK_POINTER_MOTION_HINT_MASK |
4902 				   GDK_BUTTON_RELEASE_MASK);
4903 
4904 	      if (gdk_pointer_grab (clist->clist_window, FALSE, mask,
4905 				    NULL, NULL, event->time))
4906 		return FALSE;
4907 	      gtk_grab_add (widget);
4908 
4909 	      clist->click_cell.row = row;
4910 	      clist->click_cell.column = column;
4911 	      clist->drag_button = event->button;
4912 	    }
4913 	  else
4914 	    {
4915 	      clist->click_cell.row = -1;
4916 	      clist->click_cell.column = -1;
4917 
4918 	      clist->drag_button = 0;
4919 	      remove_grab (clist);
4920 	    }
4921 
4922 	  if (button_actions & GTK_BUTTON_SELECTS)
4923 	    {
4924 	      if (GTK_CLIST_ADD_MODE(clist))
4925 		{
4926 		  GTK_CLIST_UNSET_FLAG (clist, CLIST_ADD_MODE);
4927 		  if (gtk_widget_has_focus (widget))
4928 		    {
4929 		      gtk_clist_draw_focus (widget);
4930 		      gdk_gc_set_line_attributes (clist->xor_gc, 1,
4931 						  GDK_LINE_SOLID, 0, 0);
4932 		      clist->focus_row = row;
4933 		      gtk_clist_draw_focus (widget);
4934 		    }
4935 		  else
4936 		    {
4937 		      gdk_gc_set_line_attributes (clist->xor_gc, 1,
4938 						  GDK_LINE_SOLID, 0, 0);
4939 		      clist->focus_row = row;
4940 		    }
4941 		}
4942 	      else if (row != clist->focus_row)
4943 		{
4944 		  if (gtk_widget_has_focus (widget))
4945 		    {
4946 		      gtk_clist_draw_focus (widget);
4947 		      clist->focus_row = row;
4948 		      gtk_clist_draw_focus (widget);
4949 		    }
4950 		  else
4951 		    clist->focus_row = row;
4952 		}
4953 	    }
4954 
4955 	  if (!gtk_widget_has_focus (widget))
4956 	    gtk_widget_grab_focus (widget);
4957 
4958 	  if (button_actions & GTK_BUTTON_SELECTS)
4959 	    {
4960 	      switch (clist->selection_mode)
4961 		{
4962 		case GTK_SELECTION_SINGLE:
4963 		  if (event->type != GDK_BUTTON_PRESS)
4964 		    {
4965 		      gtk_signal_emit (GTK_OBJECT (clist),
4966 				       clist_signals[SELECT_ROW],
4967 				       row, column, event);
4968 		      clist->anchor = -1;
4969 		    }
4970 		  else
4971 		    clist->anchor = row;
4972 		  break;
4973 		case GTK_SELECTION_BROWSE:
4974 		  gtk_signal_emit (GTK_OBJECT (clist),
4975 				   clist_signals[SELECT_ROW],
4976 				   row, column, event);
4977 		  break;
4978 		case GTK_SELECTION_MULTIPLE:
4979 		  if (event->type != GDK_BUTTON_PRESS)
4980 		    {
4981 		      if (clist->anchor != -1)
4982 			{
4983 			  update_extended_selection (clist, clist->focus_row);
4984 			  GTK_CLIST_GET_CLASS (clist)->resync_selection
4985 			    (clist, (GdkEvent *) event);
4986 			}
4987 		      gtk_signal_emit (GTK_OBJECT (clist),
4988 				       clist_signals[SELECT_ROW],
4989 				       row, column, event);
4990 		      break;
4991 		    }
4992 
4993 		  if (event->state & GDK_CONTROL_MASK)
4994 		    {
4995 		      if (event->state & GDK_SHIFT_MASK)
4996 			{
4997 			  if (clist->anchor < 0)
4998 			    {
4999 			      g_list_free (clist->undo_selection);
5000 			      g_list_free (clist->undo_unselection);
5001 			      clist->undo_selection = NULL;
5002 			      clist->undo_unselection = NULL;
5003 			      clist->anchor = old_row;
5004 			      clist->drag_pos = old_row;
5005 			      clist->undo_anchor = old_row;
5006 			    }
5007 			  update_extended_selection (clist, clist->focus_row);
5008 			}
5009 		      else
5010 			{
5011 			  if (clist->anchor == -1)
5012 			    set_anchor (clist, TRUE, row, old_row);
5013 			  else
5014 			    update_extended_selection (clist,
5015 						       clist->focus_row);
5016 			}
5017 		      break;
5018 		    }
5019 
5020 		  if (event->state & GDK_SHIFT_MASK)
5021 		    {
5022 		      set_anchor (clist, FALSE, old_row, old_row);
5023 		      update_extended_selection (clist, clist->focus_row);
5024 		      break;
5025 		    }
5026 
5027 		  if (clist->anchor == -1)
5028 		    set_anchor (clist, FALSE, row, old_row);
5029 		  else
5030 		    update_extended_selection (clist, clist->focus_row);
5031 		  break;
5032 		default:
5033 		  break;
5034 		}
5035 	    }
5036 	}
5037       return TRUE;
5038     }
5039 
5040   /* press on resize windows */
5041   for (i = 0; i < clist->columns; i++)
5042     if (clist->column[i].resizeable && clist->column[i].window &&
5043 	event->window == clist->column[i].window)
5044       {
5045 	gpointer drag_data;
5046 
5047 	if (gdk_pointer_grab (clist->column[i].window, FALSE,
5048 			      GDK_POINTER_MOTION_HINT_MASK |
5049 			      GDK_BUTTON1_MOTION_MASK |
5050 			      GDK_BUTTON_RELEASE_MASK,
5051 			      NULL, NULL, event->time))
5052 	  return FALSE;
5053 
5054 	gtk_grab_add (widget);
5055 	GTK_CLIST_SET_FLAG (clist, CLIST_IN_DRAG);
5056 
5057 	/* block attached dnd signal handler */
5058 	drag_data = gtk_object_get_data (GTK_OBJECT (clist), "gtk-site-data");
5059 	if (drag_data)
5060 	  gtk_signal_handler_block_by_data (GTK_OBJECT (clist), drag_data);
5061 
5062 	if (!gtk_widget_has_focus (widget))
5063 	  gtk_widget_grab_focus (widget);
5064 
5065 	clist->drag_pos = i;
5066 	clist->x_drag = (COLUMN_LEFT_XPIXEL(clist, i) + COLUMN_INSET +
5067 			 clist->column[i].area.width + CELL_SPACING);
5068 
5069 	if (GTK_CLIST_ADD_MODE(clist))
5070 	  gdk_gc_set_line_attributes (clist->xor_gc, 1, GDK_LINE_SOLID, 0, 0);
5071 	draw_xor_line (clist);
5072 
5073         return TRUE;
5074       }
5075 
5076   return FALSE;
5077 }
5078 
5079 static gint
gtk_clist_button_release(GtkWidget * widget,GdkEventButton * event)5080 gtk_clist_button_release (GtkWidget      *widget,
5081 			  GdkEventButton *event)
5082 {
5083   GtkCList *clist;
5084   gint button_actions;
5085 
5086   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
5087   g_return_val_if_fail (event != NULL, FALSE);
5088 
5089   clist = GTK_CLIST (widget);
5090 
5091   button_actions = clist->button_actions[event->button - 1];
5092   if (button_actions == GTK_BUTTON_IGNORED)
5093     return FALSE;
5094 
5095   /* release on resize windows */
5096   if (GTK_CLIST_IN_DRAG(clist))
5097     {
5098       gpointer drag_data;
5099       gint width;
5100       gint x;
5101       gint i;
5102 
5103       i = clist->drag_pos;
5104       clist->drag_pos = -1;
5105 
5106       /* unblock attached dnd signal handler */
5107       drag_data = gtk_object_get_data (GTK_OBJECT (clist), "gtk-site-data");
5108       if (drag_data)
5109 	gtk_signal_handler_unblock_by_data (GTK_OBJECT (clist), drag_data);
5110 
5111       GTK_CLIST_UNSET_FLAG (clist, CLIST_IN_DRAG);
5112       gtk_widget_get_pointer (widget, &x, NULL);
5113       gtk_grab_remove (widget);
5114       gdk_display_pointer_ungrab (gtk_widget_get_display (widget), event->time);
5115 
5116       if (clist->x_drag >= 0)
5117 	draw_xor_line (clist);
5118 
5119       if (GTK_CLIST_ADD_MODE(clist))
5120 	{
5121 	  gint8 dashes[] = { 4, 4 };
5122 
5123 	  gdk_gc_set_line_attributes (clist->xor_gc, 1,
5124 				      GDK_LINE_ON_OFF_DASH, 0, 0);
5125 	  gdk_gc_set_dashes (clist->xor_gc, 0, dashes, G_N_ELEMENTS (dashes));
5126 	}
5127 
5128       width = new_column_width (clist, i, &x);
5129       gtk_clist_set_column_width (clist, i, width);
5130 
5131       return TRUE;
5132     }
5133 
5134   if (clist->drag_button == event->button)
5135     {
5136       gint row;
5137       gint column;
5138 
5139       clist->drag_button = 0;
5140       clist->click_cell.row = -1;
5141       clist->click_cell.column = -1;
5142 
5143       remove_grab (clist);
5144 
5145       if (button_actions & GTK_BUTTON_SELECTS)
5146 	{
5147 	  switch (clist->selection_mode)
5148 	    {
5149 	    case GTK_SELECTION_MULTIPLE:
5150 	      if (!(event->state & GDK_SHIFT_MASK) ||
5151 		  !GTK_WIDGET_CAN_FOCUS (widget) ||
5152 		  event->x < 0 || event->x >= clist->clist_window_width ||
5153 		  event->y < 0 || event->y >= clist->clist_window_height)
5154 		GTK_CLIST_GET_CLASS (clist)->resync_selection
5155 		  (clist, (GdkEvent *) event);
5156 	      break;
5157 	    case GTK_SELECTION_SINGLE:
5158 	      if (get_selection_info (clist, event->x, event->y,
5159 				      &row, &column))
5160 		{
5161 		  if (row >= 0 && row < clist->rows && clist->anchor == row)
5162 		    toggle_row (clist, row, column, (GdkEvent *) event);
5163 		}
5164 	      clist->anchor = -1;
5165 	      break;
5166 	    default:
5167 	      break;
5168 	    }
5169 	}
5170 
5171       return TRUE;
5172     }
5173 
5174   return FALSE;
5175 }
5176 
5177 static gint
gtk_clist_motion(GtkWidget * widget,GdkEventMotion * event)5178 gtk_clist_motion (GtkWidget      *widget,
5179 		  GdkEventMotion *event)
5180 {
5181   GtkCList *clist;
5182   gint x;
5183   gint y;
5184   gint row;
5185   gint new_width;
5186   gint button_actions = 0;
5187 
5188   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
5189 
5190   clist = GTK_CLIST (widget);
5191   if (!clist_has_grab (clist))
5192     return FALSE;
5193 
5194   if (clist->drag_button > 0)
5195     button_actions = clist->button_actions[clist->drag_button - 1];
5196 
5197   if (GTK_CLIST_IN_DRAG(clist))
5198     {
5199       if (event->is_hint || event->window != widget->window)
5200 	gtk_widget_get_pointer (widget, &x, NULL);
5201       else
5202 	x = event->x;
5203 
5204       new_width = new_column_width (clist, clist->drag_pos, &x);
5205       if (x != clist->x_drag)
5206 	{
5207 	  /* x_drag < 0 indicates that the xor line is already invisible */
5208 	  if (clist->x_drag >= 0)
5209 	    draw_xor_line (clist);
5210 
5211 	  clist->x_drag = x;
5212 
5213 	  if (clist->x_drag >= 0)
5214 	    draw_xor_line (clist);
5215 	}
5216 
5217       if (new_width <= MAX (COLUMN_MIN_WIDTH + 1,
5218 			    clist->column[clist->drag_pos].min_width + 1))
5219 	{
5220 	  if (COLUMN_LEFT_XPIXEL (clist, clist->drag_pos) < 0 && x < 0)
5221 	    gtk_clist_moveto (clist, -1, clist->drag_pos, 0, 0);
5222 	  return FALSE;
5223 	}
5224       if (clist->column[clist->drag_pos].max_width >= COLUMN_MIN_WIDTH &&
5225 	  new_width >= clist->column[clist->drag_pos].max_width)
5226 	{
5227 	  if (COLUMN_LEFT_XPIXEL (clist, clist->drag_pos) + new_width >
5228 	      clist->clist_window_width && x < 0)
5229 	    move_horizontal (clist,
5230 			     COLUMN_LEFT_XPIXEL (clist, clist->drag_pos) +
5231 			     new_width - clist->clist_window_width +
5232 			     COLUMN_INSET + CELL_SPACING);
5233 	  return FALSE;
5234 	}
5235     }
5236 
5237   if (event->is_hint || event->window != clist->clist_window)
5238     gdk_window_get_pointer (clist->clist_window, &x, &y, NULL);
5239   else
5240     {
5241       x = event->x;
5242       y = event->y;
5243     }
5244 
5245   if (GTK_CLIST_REORDERABLE(clist) && button_actions & GTK_BUTTON_DRAGS)
5246     {
5247       /* delayed drag start */
5248       if (event->window == clist->clist_window &&
5249 	  clist->click_cell.row >= 0 && clist->click_cell.column >= 0 &&
5250 	  (y < 0 || y >= clist->clist_window_height ||
5251 	   x < 0 || x >= clist->clist_window_width  ||
5252 	   y < ROW_TOP_YPIXEL (clist, clist->click_cell.row) ||
5253 	   y >= (ROW_TOP_YPIXEL (clist, clist->click_cell.row) +
5254 		 clist->row_height) ||
5255 	   x < COLUMN_LEFT_XPIXEL (clist, clist->click_cell.column) ||
5256 	   x >= (COLUMN_LEFT_XPIXEL(clist, clist->click_cell.column) +
5257 		 clist->column[clist->click_cell.column].area.width)))
5258 	{
5259 	  GtkTargetList  *target_list;
5260 
5261 	  target_list = gtk_target_list_new (&clist_target_table, 1);
5262 	  gtk_drag_begin (widget, target_list, GDK_ACTION_MOVE,
5263 			  clist->drag_button, (GdkEvent *)event);
5264 
5265 	}
5266       return TRUE;
5267     }
5268 
5269   /* horizontal autoscrolling */
5270   if (clist->hadjustment && LIST_WIDTH (clist) > clist->clist_window_width &&
5271       (x < 0 || x >= clist->clist_window_width))
5272     {
5273       if (clist->htimer)
5274 	return FALSE;
5275 
5276       clist->htimer = gdk_threads_add_timeout
5277 	(SCROLL_TIME, (GSourceFunc) horizontal_timeout, clist);
5278 
5279       if (!((x < 0 && clist->hadjustment->value == 0) ||
5280 	    (x >= clist->clist_window_width &&
5281 	     clist->hadjustment->value ==
5282 	     LIST_WIDTH (clist) - clist->clist_window_width)))
5283 	{
5284 	  if (x < 0)
5285 	    move_horizontal (clist, -1 + (x/2));
5286 	  else
5287 	    move_horizontal (clist, 1 + (x - clist->clist_window_width) / 2);
5288 	}
5289     }
5290 
5291   if (GTK_CLIST_IN_DRAG(clist))
5292     return FALSE;
5293 
5294   /* vertical autoscrolling */
5295   row = ROW_FROM_YPIXEL (clist, y);
5296 
5297   /* don't scroll on last pixel row if it's a cell spacing */
5298   if (y == clist->clist_window_height - 1 &&
5299       y == ROW_TOP_YPIXEL (clist, row-1) + clist->row_height)
5300     return FALSE;
5301 
5302   if (LIST_HEIGHT (clist) > clist->clist_window_height &&
5303       (y < 0 || y >= clist->clist_window_height))
5304     {
5305       if (clist->vtimer)
5306 	return FALSE;
5307 
5308       clist->vtimer = gdk_threads_add_timeout (SCROLL_TIME,
5309 				     (GSourceFunc) vertical_timeout, clist);
5310 
5311       if (clist->drag_button &&
5312 	  ((y < 0 && clist->focus_row == 0) ||
5313 	   (y >= clist->clist_window_height &&
5314 	    clist->focus_row == clist->rows - 1)))
5315 	return FALSE;
5316     }
5317 
5318   row = CLAMP (row, 0, clist->rows - 1);
5319 
5320   if (button_actions & GTK_BUTTON_SELECTS &
5321       !gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data"))
5322     {
5323       if (row == clist->focus_row)
5324 	return FALSE;
5325 
5326       gtk_clist_draw_focus (widget);
5327       clist->focus_row = row;
5328       gtk_clist_draw_focus (widget);
5329 
5330       switch (clist->selection_mode)
5331 	{
5332 	case GTK_SELECTION_BROWSE:
5333 	  gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
5334 			   clist->focus_row, -1, event);
5335 	  break;
5336 	case GTK_SELECTION_MULTIPLE:
5337 	  update_extended_selection (clist, clist->focus_row);
5338 	  break;
5339 	default:
5340 	  break;
5341 	}
5342     }
5343 
5344   if (ROW_TOP_YPIXEL(clist, row) < 0)
5345     move_vertical (clist, row, 0);
5346   else if (ROW_TOP_YPIXEL(clist, row) + clist->row_height >
5347 	   clist->clist_window_height)
5348     move_vertical (clist, row, 1);
5349 
5350   return FALSE;
5351 }
5352 
5353 static void
gtk_clist_size_request(GtkWidget * widget,GtkRequisition * requisition)5354 gtk_clist_size_request (GtkWidget      *widget,
5355 			GtkRequisition *requisition)
5356 {
5357   GtkCList *clist;
5358   gint i;
5359 
5360   g_return_if_fail (GTK_IS_CLIST (widget));
5361   g_return_if_fail (requisition != NULL);
5362 
5363   clist = GTK_CLIST (widget);
5364 
5365   requisition->width = 0;
5366   requisition->height = 0;
5367 
5368   /* compute the size of the column title (title) area */
5369   clist->column_title_area.height = 0;
5370   if (GTK_CLIST_SHOW_TITLES(clist))
5371     for (i = 0; i < clist->columns; i++)
5372       if (clist->column[i].button)
5373 	{
5374 	  GtkRequisition child_requisition;
5375 
5376 	  gtk_widget_size_request (clist->column[i].button,
5377 				   &child_requisition);
5378 	  clist->column_title_area.height =
5379 	    MAX (clist->column_title_area.height,
5380 		 child_requisition.height);
5381 	}
5382 
5383   requisition->width += (widget->style->xthickness +
5384 			 GTK_CONTAINER (widget)->border_width) * 2;
5385   requisition->height += (clist->column_title_area.height +
5386 			  (widget->style->ythickness +
5387 			   GTK_CONTAINER (widget)->border_width) * 2);
5388 
5389   /* if (!clist->hadjustment) */
5390   requisition->width += list_requisition_width (clist);
5391   /* if (!clist->vadjustment) */
5392   requisition->height += LIST_HEIGHT (clist);
5393 }
5394 
5395 static void
gtk_clist_size_allocate(GtkWidget * widget,GtkAllocation * allocation)5396 gtk_clist_size_allocate (GtkWidget     *widget,
5397 			 GtkAllocation *allocation)
5398 {
5399   GtkCList *clist;
5400   GtkAllocation clist_allocation;
5401   gint border_width;
5402 
5403   g_return_if_fail (GTK_IS_CLIST (widget));
5404   g_return_if_fail (allocation != NULL);
5405 
5406   clist = GTK_CLIST (widget);
5407   widget->allocation = *allocation;
5408   border_width = GTK_CONTAINER (widget)->border_width;
5409 
5410   if (gtk_widget_get_realized (widget))
5411     {
5412       gdk_window_move_resize (widget->window,
5413 			      allocation->x + border_width,
5414 			      allocation->y + border_width,
5415 			      allocation->width - border_width * 2,
5416 			      allocation->height - border_width * 2);
5417     }
5418 
5419   /* use internal allocation structure for all the math
5420    * because it's easier than always subtracting the container
5421    * border width */
5422   clist->internal_allocation.x = 0;
5423   clist->internal_allocation.y = 0;
5424   clist->internal_allocation.width = MAX (1, (gint)allocation->width -
5425 					  border_width * 2);
5426   clist->internal_allocation.height = MAX (1, (gint)allocation->height -
5427 					   border_width * 2);
5428 
5429   /* allocate clist window assuming no scrollbars */
5430   clist_allocation.x = (clist->internal_allocation.x +
5431 			widget->style->xthickness);
5432   clist_allocation.y = (clist->internal_allocation.y +
5433 			widget->style->ythickness +
5434 			clist->column_title_area.height);
5435   clist_allocation.width = MAX (1, (gint)clist->internal_allocation.width -
5436 				(2 * (gint)widget->style->xthickness));
5437   clist_allocation.height = MAX (1, (gint)clist->internal_allocation.height -
5438 				 (2 * (gint)widget->style->ythickness) -
5439 				 (gint)clist->column_title_area.height);
5440 
5441   clist->clist_window_width = clist_allocation.width;
5442   clist->clist_window_height = clist_allocation.height;
5443 
5444   if (gtk_widget_get_realized (widget))
5445     {
5446       gdk_window_move_resize (clist->clist_window,
5447 			      clist_allocation.x,
5448 			      clist_allocation.y,
5449 			      clist_allocation.width,
5450 			      clist_allocation.height);
5451     }
5452 
5453   /* position the window which holds the column title buttons */
5454   clist->column_title_area.x = widget->style->xthickness;
5455   clist->column_title_area.y = widget->style->ythickness;
5456   clist->column_title_area.width = clist_allocation.width;
5457 
5458   if (gtk_widget_get_realized (widget))
5459     {
5460       gdk_window_move_resize (clist->title_window,
5461 			      clist->column_title_area.x,
5462 			      clist->column_title_area.y,
5463 			      clist->column_title_area.width,
5464 			      clist->column_title_area.height);
5465     }
5466 
5467   /* column button allocation */
5468   size_allocate_columns (clist, FALSE);
5469   size_allocate_title_buttons (clist);
5470 
5471   adjust_adjustments (clist, TRUE);
5472 }
5473 
5474 /* GTKCONTAINER
5475  *   gtk_clist_forall
5476  */
5477 static void
gtk_clist_forall(GtkContainer * container,gboolean include_internals,GtkCallback callback,gpointer callback_data)5478 gtk_clist_forall (GtkContainer *container,
5479 		  gboolean      include_internals,
5480 		  GtkCallback   callback,
5481 		  gpointer      callback_data)
5482 {
5483   GtkCList *clist;
5484   guint i;
5485 
5486   g_return_if_fail (GTK_IS_CLIST (container));
5487   g_return_if_fail (callback != NULL);
5488 
5489   if (!include_internals)
5490     return;
5491 
5492   clist = GTK_CLIST (container);
5493 
5494   /* callback for the column buttons */
5495   for (i = 0; i < clist->columns; i++)
5496     if (clist->column[i].button)
5497       (*callback) (clist->column[i].button, callback_data);
5498 }
5499 
5500 /* PRIVATE DRAWING FUNCTIONS
5501  *   get_cell_style
5502  *   draw_cell_pixmap
5503  *   draw_row
5504  *   draw_rows
5505  *   draw_xor_line
5506  *   clist_refresh
5507  */
5508 static void
get_cell_style(GtkCList * clist,GtkCListRow * clist_row,gint state,gint column,GtkStyle ** style,GdkGC ** fg_gc,GdkGC ** bg_gc)5509 get_cell_style (GtkCList     *clist,
5510 		GtkCListRow  *clist_row,
5511 		gint          state,
5512 		gint          column,
5513 		GtkStyle    **style,
5514 		GdkGC       **fg_gc,
5515 		GdkGC       **bg_gc)
5516 {
5517   gint fg_state;
5518 
5519   if ((state == GTK_STATE_NORMAL) &&
5520       (GTK_WIDGET (clist)->state == GTK_STATE_INSENSITIVE))
5521     fg_state = GTK_STATE_INSENSITIVE;
5522   else
5523     fg_state = state;
5524 
5525   if (clist_row->cell[column].style)
5526     {
5527       if (style)
5528 	*style = clist_row->cell[column].style;
5529       if (fg_gc)
5530 	*fg_gc = clist_row->cell[column].style->fg_gc[fg_state];
5531       if (bg_gc) {
5532 	if (state == GTK_STATE_SELECTED)
5533 	  *bg_gc = clist_row->cell[column].style->bg_gc[state];
5534 	else
5535 	  *bg_gc = clist_row->cell[column].style->base_gc[state];
5536       }
5537     }
5538   else if (clist_row->style)
5539     {
5540       if (style)
5541 	*style = clist_row->style;
5542       if (fg_gc)
5543 	*fg_gc = clist_row->style->fg_gc[fg_state];
5544       if (bg_gc) {
5545 	if (state == GTK_STATE_SELECTED)
5546 	  *bg_gc = clist_row->style->bg_gc[state];
5547 	else
5548 	  *bg_gc = clist_row->style->base_gc[state];
5549       }
5550     }
5551   else
5552     {
5553       if (style)
5554 	*style = GTK_WIDGET (clist)->style;
5555       if (fg_gc)
5556 	*fg_gc = GTK_WIDGET (clist)->style->fg_gc[fg_state];
5557       if (bg_gc) {
5558 	if (state == GTK_STATE_SELECTED)
5559 	  *bg_gc = GTK_WIDGET (clist)->style->bg_gc[state];
5560 	else
5561 	  *bg_gc = GTK_WIDGET (clist)->style->base_gc[state];
5562       }
5563 
5564       if (state != GTK_STATE_SELECTED)
5565 	{
5566 	  if (fg_gc && clist_row->fg_set)
5567 	    *fg_gc = clist->fg_gc;
5568 	  if (bg_gc && clist_row->bg_set)
5569 	    *bg_gc = clist->bg_gc;
5570 	}
5571     }
5572 }
5573 
5574 static gint
draw_cell_pixmap(GdkWindow * window,GdkRectangle * clip_rectangle,GdkGC * fg_gc,GdkPixmap * pixmap,GdkBitmap * mask,gint x,gint y,gint width,gint height)5575 draw_cell_pixmap (GdkWindow    *window,
5576 		  GdkRectangle *clip_rectangle,
5577 		  GdkGC        *fg_gc,
5578 		  GdkPixmap    *pixmap,
5579 		  GdkBitmap    *mask,
5580 		  gint          x,
5581 		  gint          y,
5582 		  gint          width,
5583 		  gint          height)
5584 {
5585   gint xsrc = 0;
5586   gint ysrc = 0;
5587 
5588   if (mask)
5589     {
5590       gdk_gc_set_clip_mask (fg_gc, mask);
5591       gdk_gc_set_clip_origin (fg_gc, x, y);
5592     }
5593 
5594   if (x < clip_rectangle->x)
5595     {
5596       xsrc = clip_rectangle->x - x;
5597       width -= xsrc;
5598       x = clip_rectangle->x;
5599     }
5600   if (x + width > clip_rectangle->x + clip_rectangle->width)
5601     width = clip_rectangle->x + clip_rectangle->width - x;
5602 
5603   if (y < clip_rectangle->y)
5604     {
5605       ysrc = clip_rectangle->y - y;
5606       height -= ysrc;
5607       y = clip_rectangle->y;
5608     }
5609   if (y + height > clip_rectangle->y + clip_rectangle->height)
5610     height = clip_rectangle->y + clip_rectangle->height - y;
5611 
5612   gdk_draw_drawable (window, fg_gc, pixmap, xsrc, ysrc, x, y, width, height);
5613   gdk_gc_set_clip_origin (fg_gc, 0, 0);
5614   if (mask)
5615     gdk_gc_set_clip_mask (fg_gc, NULL);
5616 
5617   return x + MAX (width, 0);
5618 }
5619 
5620 static void
draw_row(GtkCList * clist,GdkRectangle * area,gint row,GtkCListRow * clist_row)5621 draw_row (GtkCList     *clist,
5622 	  GdkRectangle *area,
5623 	  gint          row,
5624 	  GtkCListRow  *clist_row)
5625 {
5626   GtkWidget *widget;
5627   GdkRectangle *rect;
5628   GdkRectangle row_rectangle;
5629   GdkRectangle cell_rectangle;
5630   GdkRectangle clip_rectangle;
5631   GdkRectangle intersect_rectangle;
5632   gint last_column;
5633   gint state;
5634   gint i;
5635 
5636   g_return_if_fail (clist != NULL);
5637 
5638   /* bail now if we arn't drawable yet */
5639   if (!GTK_WIDGET_DRAWABLE (clist) || row < 0 || row >= clist->rows)
5640     return;
5641 
5642   widget = GTK_WIDGET (clist);
5643 
5644   /* if the function is passed the pointer to the row instead of null,
5645    * it avoids this expensive lookup */
5646   if (!clist_row)
5647     clist_row = ROW_ELEMENT (clist, row)->data;
5648 
5649   /* rectangle of the entire row */
5650   row_rectangle.x = 0;
5651   row_rectangle.y = ROW_TOP_YPIXEL (clist, row);
5652   row_rectangle.width = clist->clist_window_width;
5653   row_rectangle.height = clist->row_height;
5654 
5655   /* rectangle of the cell spacing above the row */
5656   cell_rectangle.x = 0;
5657   cell_rectangle.y = row_rectangle.y - CELL_SPACING;
5658   cell_rectangle.width = row_rectangle.width;
5659   cell_rectangle.height = CELL_SPACING;
5660 
5661   /* rectangle used to clip drawing operations, its y and height
5662    * positions only need to be set once, so we set them once here.
5663    * the x and width are set withing the drawing loop below once per
5664    * column */
5665   clip_rectangle.y = row_rectangle.y;
5666   clip_rectangle.height = row_rectangle.height;
5667 
5668   if (clist_row->state == GTK_STATE_NORMAL)
5669     {
5670       if (clist_row->fg_set)
5671 	gdk_gc_set_foreground (clist->fg_gc, &clist_row->foreground);
5672       if (clist_row->bg_set)
5673 	gdk_gc_set_foreground (clist->bg_gc, &clist_row->background);
5674     }
5675 
5676   state = clist_row->state;
5677 
5678   /* draw the cell borders and background */
5679   if (area)
5680     {
5681       rect = &intersect_rectangle;
5682       if (gdk_rectangle_intersect (area, &cell_rectangle,
5683 				   &intersect_rectangle))
5684 	gdk_draw_rectangle (clist->clist_window,
5685 			    widget->style->base_gc[GTK_STATE_NORMAL],
5686 			    TRUE,
5687 			    intersect_rectangle.x,
5688 			    intersect_rectangle.y,
5689 			    intersect_rectangle.width,
5690 			    intersect_rectangle.height);
5691 
5692       /* the last row has to clear its bottom cell spacing too */
5693       if (clist_row == clist->row_list_end->data)
5694 	{
5695 	  cell_rectangle.y += clist->row_height + CELL_SPACING;
5696 
5697 	  if (gdk_rectangle_intersect (area, &cell_rectangle,
5698 				       &intersect_rectangle))
5699 	    gdk_draw_rectangle (clist->clist_window,
5700 				widget->style->base_gc[GTK_STATE_NORMAL],
5701 				TRUE,
5702 				intersect_rectangle.x,
5703 				intersect_rectangle.y,
5704 				intersect_rectangle.width,
5705 				intersect_rectangle.height);
5706 	}
5707 
5708       if (!gdk_rectangle_intersect (area, &row_rectangle,&intersect_rectangle))
5709 	return;
5710 
5711     }
5712   else
5713     {
5714       rect = &clip_rectangle;
5715       gdk_draw_rectangle (clist->clist_window,
5716 			  widget->style->base_gc[GTK_STATE_NORMAL],
5717 			  TRUE,
5718 			  cell_rectangle.x,
5719 			  cell_rectangle.y,
5720 			  cell_rectangle.width,
5721 			  cell_rectangle.height);
5722 
5723       /* the last row has to clear its bottom cell spacing too */
5724       if (clist_row == clist->row_list_end->data)
5725 	{
5726 	  cell_rectangle.y += clist->row_height + CELL_SPACING;
5727 
5728 	  gdk_draw_rectangle (clist->clist_window,
5729 			      widget->style->base_gc[GTK_STATE_NORMAL],
5730 			      TRUE,
5731 			      cell_rectangle.x,
5732 			      cell_rectangle.y,
5733 			      cell_rectangle.width,
5734 			      cell_rectangle.height);
5735 	}
5736     }
5737 
5738   for (last_column = clist->columns - 1;
5739        last_column >= 0 && !clist->column[last_column].visible; last_column--)
5740     ;
5741 
5742   /* iterate and draw all the columns (row cells) and draw their contents */
5743   for (i = 0; i < clist->columns; i++)
5744     {
5745       GtkStyle *style;
5746       GdkGC *fg_gc;
5747       GdkGC *bg_gc;
5748       PangoLayout *layout;
5749       PangoRectangle logical_rect;
5750 
5751       gint width;
5752       gint height;
5753       gint pixmap_width;
5754       gint offset = 0;
5755 
5756       if (!clist->column[i].visible)
5757 	continue;
5758 
5759       get_cell_style (clist, clist_row, state, i, &style, &fg_gc, &bg_gc);
5760 
5761       clip_rectangle.x = clist->column[i].area.x + clist->hoffset;
5762       clip_rectangle.width = clist->column[i].area.width;
5763 
5764       /* calculate clipping region clipping region */
5765       clip_rectangle.x -= COLUMN_INSET + CELL_SPACING;
5766       clip_rectangle.width += (2 * COLUMN_INSET + CELL_SPACING +
5767 			       (i == last_column) * CELL_SPACING);
5768 
5769       if (area && !gdk_rectangle_intersect (area, &clip_rectangle,
5770 					    &intersect_rectangle))
5771 	continue;
5772 
5773       gdk_draw_rectangle (clist->clist_window, bg_gc, TRUE,
5774 			  rect->x, rect->y, rect->width, rect->height);
5775 
5776       clip_rectangle.x += COLUMN_INSET + CELL_SPACING;
5777       clip_rectangle.width -= (2 * COLUMN_INSET + CELL_SPACING +
5778 			       (i == last_column) * CELL_SPACING);
5779 
5780 
5781       /* calculate real width for column justification */
5782 
5783       layout = _gtk_clist_create_cell_layout (clist, clist_row, i);
5784       if (layout)
5785 	{
5786 	  pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
5787 	  width = logical_rect.width;
5788 	}
5789       else
5790 	width = 0;
5791 
5792       pixmap_width = 0;
5793       offset = 0;
5794       switch (clist_row->cell[i].type)
5795 	{
5796 	case GTK_CELL_PIXMAP:
5797 	  gdk_drawable_get_size (GTK_CELL_PIXMAP (clist_row->cell[i])->pixmap,
5798                                  &pixmap_width, &height);
5799 	  width += pixmap_width;
5800 	  break;
5801 	case GTK_CELL_PIXTEXT:
5802 	  gdk_drawable_get_size (GTK_CELL_PIXTEXT (clist_row->cell[i])->pixmap,
5803                                  &pixmap_width, &height);
5804 	  width += pixmap_width + GTK_CELL_PIXTEXT (clist_row->cell[i])->spacing;
5805 	  break;
5806 	default:
5807 	  break;
5808 	}
5809 
5810       switch (clist->column[i].justification)
5811 	{
5812 	case GTK_JUSTIFY_LEFT:
5813 	  offset = clip_rectangle.x + clist_row->cell[i].horizontal;
5814 	  break;
5815 	case GTK_JUSTIFY_RIGHT:
5816 	  offset = (clip_rectangle.x + clist_row->cell[i].horizontal +
5817 		    clip_rectangle.width - width);
5818 	  break;
5819 	case GTK_JUSTIFY_CENTER:
5820 	case GTK_JUSTIFY_FILL:
5821 	  offset = (clip_rectangle.x + clist_row->cell[i].horizontal +
5822 		    (clip_rectangle.width / 2) - (width / 2));
5823 	  break;
5824 	};
5825 
5826       /* Draw Text and/or Pixmap */
5827       switch (clist_row->cell[i].type)
5828 	{
5829 	case GTK_CELL_PIXMAP:
5830 	  draw_cell_pixmap (clist->clist_window, &clip_rectangle, fg_gc,
5831 			    GTK_CELL_PIXMAP (clist_row->cell[i])->pixmap,
5832 			    GTK_CELL_PIXMAP (clist_row->cell[i])->mask,
5833 			    offset,
5834 			    clip_rectangle.y + clist_row->cell[i].vertical +
5835 			    (clip_rectangle.height - height) / 2,
5836 			    pixmap_width, height);
5837 	  break;
5838 	case GTK_CELL_PIXTEXT:
5839 	  offset =
5840 	    draw_cell_pixmap (clist->clist_window, &clip_rectangle, fg_gc,
5841 			      GTK_CELL_PIXTEXT (clist_row->cell[i])->pixmap,
5842 			      GTK_CELL_PIXTEXT (clist_row->cell[i])->mask,
5843 			      offset,
5844 			      clip_rectangle.y + clist_row->cell[i].vertical+
5845 			      (clip_rectangle.height - height) / 2,
5846 			      pixmap_width, height);
5847 	  offset += GTK_CELL_PIXTEXT (clist_row->cell[i])->spacing;
5848 
5849 	  /* Fall through */
5850 	case GTK_CELL_TEXT:
5851 	  if (layout)
5852 	    {
5853 	      gint row_center_offset = (clist->row_height - logical_rect.height - 1) / 2;
5854 
5855 	      gdk_gc_set_clip_rectangle (fg_gc, &clip_rectangle);
5856 	      gdk_draw_layout (clist->clist_window, fg_gc,
5857 			       offset,
5858 			       row_rectangle.y + row_center_offset + clist_row->cell[i].vertical,
5859 			       layout);
5860               g_object_unref (layout);
5861 	      gdk_gc_set_clip_rectangle (fg_gc, NULL);
5862 	    }
5863 	  break;
5864 	default:
5865 	  break;
5866 	}
5867     }
5868 
5869   /* draw focus rectangle */
5870   if (clist->focus_row == row &&
5871       GTK_WIDGET_CAN_FOCUS (widget) && gtk_widget_has_focus (widget))
5872     {
5873       if (!area)
5874 	gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE,
5875 			    row_rectangle.x, row_rectangle.y,
5876 			    row_rectangle.width - 1, row_rectangle.height - 1);
5877       else if (gdk_rectangle_intersect (area, &row_rectangle,
5878 					&intersect_rectangle))
5879 	{
5880 	  gdk_gc_set_clip_rectangle (clist->xor_gc, &intersect_rectangle);
5881 	  gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE,
5882 			      row_rectangle.x, row_rectangle.y,
5883 			      row_rectangle.width - 1,
5884 			      row_rectangle.height - 1);
5885 	  gdk_gc_set_clip_rectangle (clist->xor_gc, NULL);
5886 	}
5887     }
5888 }
5889 
5890 static void
draw_rows(GtkCList * clist,GdkRectangle * area)5891 draw_rows (GtkCList     *clist,
5892 	   GdkRectangle *area)
5893 {
5894   GList *list;
5895   GtkCListRow *clist_row;
5896   gint i;
5897   gint first_row;
5898   gint last_row;
5899 
5900   g_return_if_fail (GTK_IS_CLIST (clist));
5901 
5902   if (clist->row_height == 0 ||
5903       !GTK_WIDGET_DRAWABLE (clist))
5904     return;
5905 
5906   if (area)
5907     {
5908       first_row = ROW_FROM_YPIXEL (clist, area->y);
5909       last_row = ROW_FROM_YPIXEL (clist, area->y + area->height);
5910     }
5911   else
5912     {
5913       first_row = ROW_FROM_YPIXEL (clist, 0);
5914       last_row = ROW_FROM_YPIXEL (clist, clist->clist_window_height);
5915     }
5916 
5917   /* this is a small special case which exposes the bottom cell line
5918    * on the last row -- it might go away if I change the wall the cell
5919    * spacings are drawn
5920    */
5921   if (clist->rows == first_row)
5922     first_row--;
5923 
5924   list = ROW_ELEMENT (clist, first_row);
5925   i = first_row;
5926   while (list)
5927     {
5928       clist_row = list->data;
5929       list = list->next;
5930 
5931       if (i > last_row)
5932 	return;
5933 
5934       GTK_CLIST_GET_CLASS (clist)->draw_row (clist, area, i, clist_row);
5935       i++;
5936     }
5937 
5938   if (!area)
5939     {
5940       int w, h, y;
5941       gdk_drawable_get_size (GDK_DRAWABLE (clist->clist_window), &w, &h);
5942       y = ROW_TOP_YPIXEL (clist, i);
5943       gdk_window_clear_area (clist->clist_window,
5944 			     0, y,
5945 			     w, h - y);
5946     }
5947 }
5948 
5949 static void
draw_xor_line(GtkCList * clist)5950 draw_xor_line (GtkCList *clist)
5951 {
5952   GtkWidget *widget;
5953 
5954   g_return_if_fail (clist != NULL);
5955 
5956   widget = GTK_WIDGET (clist);
5957 
5958   gdk_draw_line (widget->window, clist->xor_gc,
5959                  clist->x_drag,
5960 		 widget->style->ythickness,
5961                  clist->x_drag,
5962                  clist->column_title_area.height +
5963 		 clist->clist_window_height + 1);
5964 }
5965 
5966 static void
clist_refresh(GtkCList * clist)5967 clist_refresh (GtkCList *clist)
5968 {
5969   g_return_if_fail (GTK_IS_CLIST (clist));
5970 
5971   if (CLIST_UNFROZEN (clist))
5972     {
5973       adjust_adjustments (clist, FALSE);
5974       draw_rows (clist, NULL);
5975     }
5976 }
5977 
5978 /* get cell from coordinates
5979  *   get_selection_info
5980  *   gtk_clist_get_selection_info
5981  */
5982 static gint
get_selection_info(GtkCList * clist,gint x,gint y,gint * row,gint * column)5983 get_selection_info (GtkCList *clist,
5984 		    gint      x,
5985 		    gint      y,
5986 		    gint     *row,
5987 		    gint     *column)
5988 {
5989   gint trow, tcol;
5990 
5991   g_return_val_if_fail (GTK_IS_CLIST (clist), 0);
5992 
5993   /* bounds checking, return false if the user clicked
5994    * on a blank area */
5995   trow = ROW_FROM_YPIXEL (clist, y);
5996   if (trow >= clist->rows)
5997     return 0;
5998 
5999   if (row)
6000     *row = trow;
6001 
6002   tcol = COLUMN_FROM_XPIXEL (clist, x);
6003   if (tcol >= clist->columns)
6004     return 0;
6005 
6006   if (column)
6007     *column = tcol;
6008 
6009   return 1;
6010 }
6011 
6012 gint
gtk_clist_get_selection_info(GtkCList * clist,gint x,gint y,gint * row,gint * column)6013 gtk_clist_get_selection_info (GtkCList *clist,
6014 			      gint      x,
6015 			      gint      y,
6016 			      gint     *row,
6017 			      gint     *column)
6018 {
6019   g_return_val_if_fail (GTK_IS_CLIST (clist), 0);
6020   return get_selection_info (clist, x, y, row, column);
6021 }
6022 
6023 /* PRIVATE ADJUSTMENT FUNCTIONS
6024  *   adjust_adjustments
6025  *   vadjustment_changed
6026  *   hadjustment_changed
6027  *   vadjustment_value_changed
6028  *   hadjustment_value_changed
6029  *   check_exposures
6030  */
6031 static void
adjust_adjustments(GtkCList * clist,gboolean block_resize)6032 adjust_adjustments (GtkCList *clist,
6033 		    gboolean  block_resize)
6034 {
6035   if (clist->vadjustment)
6036     {
6037       clist->vadjustment->page_size = clist->clist_window_height;
6038       clist->vadjustment->step_increment = clist->row_height;
6039       clist->vadjustment->page_increment =
6040 	MAX (clist->vadjustment->page_size - clist->vadjustment->step_increment,
6041 	     clist->vadjustment->page_size / 2);
6042       clist->vadjustment->lower = 0;
6043       clist->vadjustment->upper = LIST_HEIGHT (clist);
6044 
6045       if (clist->clist_window_height - clist->voffset > LIST_HEIGHT (clist) ||
6046 	  (clist->voffset + (gint)clist->vadjustment->value) != 0)
6047 	{
6048 	  clist->vadjustment->value = MAX (0, (LIST_HEIGHT (clist) -
6049 					       clist->clist_window_height));
6050 	  gtk_signal_emit_by_name (GTK_OBJECT (clist->vadjustment),
6051 				   "value-changed");
6052 	}
6053       gtk_signal_emit_by_name (GTK_OBJECT (clist->vadjustment), "changed");
6054     }
6055 
6056   if (clist->hadjustment)
6057     {
6058       clist->hadjustment->page_size = clist->clist_window_width;
6059       clist->hadjustment->step_increment = 10;
6060       clist->hadjustment->page_increment =
6061 	MAX (clist->hadjustment->page_size - clist->hadjustment->step_increment,
6062 	     clist->hadjustment->page_size / 2);
6063       clist->hadjustment->lower = 0;
6064       clist->hadjustment->upper = LIST_WIDTH (clist);
6065 
6066       if (clist->clist_window_width - clist->hoffset > LIST_WIDTH (clist) ||
6067 	  (clist->hoffset + (gint)clist->hadjustment->value) != 0)
6068 	{
6069 	  clist->hadjustment->value = MAX (0, (LIST_WIDTH (clist) -
6070 					       clist->clist_window_width));
6071 	  gtk_signal_emit_by_name (GTK_OBJECT (clist->hadjustment),
6072 				   "value-changed");
6073 	}
6074       gtk_signal_emit_by_name (GTK_OBJECT (clist->hadjustment), "changed");
6075     }
6076 
6077   if (!block_resize && (!clist->vadjustment || !clist->hadjustment))
6078     {
6079       GtkWidget *widget;
6080       GtkRequisition requisition;
6081 
6082       widget = GTK_WIDGET (clist);
6083       gtk_widget_size_request (widget, &requisition);
6084 
6085       if ((!clist->hadjustment &&
6086 	   requisition.width != widget->allocation.width) ||
6087 	  (!clist->vadjustment &&
6088 	   requisition.height != widget->allocation.height))
6089 	gtk_widget_queue_resize (widget);
6090     }
6091 }
6092 
6093 static void
vadjustment_changed(GtkAdjustment * adjustment,gpointer data)6094 vadjustment_changed (GtkAdjustment *adjustment,
6095 		     gpointer       data)
6096 {
6097   GtkCList *clist;
6098 
6099   g_return_if_fail (adjustment != NULL);
6100   g_return_if_fail (data != NULL);
6101 
6102   clist = GTK_CLIST (data);
6103 }
6104 
6105 static void
hadjustment_changed(GtkAdjustment * adjustment,gpointer data)6106 hadjustment_changed (GtkAdjustment *adjustment,
6107 		     gpointer       data)
6108 {
6109   GtkCList *clist;
6110 
6111   g_return_if_fail (adjustment != NULL);
6112   g_return_if_fail (data != NULL);
6113 
6114   clist = GTK_CLIST (data);
6115 }
6116 
6117 static void
vadjustment_value_changed(GtkAdjustment * adjustment,gpointer data)6118 vadjustment_value_changed (GtkAdjustment *adjustment,
6119 			   gpointer       data)
6120 {
6121   GtkCList *clist;
6122   gint dy, value;
6123 
6124   g_return_if_fail (adjustment != NULL);
6125   g_return_if_fail (GTK_IS_CLIST (data));
6126 
6127   clist = GTK_CLIST (data);
6128 
6129   if (adjustment != clist->vadjustment)
6130     return;
6131 
6132   value = -adjustment->value;
6133   dy = value - clist->voffset;
6134   clist->voffset = value;
6135 
6136   if (GTK_WIDGET_DRAWABLE (clist))
6137     {
6138       gdk_window_scroll (clist->clist_window, 0, dy);
6139       gdk_window_process_updates (clist->clist_window, FALSE);
6140     }
6141 
6142   return;
6143 }
6144 
6145 typedef struct
6146 {
6147   GdkWindow *window;
6148   gint dx;
6149 } ScrollData;
6150 
6151 /* The window to which widget->window is relative */
6152 #define ALLOCATION_WINDOW(widget)		\
6153    (!gtk_widget_get_has_window (widget) ?		\
6154     (widget)->window :                          \
6155      gdk_window_get_parent ((widget)->window))
6156 
6157 static void
adjust_allocation_recurse(GtkWidget * widget,gpointer data)6158 adjust_allocation_recurse (GtkWidget *widget,
6159 			   gpointer   data)
6160 {
6161   ScrollData *scroll_data = data;
6162 
6163   if (!gtk_widget_get_realized (widget))
6164     {
6165       if (gtk_widget_get_visible (widget))
6166 	{
6167 	  GdkRectangle tmp_rectangle = widget->allocation;
6168 	  tmp_rectangle.x += scroll_data->dx;
6169 
6170 	  gtk_widget_size_allocate (widget, &tmp_rectangle);
6171 	}
6172     }
6173   else
6174     {
6175       if (ALLOCATION_WINDOW (widget) == scroll_data->window)
6176 	{
6177 	  widget->allocation.x += scroll_data->dx;
6178 
6179 	  if (GTK_IS_CONTAINER (widget))
6180 	    gtk_container_forall (GTK_CONTAINER (widget),
6181 				  adjust_allocation_recurse,
6182 				  data);
6183 	}
6184     }
6185 }
6186 
6187 static void
adjust_allocation(GtkWidget * widget,gint dx)6188 adjust_allocation (GtkWidget *widget,
6189 		   gint       dx)
6190 {
6191   ScrollData scroll_data;
6192 
6193   if (gtk_widget_get_realized (widget))
6194     scroll_data.window = ALLOCATION_WINDOW (widget);
6195   else
6196     scroll_data.window = NULL;
6197 
6198   scroll_data.dx = dx;
6199 
6200   adjust_allocation_recurse (widget, &scroll_data);
6201 }
6202 
6203 static void
hadjustment_value_changed(GtkAdjustment * adjustment,gpointer data)6204 hadjustment_value_changed (GtkAdjustment *adjustment,
6205 			   gpointer       data)
6206 {
6207   GtkCList *clist;
6208   GtkContainer *container;
6209   GdkRectangle area;
6210   gint i;
6211   gint y = 0;
6212   gint value;
6213   gint dx;
6214 
6215   g_return_if_fail (adjustment != NULL);
6216   g_return_if_fail (GTK_IS_CLIST (data));
6217 
6218   clist = GTK_CLIST (data);
6219   container = GTK_CONTAINER (data);
6220 
6221   if (adjustment != clist->hadjustment)
6222     return;
6223 
6224   value = adjustment->value;
6225 
6226   dx = -value - clist->hoffset;
6227 
6228   if (gtk_widget_get_realized (GTK_WIDGET (clist)))
6229     gdk_window_scroll (clist->title_window, dx, 0);
6230 
6231   /* adjust the column button's allocations */
6232   for (i = 0; i < clist->columns; i++)
6233     if (clist->column[i].button)
6234       adjust_allocation (clist->column[i].button, dx);
6235 
6236   clist->hoffset = -value;
6237 
6238   if (GTK_WIDGET_DRAWABLE (clist))
6239     {
6240       if (GTK_WIDGET_CAN_FOCUS(clist) && gtk_widget_has_focus (GTK_WIDGET (clist)) &&
6241           !container->focus_child && GTK_CLIST_ADD_MODE(clist))
6242         {
6243           y = ROW_TOP_YPIXEL (clist, clist->focus_row);
6244 
6245           gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE, 0, y,
6246                               clist->clist_window_width - 1,
6247                               clist->row_height - 1);
6248         }
6249 
6250       gdk_window_scroll (clist->clist_window, dx, 0);
6251       gdk_window_process_updates (clist->clist_window, FALSE);
6252 
6253       if (GTK_WIDGET_CAN_FOCUS(clist) && gtk_widget_has_focus (GTK_WIDGET (clist)) &&
6254           !container->focus_child)
6255         {
6256           if (GTK_CLIST_ADD_MODE(clist))
6257             {
6258               gint focus_row;
6259 
6260               focus_row = clist->focus_row;
6261               clist->focus_row = -1;
6262               draw_rows (clist, &area);
6263               clist->focus_row = focus_row;
6264 
6265               gdk_draw_rectangle (clist->clist_window, clist->xor_gc,
6266                                   FALSE, 0, y, clist->clist_window_width - 1,
6267                                   clist->row_height - 1);
6268               return;
6269             }
6270           else if (ABS(dx) < clist->clist_window_width - 1)
6271             {
6272               gint x0;
6273               gint x1;
6274 
6275               if (dx > 0)
6276                 {
6277                   x0 = clist->clist_window_width - 1;
6278                   x1 = dx;
6279                 }
6280               else
6281                 {
6282                   x0 = 0;
6283                   x1 = clist->clist_window_width - 1 + dx;
6284                 }
6285 
6286               y = ROW_TOP_YPIXEL (clist, clist->focus_row);
6287               gdk_draw_line (clist->clist_window, clist->xor_gc,
6288                              x0, y + 1, x0, y + clist->row_height - 2);
6289               gdk_draw_line (clist->clist_window, clist->xor_gc,
6290                              x1, y + 1, x1, y + clist->row_height - 2);
6291             }
6292         }
6293     }
6294 }
6295 
6296 /* PRIVATE
6297  * Memory Allocation/Distruction Routines for GtkCList stuctures
6298  *
6299  * functions:
6300  *   columns_new
6301  *   column_title_new
6302  *   columns_delete
6303  *   row_new
6304  *   row_delete
6305  */
6306 static GtkCListColumn *
columns_new(GtkCList * clist)6307 columns_new (GtkCList *clist)
6308 {
6309   GtkCListColumn *column;
6310   gint i;
6311 
6312   column = g_new (GtkCListColumn, clist->columns);
6313 
6314   for (i = 0; i < clist->columns; i++)
6315     {
6316       column[i].area.x = 0;
6317       column[i].area.y = 0;
6318       column[i].area.width = 0;
6319       column[i].area.height = 0;
6320       column[i].title = NULL;
6321       column[i].button = NULL;
6322       column[i].window = NULL;
6323       column[i].width = 0;
6324       column[i].min_width = -1;
6325       column[i].max_width = -1;
6326       column[i].visible = TRUE;
6327       column[i].width_set = FALSE;
6328       column[i].resizeable = TRUE;
6329       column[i].auto_resize = FALSE;
6330       column[i].button_passive = FALSE;
6331       column[i].justification = GTK_JUSTIFY_LEFT;
6332     }
6333 
6334   return column;
6335 }
6336 
6337 static void
column_title_new(GtkCList * clist,gint column,const gchar * title)6338 column_title_new (GtkCList    *clist,
6339 		  gint         column,
6340 		  const gchar *title)
6341 {
6342   g_free (clist->column[column].title);
6343 
6344   clist->column[column].title = g_strdup (title);
6345 }
6346 
6347 static void
columns_delete(GtkCList * clist)6348 columns_delete (GtkCList *clist)
6349 {
6350   gint i;
6351 
6352   for (i = 0; i < clist->columns; i++)
6353     g_free (clist->column[i].title);
6354 
6355   g_free (clist->column);
6356 }
6357 
6358 static GtkCListRow *
row_new(GtkCList * clist)6359 row_new (GtkCList *clist)
6360 {
6361   int i;
6362   GtkCListRow *clist_row;
6363 
6364   clist_row = g_slice_new (GtkCListRow);
6365   clist_row->cell = g_slice_alloc (sizeof (GtkCell) * clist->columns);
6366 
6367   for (i = 0; i < clist->columns; i++)
6368     {
6369       clist_row->cell[i].type = GTK_CELL_EMPTY;
6370       clist_row->cell[i].vertical = 0;
6371       clist_row->cell[i].horizontal = 0;
6372       clist_row->cell[i].style = NULL;
6373     }
6374 
6375   clist_row->fg_set = FALSE;
6376   clist_row->bg_set = FALSE;
6377   clist_row->style = NULL;
6378   clist_row->selectable = TRUE;
6379   clist_row->state = GTK_STATE_NORMAL;
6380   clist_row->data = NULL;
6381   clist_row->destroy = NULL;
6382 
6383   return clist_row;
6384 }
6385 
6386 static void
row_delete(GtkCList * clist,GtkCListRow * clist_row)6387 row_delete (GtkCList    *clist,
6388 	    GtkCListRow *clist_row)
6389 {
6390   gint i;
6391 
6392   for (i = 0; i < clist->columns; i++)
6393     {
6394       GTK_CLIST_GET_CLASS (clist)->set_cell_contents
6395 	(clist, clist_row, i, GTK_CELL_EMPTY, NULL, 0, NULL, NULL);
6396       if (clist_row->cell[i].style)
6397 	{
6398 	  if (gtk_widget_get_realized (GTK_WIDGET (clist)))
6399 	    gtk_style_detach (clist_row->cell[i].style);
6400 	  g_object_unref (clist_row->cell[i].style);
6401 	}
6402     }
6403 
6404   if (clist_row->style)
6405     {
6406       if (gtk_widget_get_realized (GTK_WIDGET (clist)))
6407         gtk_style_detach (clist_row->style);
6408       g_object_unref (clist_row->style);
6409     }
6410 
6411   if (clist_row->destroy)
6412     clist_row->destroy (clist_row->data);
6413 
6414   g_slice_free1 (sizeof (GtkCell) * clist->columns, clist_row->cell);
6415   g_slice_free (GtkCListRow, clist_row);
6416 }
6417 
6418 /* FOCUS FUNCTIONS
6419  *   gtk_clist_focus_content_area
6420  *   gtk_clist_focus
6421  *   gtk_clist_draw_focus
6422  *   gtk_clist_focus_in
6423  *   gtk_clist_focus_out
6424  *   title_focus
6425  */
6426 static void
gtk_clist_focus_content_area(GtkCList * clist)6427 gtk_clist_focus_content_area (GtkCList *clist)
6428 {
6429   if (clist->focus_row < 0)
6430     {
6431       clist->focus_row = 0;
6432 
6433       if ((clist->selection_mode == GTK_SELECTION_BROWSE ||
6434 	   clist->selection_mode == GTK_SELECTION_MULTIPLE) &&
6435 	  !clist->selection)
6436 	gtk_signal_emit (GTK_OBJECT (clist),
6437 			 clist_signals[SELECT_ROW],
6438 			 clist->focus_row, -1, NULL);
6439     }
6440   gtk_widget_grab_focus (GTK_WIDGET (clist));
6441 }
6442 
6443 static gboolean
gtk_clist_focus(GtkWidget * widget,GtkDirectionType direction)6444 gtk_clist_focus (GtkWidget        *widget,
6445 		 GtkDirectionType  direction)
6446 {
6447   GtkCList *clist = GTK_CLIST (widget);
6448   GtkWidget *focus_child;
6449   gboolean is_current_focus;
6450 
6451   if (!gtk_widget_is_sensitive (widget))
6452     return FALSE;
6453 
6454   focus_child = GTK_CONTAINER (widget)->focus_child;
6455 
6456   is_current_focus = gtk_widget_is_focus (GTK_WIDGET (clist));
6457 
6458   if (focus_child &&
6459       gtk_widget_child_focus (focus_child, direction))
6460     return TRUE;
6461 
6462   switch (direction)
6463     {
6464     case GTK_DIR_LEFT:
6465     case GTK_DIR_RIGHT:
6466       if (focus_child)
6467 	{
6468 	  if (title_focus_move (clist, direction))
6469 	    return TRUE;
6470 	}
6471       else if (!is_current_focus)
6472 	{
6473 	  gtk_clist_focus_content_area (clist);
6474 	  return TRUE;
6475 	}
6476       break;
6477     case GTK_DIR_DOWN:
6478     case GTK_DIR_TAB_FORWARD:
6479       if (!focus_child && !is_current_focus)
6480 	{
6481 	  if (title_focus_in (clist, direction))
6482 	    return TRUE;
6483 	}
6484 
6485       if (!is_current_focus && clist->rows)
6486 	{
6487 	  gtk_clist_focus_content_area (clist);
6488 	  return TRUE;
6489 	}
6490       break;
6491     case GTK_DIR_UP:
6492     case GTK_DIR_TAB_BACKWARD:
6493       if (!focus_child && is_current_focus)
6494 	{
6495 	  if (title_focus_in (clist, direction))
6496 	    return TRUE;
6497 	}
6498 
6499       if (!is_current_focus && !focus_child && clist->rows)
6500 	{
6501 	  gtk_clist_focus_content_area (clist);
6502 	  return TRUE;
6503 	}
6504       break;
6505     default:
6506       break;
6507     }
6508 
6509   return FALSE;
6510 }
6511 
6512 static void
gtk_clist_set_focus_child(GtkContainer * container,GtkWidget * child)6513 gtk_clist_set_focus_child (GtkContainer *container,
6514 			   GtkWidget    *child)
6515 {
6516   GtkCList *clist = GTK_CLIST (container);
6517   gint i;
6518 
6519   for (i = 0; i < clist->columns; i++)
6520     if (clist->column[i].button == child)
6521       clist->focus_header_column = i;
6522 
6523   parent_class->set_focus_child (container, child);
6524 }
6525 
6526 static void
gtk_clist_draw_focus(GtkWidget * widget)6527 gtk_clist_draw_focus (GtkWidget *widget)
6528 {
6529   GtkCList *clist;
6530 
6531   g_return_if_fail (GTK_IS_CLIST (widget));
6532 
6533   if (!GTK_WIDGET_DRAWABLE (widget) || !GTK_WIDGET_CAN_FOCUS (widget))
6534     return;
6535 
6536   clist = GTK_CLIST (widget);
6537   if (clist->focus_row >= 0)
6538     gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE,
6539 			0, ROW_TOP_YPIXEL(clist, clist->focus_row),
6540 			clist->clist_window_width - 1,
6541 			clist->row_height - 1);
6542 }
6543 
6544 static gint
gtk_clist_focus_in(GtkWidget * widget,GdkEventFocus * event)6545 gtk_clist_focus_in (GtkWidget     *widget,
6546 		    GdkEventFocus *event)
6547 {
6548   GtkCList *clist = GTK_CLIST (widget);
6549 
6550   if (clist->selection_mode == GTK_SELECTION_BROWSE &&
6551       clist->selection == NULL && clist->focus_row > -1)
6552     {
6553       GList *list;
6554 
6555       list = g_list_nth (clist->row_list, clist->focus_row);
6556       if (list && GTK_CLIST_ROW (list)->selectable)
6557 	gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
6558 			 clist->focus_row, -1, event);
6559       else
6560 	gtk_clist_draw_focus (widget);
6561     }
6562   else
6563     gtk_clist_draw_focus (widget);
6564 
6565   return FALSE;
6566 }
6567 
6568 static gint
gtk_clist_focus_out(GtkWidget * widget,GdkEventFocus * event)6569 gtk_clist_focus_out (GtkWidget     *widget,
6570 		     GdkEventFocus *event)
6571 {
6572   GtkCList *clist = GTK_CLIST (widget);
6573 
6574   gtk_clist_draw_focus (widget);
6575 
6576   GTK_CLIST_GET_CLASS (widget)->resync_selection (clist, (GdkEvent *) event);
6577 
6578   return FALSE;
6579 }
6580 
6581 static gboolean
focus_column(GtkCList * clist,gint column,gint dir)6582 focus_column (GtkCList *clist, gint column, gint dir)
6583 {
6584   GtkWidget *child = clist->column[column].button;
6585 
6586   if (gtk_widget_child_focus (child, dir))
6587     {
6588       return TRUE;
6589     }
6590   else if (GTK_WIDGET_CAN_FOCUS (child))
6591     {
6592       gtk_widget_grab_focus (child);
6593       return TRUE;
6594     }
6595 
6596   return FALSE;
6597 }
6598 
6599 /* Focus moved onto the headers. Focus first focusable and visible child.
6600  * (FIXME: focus the last focused child if visible)
6601  */
6602 static gboolean
title_focus_in(GtkCList * clist,gint dir)6603 title_focus_in (GtkCList *clist, gint dir)
6604 {
6605   gint i;
6606   gint left, right;
6607 
6608   if (!GTK_CLIST_SHOW_TITLES (clist))
6609     return FALSE;
6610 
6611   /* Check last focused column */
6612   if (clist->focus_header_column != -1)
6613     {
6614       i = clist->focus_header_column;
6615 
6616       left = COLUMN_LEFT_XPIXEL (clist, i);
6617       right = left + clist->column[i].area.width;
6618 
6619       if (left >= 0 && right <= clist->clist_window_width)
6620 	{
6621 	  if (focus_column (clist, i, dir))
6622 	    return TRUE;
6623 	}
6624     }
6625 
6626   /* Check fully visible columns */
6627   for (i = 0 ; i < clist->columns ; i++)
6628     {
6629       left = COLUMN_LEFT_XPIXEL (clist, i);
6630       right = left + clist->column[i].area.width;
6631 
6632       if (left >= 0 && right <= clist->clist_window_width)
6633 	{
6634 	  if (focus_column (clist, i, dir))
6635 	    return TRUE;
6636 	}
6637     }
6638 
6639   /* Check partially visible columns */
6640   for (i = 0 ; i < clist->columns ; i++)
6641     {
6642       left = COLUMN_LEFT_XPIXEL (clist, i);
6643       right = left + clist->column[i].area.width;
6644 
6645       if ((left < 0 && right > 0) ||
6646 	  (left < clist->clist_window_width && right > clist->clist_window_width))
6647 	{
6648 	  if (focus_column (clist, i, dir))
6649 	    return TRUE;
6650 	}
6651     }
6652 
6653   return FALSE;
6654 }
6655 
6656 /* Move the focus right or left within the title buttons, scrolling
6657  * as necessary to keep the focused child visible.
6658  */
6659 static gboolean
title_focus_move(GtkCList * clist,gint dir)6660 title_focus_move (GtkCList *clist,
6661 		  gint      dir)
6662 {
6663   GtkWidget *focus_child;
6664   gboolean return_val = FALSE;
6665   gint d = 0;
6666   gint i = -1;
6667   gint j;
6668 
6669   if (!GTK_CLIST_SHOW_TITLES(clist))
6670     return FALSE;
6671 
6672   focus_child = GTK_CONTAINER (clist)->focus_child;
6673   g_assert (focus_child);
6674 
6675   /* Movement direction within headers
6676    */
6677   switch (dir)
6678     {
6679     case GTK_DIR_RIGHT:
6680       d = 1;
6681       break;
6682     case GTK_DIR_LEFT:
6683       d = -1;
6684       break;
6685     }
6686 
6687   for (i = 0; i < clist->columns; i++)
6688     if (clist->column[i].button == focus_child)
6689       break;
6690 
6691   g_assert (i != -1);		/* Have a starting column */
6692 
6693   j = i + d;
6694   while (!return_val && j >= 0 && j < clist->columns)
6695     {
6696       if (clist->column[j].button &&
6697 	  gtk_widget_get_visible (clist->column[j].button))
6698 	{
6699 	  if (focus_column (clist, j, dir))
6700 	    {
6701 	      return_val = TRUE;
6702 	      break;
6703 	    }
6704 	}
6705       j += d;
6706     }
6707 
6708   /* If we didn't find it, wrap around and keep looking
6709    */
6710   if (!return_val)
6711     {
6712       j = d > 0 ? 0 : clist->columns - 1;
6713 
6714       while (!return_val && j != i)
6715 	{
6716 	  if (clist->column[j].button &&
6717 	      gtk_widget_get_visible (clist->column[j].button))
6718 	    {
6719 	      if (focus_column (clist, j, dir))
6720 		{
6721 		  return_val = TRUE;
6722 		  break;
6723 		}
6724 	    }
6725 	  j += d;
6726 	}
6727     }
6728 
6729   /* Scroll horizontally so focused column is visible
6730    */
6731   if (return_val)
6732     {
6733       if (COLUMN_LEFT_XPIXEL (clist, j) < CELL_SPACING + COLUMN_INSET)
6734 	gtk_clist_moveto (clist, -1, j, 0, 0);
6735       else if (COLUMN_LEFT_XPIXEL(clist, j) + clist->column[j].area.width >
6736 	       clist->clist_window_width)
6737 	{
6738 	  gint last_column;
6739 
6740 	  for (last_column = clist->columns - 1;
6741 	       last_column >= 0 && !clist->column[last_column].visible; last_column--);
6742 
6743 	  if (j == last_column)
6744 	    gtk_clist_moveto (clist, -1, j, 0, 0);
6745 	  else
6746 	    gtk_clist_moveto (clist, -1, j, 0, 1);
6747 	}
6748     }
6749   return TRUE;			/* Even if we didn't find a new one, we can keep the
6750 				 * focus in the same place.
6751 				 */
6752 }
6753 
6754 /* PRIVATE SCROLLING FUNCTIONS
6755  *   move_focus_row
6756  *   scroll_horizontal
6757  *   scroll_vertical
6758  *   move_horizontal
6759  *   move_vertical
6760  *   horizontal_timeout
6761  *   vertical_timeout
6762  *   remove_grab
6763  */
6764 static void
move_focus_row(GtkCList * clist,GtkScrollType scroll_type,gfloat position)6765 move_focus_row (GtkCList      *clist,
6766 		GtkScrollType  scroll_type,
6767 		gfloat         position)
6768 {
6769   GtkWidget *widget;
6770 
6771   g_return_if_fail (clist != 0);
6772   g_return_if_fail (GTK_IS_CLIST (clist));
6773 
6774   widget = GTK_WIDGET (clist);
6775 
6776   switch (scroll_type)
6777     {
6778     case GTK_SCROLL_STEP_UP:
6779     case GTK_SCROLL_STEP_BACKWARD:
6780       if (clist->focus_row <= 0)
6781 	return;
6782       gtk_clist_draw_focus (widget);
6783       clist->focus_row--;
6784       gtk_clist_draw_focus (widget);
6785       break;
6786 
6787     case GTK_SCROLL_STEP_DOWN:
6788     case GTK_SCROLL_STEP_FORWARD:
6789       if (clist->focus_row >= clist->rows - 1)
6790 	return;
6791       gtk_clist_draw_focus (widget);
6792       clist->focus_row++;
6793       gtk_clist_draw_focus (widget);
6794       break;
6795     case GTK_SCROLL_PAGE_UP:
6796     case GTK_SCROLL_PAGE_BACKWARD:
6797       if (clist->focus_row <= 0)
6798 	return;
6799       gtk_clist_draw_focus (widget);
6800       clist->focus_row = MAX (0, clist->focus_row -
6801 			      (2 * clist->clist_window_height -
6802 			       clist->row_height - CELL_SPACING) /
6803 			      (2 * (clist->row_height + CELL_SPACING)));
6804       gtk_clist_draw_focus (widget);
6805       break;
6806     case GTK_SCROLL_PAGE_DOWN:
6807     case GTK_SCROLL_PAGE_FORWARD:
6808       if (clist->focus_row >= clist->rows - 1)
6809 	return;
6810       gtk_clist_draw_focus (widget);
6811       clist->focus_row = MIN (clist->rows - 1, clist->focus_row +
6812 			      (2 * clist->clist_window_height -
6813 			       clist->row_height - CELL_SPACING) /
6814 			      (2 * (clist->row_height + CELL_SPACING)));
6815       gtk_clist_draw_focus (widget);
6816       break;
6817     case GTK_SCROLL_JUMP:
6818       if (position >= 0 && position <= 1)
6819 	{
6820 	  gtk_clist_draw_focus (widget);
6821 	  clist->focus_row = position * (clist->rows - 1);
6822 	  gtk_clist_draw_focus (widget);
6823 	}
6824       break;
6825     default:
6826       break;
6827     }
6828 }
6829 
6830 static void
scroll_horizontal(GtkCList * clist,GtkScrollType scroll_type,gfloat position)6831 scroll_horizontal (GtkCList      *clist,
6832 		   GtkScrollType  scroll_type,
6833 		   gfloat         position)
6834 {
6835   gint column = 0;
6836   gint last_column;
6837 
6838   g_return_if_fail (clist != 0);
6839   g_return_if_fail (GTK_IS_CLIST (clist));
6840 
6841   if (clist_has_grab (clist))
6842     return;
6843 
6844   for (last_column = clist->columns - 1;
6845        last_column >= 0 && !clist->column[last_column].visible; last_column--)
6846     ;
6847 
6848   switch (scroll_type)
6849     {
6850     case GTK_SCROLL_STEP_BACKWARD:
6851       column = COLUMN_FROM_XPIXEL (clist, 0);
6852       if (COLUMN_LEFT_XPIXEL (clist, column) - CELL_SPACING - COLUMN_INSET >= 0
6853 	  && column > 0)
6854 	column--;
6855       break;
6856     case GTK_SCROLL_STEP_FORWARD:
6857       column = COLUMN_FROM_XPIXEL (clist, clist->clist_window_width);
6858       if (column < 0)
6859 	return;
6860       if (COLUMN_LEFT_XPIXEL (clist, column) +
6861 	  clist->column[column].area.width +
6862 	  CELL_SPACING + COLUMN_INSET - 1 <= clist->clist_window_width &&
6863 	  column < last_column)
6864 	column++;
6865       break;
6866     case GTK_SCROLL_PAGE_BACKWARD:
6867     case GTK_SCROLL_PAGE_FORWARD:
6868       return;
6869     case GTK_SCROLL_JUMP:
6870       if (position >= 0 && position <= 1)
6871 	{
6872 	  gint vis_columns = 0;
6873 	  gint i;
6874 
6875 	  for (i = 0; i <= last_column; i++)
6876  	    if (clist->column[i].visible)
6877 	      vis_columns++;
6878 
6879 	  column = position * vis_columns;
6880 
6881 	  for (i = 0; i <= last_column && column > 0; i++)
6882 	    if (clist->column[i].visible)
6883 	      column--;
6884 
6885 	  column = i;
6886 	}
6887       else
6888 	return;
6889       break;
6890     default:
6891       break;
6892     }
6893 
6894   if (COLUMN_LEFT_XPIXEL (clist, column) < CELL_SPACING + COLUMN_INSET)
6895     gtk_clist_moveto (clist, -1, column, 0, 0);
6896   else if (COLUMN_LEFT_XPIXEL (clist, column) + CELL_SPACING + COLUMN_INSET - 1
6897 	   + clist->column[column].area.width > clist->clist_window_width)
6898     {
6899       if (column == last_column)
6900 	gtk_clist_moveto (clist, -1, column, 0, 0);
6901       else
6902 	gtk_clist_moveto (clist, -1, column, 0, 1);
6903     }
6904 }
6905 
6906 static void
scroll_vertical(GtkCList * clist,GtkScrollType scroll_type,gfloat position)6907 scroll_vertical (GtkCList      *clist,
6908 		 GtkScrollType  scroll_type,
6909 		 gfloat         position)
6910 {
6911   gint old_focus_row;
6912 
6913   g_return_if_fail (GTK_IS_CLIST (clist));
6914 
6915   if (clist_has_grab (clist))
6916     return;
6917 
6918   switch (clist->selection_mode)
6919     {
6920     case GTK_SELECTION_MULTIPLE:
6921       if (clist->anchor >= 0)
6922 	return;
6923     case GTK_SELECTION_BROWSE:
6924 
6925       old_focus_row = clist->focus_row;
6926       move_focus_row (clist, scroll_type, position);
6927 
6928       if (old_focus_row != clist->focus_row)
6929 	{
6930 	  if (clist->selection_mode == GTK_SELECTION_BROWSE)
6931 	    gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW],
6932 			     old_focus_row, -1, NULL);
6933 	  else if (!GTK_CLIST_ADD_MODE(clist))
6934 	    {
6935 	      gtk_clist_unselect_all (clist);
6936 	      clist->undo_anchor = old_focus_row;
6937 	    }
6938 	}
6939 
6940       switch (gtk_clist_row_is_visible (clist, clist->focus_row))
6941 	{
6942 	case GTK_VISIBILITY_NONE:
6943 	  if (old_focus_row != clist->focus_row &&
6944 	      !(clist->selection_mode == GTK_SELECTION_MULTIPLE &&
6945 		GTK_CLIST_ADD_MODE(clist)))
6946 	    gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
6947 			     clist->focus_row, -1, NULL);
6948 	  switch (scroll_type)
6949 	    {
6950             case GTK_SCROLL_PAGE_UP:
6951             case GTK_SCROLL_STEP_UP:
6952 	    case GTK_SCROLL_STEP_BACKWARD:
6953 	    case GTK_SCROLL_PAGE_BACKWARD:
6954 	      gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
6955 	      break;
6956             case GTK_SCROLL_PAGE_DOWN:
6957             case GTK_SCROLL_STEP_DOWN:
6958 	    case GTK_SCROLL_STEP_FORWARD:
6959 	    case GTK_SCROLL_PAGE_FORWARD:
6960 	      gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
6961 	      break;
6962 	    case GTK_SCROLL_JUMP:
6963 	      gtk_clist_moveto (clist, clist->focus_row, -1, 0.5, 0);
6964 	      break;
6965 	    default:
6966 	      break;
6967 	    }
6968 	  break;
6969 	case GTK_VISIBILITY_PARTIAL:
6970 	  switch (scroll_type)
6971 	    {
6972 	    case GTK_SCROLL_STEP_BACKWARD:
6973 	    case GTK_SCROLL_PAGE_BACKWARD:
6974 	      gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
6975 	      break;
6976 	    case GTK_SCROLL_STEP_FORWARD:
6977 	    case GTK_SCROLL_PAGE_FORWARD:
6978 	      gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
6979 	      break;
6980 	    case GTK_SCROLL_JUMP:
6981 	      gtk_clist_moveto (clist, clist->focus_row, -1, 0.5, 0);
6982 	      break;
6983 	    default:
6984 	      break;
6985 	    }
6986 	default:
6987 	  if (old_focus_row != clist->focus_row &&
6988 	      !(clist->selection_mode == GTK_SELECTION_MULTIPLE &&
6989 		GTK_CLIST_ADD_MODE(clist)))
6990 	    gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
6991 			     clist->focus_row, -1, NULL);
6992 	  break;
6993 	}
6994       break;
6995     default:
6996       move_focus_row (clist, scroll_type, position);
6997 
6998       if (ROW_TOP_YPIXEL (clist, clist->focus_row) + clist->row_height >
6999 	  clist->clist_window_height)
7000 	gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
7001       else if (ROW_TOP_YPIXEL (clist, clist->focus_row) < 0)
7002 	gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
7003       break;
7004     }
7005 }
7006 
7007 static void
move_horizontal(GtkCList * clist,gint diff)7008 move_horizontal (GtkCList *clist,
7009 		 gint      diff)
7010 {
7011   gdouble value;
7012 
7013   if (!clist->hadjustment)
7014     return;
7015 
7016   value = CLAMP (clist->hadjustment->value + diff, 0.0,
7017 		 clist->hadjustment->upper - clist->hadjustment->page_size);
7018   gtk_adjustment_set_value (clist->hadjustment, value);
7019 }
7020 
7021 static void
move_vertical(GtkCList * clist,gint row,gfloat align)7022 move_vertical (GtkCList *clist,
7023 	       gint      row,
7024 	       gfloat    align)
7025 {
7026   gdouble value;
7027 
7028   if (!clist->vadjustment)
7029     return;
7030 
7031   value = (ROW_TOP_YPIXEL (clist, row) - clist->voffset -
7032 	   align * (clist->clist_window_height - clist->row_height) +
7033 	   (2 * align - 1) * CELL_SPACING);
7034 
7035   if (value + clist->vadjustment->page_size > clist->vadjustment->upper)
7036     value = clist->vadjustment->upper - clist->vadjustment->page_size;
7037 
7038   gtk_adjustment_set_value (clist->vadjustment, value);
7039 }
7040 
7041 static void
do_fake_motion(GtkWidget * widget)7042 do_fake_motion (GtkWidget *widget)
7043 {
7044   GdkEvent *event = gdk_event_new (GDK_MOTION_NOTIFY);
7045 
7046   event->motion.send_event = TRUE;
7047 
7048   gtk_clist_motion (widget, (GdkEventMotion *)event);
7049   gdk_event_free (event);
7050 }
7051 
7052 static gint
horizontal_timeout(GtkCList * clist)7053 horizontal_timeout (GtkCList *clist)
7054 {
7055   clist->htimer = 0;
7056   do_fake_motion (GTK_WIDGET (clist));
7057 
7058   return FALSE;
7059 }
7060 
7061 static gint
vertical_timeout(GtkCList * clist)7062 vertical_timeout (GtkCList *clist)
7063 {
7064   clist->vtimer = 0;
7065   do_fake_motion (GTK_WIDGET (clist));
7066 
7067   return FALSE;
7068 }
7069 
7070 static void
remove_grab(GtkCList * clist)7071 remove_grab (GtkCList *clist)
7072 {
7073   GtkWidget *widget = GTK_WIDGET (clist);
7074 
7075   if (GTK_WIDGET_HAS_GRAB (clist))
7076     {
7077       GdkDisplay *display = gtk_widget_get_display (widget);
7078 
7079       gtk_grab_remove (widget);
7080       if (gdk_display_pointer_is_grabbed (display))
7081 	gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
7082     }
7083 
7084   if (clist->htimer)
7085     {
7086       g_source_remove (clist->htimer);
7087       clist->htimer = 0;
7088     }
7089 
7090   if (clist->vtimer)
7091     {
7092       g_source_remove (clist->vtimer);
7093       clist->vtimer = 0;
7094     }
7095 }
7096 
7097 /* PUBLIC SORTING FUNCTIONS
7098  * gtk_clist_sort
7099  * gtk_clist_set_compare_func
7100  * gtk_clist_set_auto_sort
7101  * gtk_clist_set_sort_type
7102  * gtk_clist_set_sort_column
7103  */
7104 void
gtk_clist_sort(GtkCList * clist)7105 gtk_clist_sort (GtkCList *clist)
7106 {
7107   g_return_if_fail (GTK_IS_CLIST (clist));
7108 
7109   GTK_CLIST_GET_CLASS (clist)->sort_list (clist);
7110 }
7111 
7112 void
gtk_clist_set_compare_func(GtkCList * clist,GtkCListCompareFunc cmp_func)7113 gtk_clist_set_compare_func (GtkCList            *clist,
7114 			    GtkCListCompareFunc  cmp_func)
7115 {
7116   g_return_if_fail (GTK_IS_CLIST (clist));
7117 
7118   clist->compare = (cmp_func) ? cmp_func : default_compare;
7119 }
7120 
7121 void
gtk_clist_set_auto_sort(GtkCList * clist,gboolean auto_sort)7122 gtk_clist_set_auto_sort (GtkCList *clist,
7123 			 gboolean  auto_sort)
7124 {
7125   g_return_if_fail (GTK_IS_CLIST (clist));
7126 
7127   if (GTK_CLIST_AUTO_SORT(clist) && !auto_sort)
7128     GTK_CLIST_UNSET_FLAG (clist, CLIST_AUTO_SORT);
7129   else if (!GTK_CLIST_AUTO_SORT(clist) && auto_sort)
7130     {
7131       GTK_CLIST_SET_FLAG (clist, CLIST_AUTO_SORT);
7132       gtk_clist_sort (clist);
7133     }
7134 }
7135 
7136 void
gtk_clist_set_sort_type(GtkCList * clist,GtkSortType sort_type)7137 gtk_clist_set_sort_type (GtkCList    *clist,
7138 			 GtkSortType  sort_type)
7139 {
7140   g_return_if_fail (GTK_IS_CLIST (clist));
7141 
7142   clist->sort_type = sort_type;
7143 }
7144 
7145 void
gtk_clist_set_sort_column(GtkCList * clist,gint column)7146 gtk_clist_set_sort_column (GtkCList *clist,
7147 			   gint      column)
7148 {
7149   g_return_if_fail (GTK_IS_CLIST (clist));
7150 
7151   if (column < 0 || column >= clist->columns)
7152     return;
7153 
7154   clist->sort_column = column;
7155 }
7156 
7157 /* PRIVATE SORTING FUNCTIONS
7158  *   default_compare
7159  *   real_sort_list
7160  *   gtk_clist_merge
7161  *   gtk_clist_mergesort
7162  */
7163 static gint
default_compare(GtkCList * clist,gconstpointer ptr1,gconstpointer ptr2)7164 default_compare (GtkCList      *clist,
7165 		 gconstpointer  ptr1,
7166 		 gconstpointer  ptr2)
7167 {
7168   char *text1 = NULL;
7169   char *text2 = NULL;
7170 
7171   GtkCListRow *row1 = (GtkCListRow *) ptr1;
7172   GtkCListRow *row2 = (GtkCListRow *) ptr2;
7173 
7174   switch (row1->cell[clist->sort_column].type)
7175     {
7176     case GTK_CELL_TEXT:
7177       text1 = GTK_CELL_TEXT (row1->cell[clist->sort_column])->text;
7178       break;
7179     case GTK_CELL_PIXTEXT:
7180       text1 = GTK_CELL_PIXTEXT (row1->cell[clist->sort_column])->text;
7181       break;
7182     default:
7183       break;
7184     }
7185 
7186   switch (row2->cell[clist->sort_column].type)
7187     {
7188     case GTK_CELL_TEXT:
7189       text2 = GTK_CELL_TEXT (row2->cell[clist->sort_column])->text;
7190       break;
7191     case GTK_CELL_PIXTEXT:
7192       text2 = GTK_CELL_PIXTEXT (row2->cell[clist->sort_column])->text;
7193       break;
7194     default:
7195       break;
7196     }
7197 
7198   if (!text2)
7199     return (text1 != NULL);
7200 
7201   if (!text1)
7202     return -1;
7203 
7204   return strcmp (text1, text2);
7205 }
7206 
7207 static void
real_sort_list(GtkCList * clist)7208 real_sort_list (GtkCList *clist)
7209 {
7210   GList *list;
7211   GList *work;
7212   gint i;
7213 
7214   g_return_if_fail (GTK_IS_CLIST (clist));
7215 
7216   if (clist->rows <= 1)
7217     return;
7218 
7219   if (clist_has_grab (clist))
7220     return;
7221 
7222   gtk_clist_freeze (clist);
7223 
7224   if (clist->anchor != -1 && clist->selection_mode == GTK_SELECTION_MULTIPLE)
7225     {
7226       GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
7227       g_list_free (clist->undo_selection);
7228       g_list_free (clist->undo_unselection);
7229       clist->undo_selection = NULL;
7230       clist->undo_unselection = NULL;
7231     }
7232 
7233   clist->row_list = gtk_clist_mergesort (clist, clist->row_list, clist->rows);
7234 
7235   work = clist->selection;
7236 
7237   for (i = 0, list = clist->row_list; i < clist->rows; i++, list = list->next)
7238     {
7239       if (GTK_CLIST_ROW (list)->state == GTK_STATE_SELECTED)
7240 	{
7241 	  work->data = GINT_TO_POINTER (i);
7242 	  work = work->next;
7243 	}
7244 
7245       if (i == clist->rows - 1)
7246 	clist->row_list_end = list;
7247     }
7248 
7249   gtk_clist_thaw (clist);
7250 }
7251 
7252 static GList *
gtk_clist_merge(GtkCList * clist,GList * a,GList * b)7253 gtk_clist_merge (GtkCList *clist,
7254 		 GList    *a,         /* first list to merge */
7255 		 GList    *b)         /* second list to merge */
7256 {
7257   GList z = { 0 };                    /* auxiliary node */
7258   GList *c;
7259   gint cmp;
7260 
7261   c = &z;
7262 
7263   while (a || b)
7264     {
7265       if (a && !b)
7266 	{
7267 	  c->next = a;
7268 	  a->prev = c;
7269 	  c = a;
7270 	  a = a->next;
7271 	  break;
7272 	}
7273       else if (!a && b)
7274 	{
7275 	  c->next = b;
7276 	  b->prev = c;
7277 	  c = b;
7278 	  b = b->next;
7279 	  break;
7280 	}
7281       else /* a && b */
7282 	{
7283 	  cmp = clist->compare (clist, GTK_CLIST_ROW (a), GTK_CLIST_ROW (b));
7284 	  if ((cmp >= 0 && clist->sort_type == GTK_SORT_DESCENDING) ||
7285 	      (cmp <= 0 && clist->sort_type == GTK_SORT_ASCENDING) ||
7286 	      (a && !b))
7287 	    {
7288 	      c->next = a;
7289 	      a->prev = c;
7290 	      c = a;
7291 	      a = a->next;
7292 	    }
7293 	  else
7294 	    {
7295 	      c->next = b;
7296 	      b->prev = c;
7297 	      c = b;
7298 	      b = b->next;
7299 	    }
7300 	}
7301     }
7302 
7303   z.next->prev = NULL;
7304   return z.next;
7305 }
7306 
7307 static GList *
gtk_clist_mergesort(GtkCList * clist,GList * list,gint num)7308 gtk_clist_mergesort (GtkCList *clist,
7309 		     GList    *list,         /* the list to sort */
7310 		     gint      num)          /* the list's length */
7311 {
7312   GList *half;
7313   gint i;
7314 
7315   if (num <= 1)
7316     {
7317       return list;
7318     }
7319   else
7320     {
7321       /* move "half" to the middle */
7322       half = list;
7323       for (i = 0; i < num / 2; i++)
7324 	half = half->next;
7325 
7326       /* cut the list in two */
7327       half->prev->next = NULL;
7328       half->prev = NULL;
7329 
7330       /* recursively sort both lists */
7331       return gtk_clist_merge (clist,
7332 		       gtk_clist_mergesort (clist, list, num / 2),
7333 		       gtk_clist_mergesort (clist, half, num - num / 2));
7334     }
7335 }
7336 
7337 /************************/
7338 
7339 static void
drag_source_info_destroy(gpointer data)7340 drag_source_info_destroy (gpointer data)
7341 {
7342   GtkCListCellInfo *info = data;
7343 
7344   g_free (info);
7345 }
7346 
7347 static void
drag_dest_info_destroy(gpointer data)7348 drag_dest_info_destroy (gpointer data)
7349 {
7350   GtkCListDestInfo *info = data;
7351 
7352   g_free (info);
7353 }
7354 
7355 static void
drag_dest_cell(GtkCList * clist,gint x,gint y,GtkCListDestInfo * dest_info)7356 drag_dest_cell (GtkCList         *clist,
7357 		gint              x,
7358 		gint              y,
7359 		GtkCListDestInfo *dest_info)
7360 {
7361   GtkWidget *widget;
7362 
7363   widget = GTK_WIDGET (clist);
7364 
7365   dest_info->insert_pos = GTK_CLIST_DRAG_NONE;
7366 
7367   y -= (GTK_CONTAINER (clist)->border_width +
7368 	widget->style->ythickness +
7369 	clist->column_title_area.height);
7370 
7371   dest_info->cell.row = ROW_FROM_YPIXEL (clist, y);
7372   if (dest_info->cell.row >= clist->rows)
7373     {
7374       dest_info->cell.row = clist->rows - 1;
7375       y = ROW_TOP_YPIXEL (clist, dest_info->cell.row) + clist->row_height;
7376     }
7377   if (dest_info->cell.row < -1)
7378     dest_info->cell.row = -1;
7379 
7380   x -= GTK_CONTAINER (widget)->border_width + widget->style->xthickness;
7381 
7382   dest_info->cell.column = COLUMN_FROM_XPIXEL (clist, x);
7383 
7384   if (dest_info->cell.row >= 0)
7385     {
7386       gint y_delta;
7387       gint h = 0;
7388 
7389       y_delta = y - ROW_TOP_YPIXEL (clist, dest_info->cell.row);
7390 
7391       if (GTK_CLIST_DRAW_DRAG_RECT(clist))
7392 	{
7393 	  dest_info->insert_pos = GTK_CLIST_DRAG_INTO;
7394 	  h = clist->row_height / 4;
7395 	}
7396       else if (GTK_CLIST_DRAW_DRAG_LINE(clist))
7397 	{
7398 	  dest_info->insert_pos = GTK_CLIST_DRAG_BEFORE;
7399 	  h = clist->row_height / 2;
7400 	}
7401 
7402       if (GTK_CLIST_DRAW_DRAG_LINE(clist))
7403 	{
7404 	  if (y_delta < h)
7405 	    dest_info->insert_pos = GTK_CLIST_DRAG_BEFORE;
7406 	  else if (clist->row_height - y_delta < h)
7407 	    dest_info->insert_pos = GTK_CLIST_DRAG_AFTER;
7408 	}
7409     }
7410 }
7411 
7412 static void
gtk_clist_drag_begin(GtkWidget * widget,GdkDragContext * context)7413 gtk_clist_drag_begin (GtkWidget	     *widget,
7414 		      GdkDragContext *context)
7415 {
7416   GtkCList *clist;
7417   GtkCListCellInfo *info;
7418 
7419   g_return_if_fail (GTK_IS_CLIST (widget));
7420   g_return_if_fail (context != NULL);
7421 
7422   clist = GTK_CLIST (widget);
7423 
7424   clist->drag_button = 0;
7425   remove_grab (clist);
7426 
7427   switch (clist->selection_mode)
7428     {
7429     case GTK_SELECTION_MULTIPLE:
7430       update_extended_selection (clist, clist->focus_row);
7431       GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
7432       break;
7433     case GTK_SELECTION_SINGLE:
7434       clist->anchor = -1;
7435     case GTK_SELECTION_BROWSE:
7436       break;
7437     default:
7438       g_assert_not_reached ();
7439     }
7440 
7441   info = g_dataset_get_data (context, "gtk-clist-drag-source");
7442 
7443   if (!info)
7444     {
7445       info = g_new (GtkCListCellInfo, 1);
7446 
7447       if (clist->click_cell.row < 0)
7448 	clist->click_cell.row = 0;
7449       else if (clist->click_cell.row >= clist->rows)
7450 	clist->click_cell.row = clist->rows - 1;
7451       info->row = clist->click_cell.row;
7452       info->column = clist->click_cell.column;
7453 
7454       g_dataset_set_data_full (context, "gtk-clist-drag-source", info,
7455 			       drag_source_info_destroy);
7456     }
7457 
7458   if (GTK_CLIST_USE_DRAG_ICONS (clist))
7459     gtk_drag_set_icon_default (context);
7460 }
7461 
7462 static void
gtk_clist_drag_end(GtkWidget * widget,GdkDragContext * context)7463 gtk_clist_drag_end (GtkWidget	   *widget,
7464 		    GdkDragContext *context)
7465 {
7466   GtkCList *clist;
7467 
7468   g_return_if_fail (GTK_IS_CLIST (widget));
7469   g_return_if_fail (context != NULL);
7470 
7471   clist = GTK_CLIST (widget);
7472 
7473   clist->click_cell.row = -1;
7474   clist->click_cell.column = -1;
7475 }
7476 
7477 static void
gtk_clist_drag_leave(GtkWidget * widget,GdkDragContext * context,guint time)7478 gtk_clist_drag_leave (GtkWidget      *widget,
7479 		      GdkDragContext *context,
7480 		      guint           time)
7481 {
7482   GtkCList *clist;
7483   GtkCListDestInfo *dest_info;
7484 
7485   g_return_if_fail (GTK_IS_CLIST (widget));
7486   g_return_if_fail (context != NULL);
7487 
7488   clist = GTK_CLIST (widget);
7489 
7490   dest_info = g_dataset_get_data (context, "gtk-clist-drag-dest");
7491 
7492   if (dest_info)
7493     {
7494       if (dest_info->cell.row >= 0 &&
7495 	  GTK_CLIST_REORDERABLE(clist) &&
7496 	  gtk_drag_get_source_widget (context) == widget)
7497 	{
7498 	  GList *list;
7499 	  GdkAtom atom = gdk_atom_intern_static_string ("gtk-clist-drag-reorder");
7500 
7501 	  list = context->targets;
7502 	  while (list)
7503 	    {
7504 	      if (atom == GDK_POINTER_TO_ATOM (list->data))
7505 		{
7506 		  GTK_CLIST_GET_CLASS (clist)->draw_drag_highlight
7507 		    (clist,
7508 		     g_list_nth (clist->row_list, dest_info->cell.row)->data,
7509 		     dest_info->cell.row, dest_info->insert_pos);
7510 		  clist->drag_highlight_row = -1;
7511 		  break;
7512 		}
7513 	      list = list->next;
7514 	    }
7515 	}
7516       g_dataset_remove_data (context, "gtk-clist-drag-dest");
7517     }
7518 }
7519 
7520 static gint
gtk_clist_drag_motion(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint time)7521 gtk_clist_drag_motion (GtkWidget      *widget,
7522 		       GdkDragContext *context,
7523 		       gint            x,
7524 		       gint            y,
7525 		       guint           time)
7526 {
7527   GtkCList *clist;
7528   GtkCListDestInfo new_info;
7529   GtkCListDestInfo *dest_info;
7530 
7531   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
7532 
7533   clist = GTK_CLIST (widget);
7534 
7535   dest_info = g_dataset_get_data (context, "gtk-clist-drag-dest");
7536 
7537   if (!dest_info)
7538     {
7539       dest_info = g_new (GtkCListDestInfo, 1);
7540 
7541       dest_info->insert_pos  = GTK_CLIST_DRAG_NONE;
7542       dest_info->cell.row    = -1;
7543       dest_info->cell.column = -1;
7544 
7545       g_dataset_set_data_full (context, "gtk-clist-drag-dest", dest_info,
7546 			       drag_dest_info_destroy);
7547     }
7548 
7549   drag_dest_cell (clist, x, y, &new_info);
7550 
7551   if (GTK_CLIST_REORDERABLE (clist))
7552     {
7553       GList *list;
7554       GdkAtom atom = gdk_atom_intern_static_string ("gtk-clist-drag-reorder");
7555 
7556       list = context->targets;
7557       while (list)
7558 	{
7559 	  if (atom == GDK_POINTER_TO_ATOM (list->data))
7560 	    break;
7561 	  list = list->next;
7562 	}
7563 
7564       if (list)
7565 	{
7566 	  if (gtk_drag_get_source_widget (context) != widget ||
7567 	      new_info.insert_pos == GTK_CLIST_DRAG_NONE ||
7568 	      new_info.cell.row == clist->click_cell.row ||
7569 	      (new_info.cell.row == clist->click_cell.row - 1 &&
7570 	       new_info.insert_pos == GTK_CLIST_DRAG_AFTER) ||
7571 	      (new_info.cell.row == clist->click_cell.row + 1 &&
7572 	       new_info.insert_pos == GTK_CLIST_DRAG_BEFORE))
7573 	    {
7574 	      if (dest_info->cell.row < 0)
7575 		{
7576 		  gdk_drag_status (context, GDK_ACTION_DEFAULT, time);
7577 		  return FALSE;
7578 		}
7579 	      return TRUE;
7580 	    }
7581 
7582 	  if (new_info.cell.row != dest_info->cell.row ||
7583 	      (new_info.cell.row == dest_info->cell.row &&
7584 	       dest_info->insert_pos != new_info.insert_pos))
7585 	    {
7586 	      if (dest_info->cell.row >= 0)
7587 		GTK_CLIST_GET_CLASS (clist)->draw_drag_highlight
7588 		  (clist, g_list_nth (clist->row_list,
7589 				      dest_info->cell.row)->data,
7590 		   dest_info->cell.row, dest_info->insert_pos);
7591 
7592 	      dest_info->insert_pos  = new_info.insert_pos;
7593 	      dest_info->cell.row    = new_info.cell.row;
7594 	      dest_info->cell.column = new_info.cell.column;
7595 
7596 	      GTK_CLIST_GET_CLASS (clist)->draw_drag_highlight
7597 		(clist, g_list_nth (clist->row_list,
7598 				    dest_info->cell.row)->data,
7599 		 dest_info->cell.row, dest_info->insert_pos);
7600 
7601 	      clist->drag_highlight_row = dest_info->cell.row;
7602 	      clist->drag_highlight_pos = dest_info->insert_pos;
7603 
7604 	      gdk_drag_status (context, context->suggested_action, time);
7605 	    }
7606 	  return TRUE;
7607 	}
7608     }
7609 
7610   dest_info->insert_pos  = new_info.insert_pos;
7611   dest_info->cell.row    = new_info.cell.row;
7612   dest_info->cell.column = new_info.cell.column;
7613   return TRUE;
7614 }
7615 
7616 static gboolean
gtk_clist_drag_drop(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint time)7617 gtk_clist_drag_drop (GtkWidget      *widget,
7618 		     GdkDragContext *context,
7619 		     gint            x,
7620 		     gint            y,
7621 		     guint           time)
7622 {
7623   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
7624   g_return_val_if_fail (context != NULL, FALSE);
7625 
7626   if (GTK_CLIST_REORDERABLE (widget) &&
7627       gtk_drag_get_source_widget (context) == widget)
7628     {
7629       GList *list;
7630       GdkAtom atom = gdk_atom_intern_static_string ("gtk-clist-drag-reorder");
7631 
7632       list = context->targets;
7633       while (list)
7634 	{
7635 	  if (atom == GDK_POINTER_TO_ATOM (list->data))
7636 	    return TRUE;
7637 	  list = list->next;
7638 	}
7639     }
7640   return FALSE;
7641 }
7642 
7643 static void
gtk_clist_drag_data_received(GtkWidget * widget,GdkDragContext * context,gint x,gint y,GtkSelectionData * selection_data,guint info,guint time)7644 gtk_clist_drag_data_received (GtkWidget        *widget,
7645 			      GdkDragContext   *context,
7646 			      gint              x,
7647 			      gint              y,
7648 			      GtkSelectionData *selection_data,
7649 			      guint             info,
7650 			      guint             time)
7651 {
7652   GtkCList *clist;
7653 
7654   g_return_if_fail (GTK_IS_CLIST (widget));
7655   g_return_if_fail (context != NULL);
7656   g_return_if_fail (selection_data != NULL);
7657 
7658   clist = GTK_CLIST (widget);
7659 
7660   if (GTK_CLIST_REORDERABLE (clist) &&
7661       gtk_drag_get_source_widget (context) == widget &&
7662       selection_data->target ==
7663       gdk_atom_intern_static_string ("gtk-clist-drag-reorder") &&
7664       selection_data->format == 8 &&
7665       selection_data->length == sizeof (GtkCListCellInfo))
7666     {
7667       GtkCListCellInfo *source_info;
7668 
7669       source_info = (GtkCListCellInfo *)(selection_data->data);
7670       if (source_info)
7671 	{
7672 	  GtkCListDestInfo dest_info;
7673 
7674 	  drag_dest_cell (clist, x, y, &dest_info);
7675 
7676 	  if (dest_info.insert_pos == GTK_CLIST_DRAG_AFTER)
7677 	    dest_info.cell.row++;
7678 	  if (source_info->row < dest_info.cell.row)
7679 	    dest_info.cell.row--;
7680 	  if (dest_info.cell.row != source_info->row)
7681 	    gtk_clist_row_move (clist, source_info->row, dest_info.cell.row);
7682 
7683 	  g_dataset_remove_data (context, "gtk-clist-drag-dest");
7684 	}
7685     }
7686 }
7687 
7688 static void
gtk_clist_drag_data_get(GtkWidget * widget,GdkDragContext * context,GtkSelectionData * selection_data,guint info,guint time)7689 gtk_clist_drag_data_get (GtkWidget        *widget,
7690 			 GdkDragContext   *context,
7691 			 GtkSelectionData *selection_data,
7692 			 guint             info,
7693 			 guint             time)
7694 {
7695   g_return_if_fail (GTK_IS_CLIST (widget));
7696   g_return_if_fail (context != NULL);
7697   g_return_if_fail (selection_data != NULL);
7698 
7699   if (selection_data->target == gdk_atom_intern_static_string ("gtk-clist-drag-reorder"))
7700     {
7701       GtkCListCellInfo *info;
7702 
7703       info = g_dataset_get_data (context, "gtk-clist-drag-source");
7704 
7705       if (info)
7706 	{
7707 	  GtkCListCellInfo ret_info;
7708 
7709 	  ret_info.row = info->row;
7710 	  ret_info.column = info->column;
7711 
7712 	  gtk_selection_data_set (selection_data, selection_data->target,
7713 				  8, (guchar *) &ret_info,
7714 				  sizeof (GtkCListCellInfo));
7715 	}
7716     }
7717 }
7718 
7719 static void
draw_drag_highlight(GtkCList * clist,GtkCListRow * dest_row,gint dest_row_number,GtkCListDragPos drag_pos)7720 draw_drag_highlight (GtkCList        *clist,
7721 		     GtkCListRow     *dest_row,
7722 		     gint             dest_row_number,
7723 		     GtkCListDragPos  drag_pos)
7724 {
7725   gint y;
7726 
7727   y = ROW_TOP_YPIXEL (clist, dest_row_number) - 1;
7728 
7729   switch (drag_pos)
7730     {
7731     case GTK_CLIST_DRAG_NONE:
7732       break;
7733     case GTK_CLIST_DRAG_AFTER:
7734       y += clist->row_height + 1;
7735     case GTK_CLIST_DRAG_BEFORE:
7736       gdk_draw_line (clist->clist_window, clist->xor_gc,
7737 		     0, y, clist->clist_window_width, y);
7738       break;
7739     case GTK_CLIST_DRAG_INTO:
7740       gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE, 0, y,
7741 			  clist->clist_window_width - 1, clist->row_height);
7742       break;
7743     }
7744 }
7745 
7746 void
gtk_clist_set_reorderable(GtkCList * clist,gboolean reorderable)7747 gtk_clist_set_reorderable (GtkCList *clist,
7748 			   gboolean  reorderable)
7749 {
7750   GtkWidget *widget;
7751 
7752   g_return_if_fail (GTK_IS_CLIST (clist));
7753 
7754   if ((GTK_CLIST_REORDERABLE(clist) != 0) == reorderable)
7755     return;
7756 
7757   widget = GTK_WIDGET (clist);
7758 
7759   if (reorderable)
7760     {
7761       GTK_CLIST_SET_FLAG (clist, CLIST_REORDERABLE);
7762       gtk_drag_dest_set (widget,
7763 			 GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
7764 			 &clist_target_table, 1, GDK_ACTION_MOVE);
7765     }
7766   else
7767     {
7768       GTK_CLIST_UNSET_FLAG (clist, CLIST_REORDERABLE);
7769       gtk_drag_dest_unset (GTK_WIDGET (clist));
7770     }
7771 }
7772 
7773 void
gtk_clist_set_use_drag_icons(GtkCList * clist,gboolean use_icons)7774 gtk_clist_set_use_drag_icons (GtkCList *clist,
7775 			      gboolean  use_icons)
7776 {
7777   g_return_if_fail (GTK_IS_CLIST (clist));
7778 
7779   if (use_icons != 0)
7780     GTK_CLIST_SET_FLAG (clist, CLIST_USE_DRAG_ICONS);
7781   else
7782     GTK_CLIST_UNSET_FLAG (clist, CLIST_USE_DRAG_ICONS);
7783 }
7784 
7785 void
gtk_clist_set_button_actions(GtkCList * clist,guint button,guint8 button_actions)7786 gtk_clist_set_button_actions (GtkCList *clist,
7787 			      guint     button,
7788 			      guint8    button_actions)
7789 {
7790   g_return_if_fail (GTK_IS_CLIST (clist));
7791 
7792   if (button < MAX_BUTTON)
7793     {
7794       if (gdk_display_pointer_is_grabbed (gtk_widget_get_display (GTK_WIDGET (clist))) ||
7795 	  GTK_WIDGET_HAS_GRAB (clist))
7796 	{
7797 	  remove_grab (clist);
7798 	  clist->drag_button = 0;
7799 	}
7800 
7801       GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
7802 
7803       clist->button_actions[button] = button_actions;
7804     }
7805 }
7806 
7807 #include "gtkaliasdef.c"
7808