]> Pileus Git - ~andy/gtk/blob - gtk/gtkclist.c
Use common marshalling routines instead of having widget-private ones. It
[~andy/gtk] / gtk / gtkclist.c
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 Library 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  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library 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 #include <stdlib.h>
21 #include <string.h>
22 #include "../config.h"
23 #include "gtkmain.h"
24 #include "gtkclist.h"
25 #include "gtkbindings.h"
26 #include <gdk/gdkkeysyms.h>
27
28 /* the number rows memchunk expands at a time */
29 #define CLIST_OPTIMUM_SIZE 512
30
31 /* the width of the column resize windows */
32 #define DRAG_WIDTH  6
33
34 /* minimum allowed width of a column */
35 #define COLUMN_MIN_WIDTH 5
36
37 /* this defigns the base grid spacing */
38 #define CELL_SPACING 1
39
40 /* added the horizontal space at the beginning and end of a row*/
41 #define COLUMN_INSET 3
42
43 /* used for auto-scrolling */
44 #define SCROLL_TIME  100
45
46 /* scrollbar spacing class macro */
47 #define SCROLLBAR_SPACING(w) (GTK_CLIST_CLASS (GTK_OBJECT (w)->klass)->scrollbar_spacing)
48
49 /* gives the top pixel of the given row in context of
50  * the clist's voffset */
51 #define ROW_TOP_YPIXEL(clist, row) (((clist)->row_height * (row)) + \
52                                     (((row) + 1) * CELL_SPACING) + \
53                                     (clist)->voffset)
54
55 /* returns the row index from a y pixel location in the 
56  * context of the clist's voffset */
57 #define ROW_FROM_YPIXEL(clist, y)  (((y) - (clist)->voffset) / \
58                                     ((clist)->row_height + CELL_SPACING))
59
60 /* gives the left pixel of the given column in context of
61  * the clist's hoffset */
62 #define COLUMN_LEFT_XPIXEL(clist, colnum)  ((clist)->column[(colnum)].area.x + \
63                                             (clist)->hoffset)
64
65 /* returns the column index from a x pixel location in the 
66  * context of the clist's hoffset */
67 static inline gint
68 COLUMN_FROM_XPIXEL (GtkCList * clist,
69                     gint x)
70 {
71   gint i, cx;
72
73   for (i = 0; i < clist->columns; i++)
74     {
75       cx = clist->column[i].area.x + clist->hoffset;
76
77       if (x >= (cx - (COLUMN_INSET + CELL_SPACING)) &&
78           x <= (cx + clist->column[i].area.width + COLUMN_INSET))
79         return i;
80     }
81
82   /* no match */
83   return -1;
84 }
85
86 /* returns the top pixel of the given row in the context of
87  * the list height */
88 #define ROW_TOP(clist, row)        (((clist)->row_height + CELL_SPACING) * (row))
89
90 /* returns the left pixel of the given column in the context of
91  * the list width */
92 #define COLUMN_LEFT(clist, colnum) ((clist)->column[(colnum)].area.x)
93
94 /* returns the total height of the list */
95 #define LIST_HEIGHT(clist)         (((clist)->row_height * ((clist)->rows)) + \
96                                     (CELL_SPACING * ((clist)->rows + 1)))
97
98 /* returns the total width of the list */
99 #define LIST_WIDTH(clist)          ((clist)->column[(clist)->columns - 1].area.x + \
100                                     (clist)->column[(clist)->columns - 1].area.width + \
101                                     COLUMN_INSET + CELL_SPACING)
102
103
104 #define GTK_CLIST_CLASS_FW(_widget_) GTK_CLIST_CLASS (GTK_OBJECT (_widget_)->klass)
105
106
107 /* Signals */
108 enum
109 {
110   SELECT_ROW,
111   UNSELECT_ROW,
112   CLICK_COLUMN,
113   TOGGLE_FOCUS_ROW,
114   SELECT_ALL,
115   UNSELECT_ALL,
116   UNDO_SELECTION,
117   START_SELECTION,
118   END_SELECTION,
119   TOGGLE_ADD_MODE,
120   EXTEND_SELECTION,
121   SCROLL_VERTICAL,
122   SCROLL_HORIZONTAL,
123   ABORT_COLUMN_RESIZE,
124   LAST_SIGNAL
125 };
126
127 enum
128 {
129   SYNC_REMOVE,
130   SYNC_INSERT
131 };
132
133
134 typedef void (*GtkCListSignal1) (GtkObject * object,
135                                  gint arg1,
136                                  gint arg2,
137                                  GdkEvent * arg3,
138                                  gpointer data);
139
140 typedef void (*GtkCListSignal2) (GtkObject *object,
141                                  gpointer data); 
142
143 typedef void (*GtkCListSignal3) (GtkObject * object,
144                                  gint arg1,
145                                  gpointer data);
146 typedef void (*GtkCListSignal4) (GtkObject * object,
147                                  gint arg1,
148                                  gfloat arg2,
149                                  gpointer data);
150 typedef void (*GtkCListSignal5) (GtkObject * object,
151                                  gint arg1,
152                                  gfloat arg2,
153                                  gboolean arg3,
154                                  gpointer data);
155
156
157 static void sync_selection (GtkCList * clist,
158                             gint row,
159                             gint mode);
160
161 /* GtkCList Methods */
162 static void gtk_clist_class_init (GtkCListClass * klass);
163 static void gtk_clist_init (GtkCList * clist);
164 static void real_clear (GtkCList * clist);
165
166 /* GtkObject Methods */
167 static void gtk_clist_destroy (GtkObject * object);
168 static void gtk_clist_finalize (GtkObject * object);
169
170
171 /* GtkWidget Methods */
172 static void gtk_clist_realize (GtkWidget * widget);
173 static void gtk_clist_unrealize (GtkWidget * widget);
174 static void gtk_clist_map (GtkWidget * widget);
175 static void gtk_clist_unmap (GtkWidget * widget);
176 static void gtk_clist_draw (GtkWidget * widget,
177                             GdkRectangle * area);
178 static gint gtk_clist_expose (GtkWidget * widget,
179                               GdkEventExpose * event);
180 static gint gtk_clist_button_press (GtkWidget * widget,
181                                     GdkEventButton * event);
182 static gint gtk_clist_button_release (GtkWidget * widget,
183                                       GdkEventButton * event);
184 static gint gtk_clist_motion (GtkWidget * widget, 
185                               GdkEventMotion * event);
186 static void gtk_clist_size_request (GtkWidget * widget,
187                                     GtkRequisition * requisition);
188 static void gtk_clist_size_allocate (GtkWidget * widget,
189                                      GtkAllocation * allocation);
190 static gint get_selection_info (GtkCList * clist,
191                                 gint x,
192                                 gint y,
193                                 gint * row,
194                                 gint * column);
195
196 /* GtkContainer Methods */
197 static void gtk_clist_foreach (GtkContainer * container,
198                                GtkCallback callback,
199                                gpointer callback_data);
200
201 /* Drawing */
202 static void draw_row (GtkCList * clist,
203                       GdkRectangle * area,
204                       gint row,
205                       GtkCListRow * clist_row);
206 static void draw_rows (GtkCList * clist,
207                        GdkRectangle * area);
208
209 /* Size Allocation */
210 static void size_allocate_title_buttons (GtkCList * clist);
211 static void size_allocate_columns (GtkCList * clist);
212
213 /* Selection */
214 static void toggle_row (GtkCList * clist,
215                         gint row,
216                         gint column,
217                         GdkEvent * event);
218 static void select_row (GtkCList * clist,
219                         gint row,
220                         gint column,
221                         GdkEvent * event);
222 static void unselect_row (GtkCList * clist,
223                           gint row,
224                           gint column,
225                           GdkEvent * event);
226 static void real_select_row (GtkCList * clist,
227                              gint row,
228                              gint column,
229                              GdkEvent * event);
230 static void real_unselect_row (GtkCList * clist,
231                                gint row,
232                                gint column,
233                                GdkEvent * event);
234 static void update_extended_selection (GtkCList *clist,
235                                        gint row);
236 static GList * selection_find (GtkCList *clist,
237                                gint row_number,
238                                GList *row_list_element);
239 static void real_select_all (GtkCList * clist);
240 static void real_unselect_all (GtkCList * clist);
241 static void move_vertical (GtkCList *clist,
242                            gint row,
243                            gfloat align);
244 static void move_horizontal (GtkCList *clist,
245                              gint diff);
246 static void real_undo_selection (GtkCList * clist);
247 static void fake_unselect_all (GtkCList *clist,
248                                gint row);
249 static void fake_toggle_row (GtkCList *clist,
250                              gint row);
251 static void resync_selection (GtkCList *clist,
252                               GdkEvent *event);
253
254 /* Resize Columns */
255 static void draw_xor_line (GtkCList * clist);
256 static gint new_column_width (GtkCList * clist,
257                               gint column,
258                               gint * x,
259                               gint * visible);
260 static void resize_column (GtkCList * clist,
261                            gint column,
262                            gint width);
263 static void abort_column_resize (GtkCList *clist);
264
265 /* Buttons */
266 static void column_button_create (GtkCList * clist,
267                                   gint column);
268 static void column_button_clicked (GtkWidget * widget,
269                                    gpointer data);
270
271 /* Scrollbars */
272 static void create_scrollbars (GtkCList * clist);
273 static void adjust_scrollbars (GtkCList * clist);
274 static void check_exposures   (GtkCList * clist);
275 static void vadjustment_changed (GtkAdjustment * adjustment,
276                                  gpointer data);
277 static void vadjustment_value_changed (GtkAdjustment * adjustment,
278                                        gpointer data);
279 static void hadjustment_changed (GtkAdjustment * adjustment,
280                                  gpointer data);
281 static void hadjustment_value_changed (GtkAdjustment * adjustment,
282                                        gpointer data);
283
284 /* Memory Allocation/Distruction Routines */
285 static GtkCListColumn *columns_new (GtkCList * clist);
286
287 static void column_title_new (GtkCList * clist,
288                               gint column,
289                               gchar * title);
290 static void columns_delete (GtkCList * clist);
291
292 static GtkCListRow *row_new (GtkCList * clist);
293
294 static void row_delete (GtkCList * clist,
295                         GtkCListRow * clist_row);
296 static void cell_empty (GtkCList * clist,
297                         GtkCListRow * clist_row,
298                         gint column);
299 static void cell_set_text (GtkCList * clist,
300                            GtkCListRow * clist_row,
301                            gint column,
302                            gchar * text);
303 static void cell_set_pixmap (GtkCList * clist,
304                              GtkCListRow * clist_row,
305                              gint column,
306                              GdkPixmap * pixmap,
307                              GdkBitmap * mask);
308 static void cell_set_pixtext (GtkCList * clist,
309                               GtkCListRow * clist_row,
310                               gint column,
311                               gchar * text,
312                               guint8 spacing,
313                               GdkPixmap * pixmap,
314                               GdkBitmap * mask);
315
316 /* Focus handling */
317 static void gtk_clist_draw_focus (GtkWidget *widget);
318 static gint gtk_clist_focus_in   (GtkWidget     *widget,
319                                   GdkEventFocus *event);
320 static gint gtk_clist_focus_out  (GtkWidget     *widget,
321                                   GdkEventFocus *event);
322 static gint gtk_clist_focus      (GtkContainer     *container,
323                                   GtkDirectionType  direction);
324 static void gtk_clist_set_focus_child (GtkContainer *container,
325                                        GtkWidget    *child);
326 static gint gtk_clist_key_press  (GtkWidget   *widget,
327                                   GdkEventKey *event);
328
329 /* Selection handling */
330 static void set_anchor (GtkCList *clist,
331                         gboolean  add_mode,
332                         gint      anchor,
333                         gint      undo_anchor);
334 static void start_selection (GtkCList *clist);
335 static void end_selection (GtkCList *clist);
336
337 static void toggle_add_mode (GtkCList *clist);
338 static void toggle_focus_row (GtkCList *clist);
339 static void move_focus_row (GtkCList      *clist,
340                             GtkScrollType  scroll_type,
341                             gfloat         position);
342 static void scroll_horizontal (GtkCList      *clist,
343                                GtkScrollType  scroll_type,
344                                gfloat         position);
345 static void scroll_vertical (GtkCList      *clist,
346                              GtkScrollType  scroll_type,
347                              gfloat         position);
348 static void extend_selection (GtkCList      *clist,
349                               GtkScrollType  scroll_type,
350                               gfloat         position,
351                               gboolean       auto_start_selection);
352
353 /* Fill in data after widget is realized and has style */
354
355 static void add_style_data (GtkCList * clist);
356
357 static GtkContainerClass *parent_class = NULL;
358 static guint clist_signals[LAST_SIGNAL] = {0};
359
360
361 GtkType
362 gtk_clist_get_type (void)
363 {
364   static GtkType clist_type = 0;
365
366   if (!clist_type)
367     {
368       GtkTypeInfo clist_info =
369       {
370         "GtkCList",
371         sizeof (GtkCList),
372         sizeof (GtkCListClass),
373         (GtkClassInitFunc) gtk_clist_class_init,
374         (GtkObjectInitFunc) gtk_clist_init,
375         /* reserved_1 */ NULL,
376         /* reserved_2 */ NULL,
377         (GtkClassInitFunc) NULL,
378       };
379
380       clist_type = gtk_type_unique (GTK_TYPE_CONTAINER, &clist_info);
381     }
382
383   return clist_type;
384 }
385
386 static void
387 gtk_clist_class_init (GtkCListClass * klass)
388 {
389   GtkObjectClass *object_class;
390   GtkWidgetClass *widget_class;
391   GtkContainerClass *container_class;
392
393   object_class = (GtkObjectClass *) klass;
394   widget_class = (GtkWidgetClass *) klass;
395   container_class = (GtkContainerClass *) klass;
396
397   parent_class = gtk_type_class (GTK_TYPE_CONTAINER);
398
399   clist_signals[SELECT_ROW] =
400     gtk_signal_new ("select_row",
401                     GTK_RUN_FIRST,
402                     object_class->type,
403                     GTK_SIGNAL_OFFSET (GtkCListClass, select_row),
404                     gtk_marshal_NONE__INT_INT_POINTER,
405                     GTK_TYPE_NONE, 3,
406                     GTK_TYPE_INT,
407                     GTK_TYPE_INT,
408                     GTK_TYPE_GDK_EVENT);
409   clist_signals[UNSELECT_ROW] =
410     gtk_signal_new ("unselect_row",
411                     GTK_RUN_FIRST,
412                     object_class->type,
413                     GTK_SIGNAL_OFFSET (GtkCListClass, unselect_row),
414                     gtk_marshal_NONE__INT_INT_POINTER,
415             GTK_TYPE_NONE, 3, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_GDK_EVENT);
416   clist_signals[CLICK_COLUMN] =
417     gtk_signal_new ("click_column",
418                     GTK_RUN_FIRST,
419                     object_class->type,
420                     GTK_SIGNAL_OFFSET (GtkCListClass, click_column),
421                     gtk_marshal_NONE__INT,
422                     GTK_TYPE_NONE, 1, GTK_TYPE_INT);
423
424   clist_signals[TOGGLE_FOCUS_ROW] =
425     gtk_signal_new ("toggle_focus_row",
426                     GTK_RUN_LAST | GTK_RUN_ACTION,
427                     object_class->type,
428                     GTK_SIGNAL_OFFSET (GtkCListClass, toggle_focus_row),
429                     gtk_marshal_NONE__NONE,
430                     GTK_TYPE_NONE, 0);
431   clist_signals[SELECT_ALL] =
432     gtk_signal_new ("select_all",
433                     GTK_RUN_LAST | GTK_RUN_ACTION,
434                     object_class->type,
435                     GTK_SIGNAL_OFFSET (GtkCListClass, select_all),
436                     gtk_marshal_NONE__NONE,
437                     GTK_TYPE_NONE, 0);
438   clist_signals[UNSELECT_ALL] =
439     gtk_signal_new ("unselect_all",
440                     GTK_RUN_LAST | GTK_RUN_ACTION,
441                     object_class->type,
442                     GTK_SIGNAL_OFFSET (GtkCListClass, unselect_all),
443                     gtk_marshal_NONE__NONE,
444                     GTK_TYPE_NONE, 0);
445   clist_signals[UNDO_SELECTION] =
446     gtk_signal_new ("undo_selection",
447                     GTK_RUN_LAST | GTK_RUN_ACTION,
448                     object_class->type,
449                     GTK_SIGNAL_OFFSET (GtkCListClass, undo_selection),
450                     gtk_marshal_NONE__NONE,
451                     GTK_TYPE_NONE, 0);
452   clist_signals[START_SELECTION] =
453     gtk_signal_new ("start_selection",
454                     GTK_RUN_LAST | GTK_RUN_ACTION,
455                     object_class->type,
456                     GTK_SIGNAL_OFFSET (GtkCListClass, start_selection),
457                     gtk_marshal_NONE__NONE,
458                     GTK_TYPE_NONE, 0);
459   clist_signals[END_SELECTION] =
460     gtk_signal_new ("end_selection",
461                     GTK_RUN_LAST | GTK_RUN_ACTION,
462                     object_class->type,
463                     GTK_SIGNAL_OFFSET (GtkCListClass, end_selection),
464                     gtk_marshal_NONE__NONE,
465                     GTK_TYPE_NONE, 0);
466   clist_signals[TOGGLE_ADD_MODE] =
467     gtk_signal_new ("toggle_add_mode",
468                     GTK_RUN_LAST | GTK_RUN_ACTION,
469                     object_class->type,
470                     GTK_SIGNAL_OFFSET (GtkCListClass, toggle_add_mode),
471                     gtk_marshal_NONE__NONE,
472                     GTK_TYPE_NONE, 0);
473   clist_signals[EXTEND_SELECTION] =
474     gtk_signal_new ("extend_selection",
475                     GTK_RUN_LAST | GTK_RUN_ACTION,
476                     object_class->type,
477                     GTK_SIGNAL_OFFSET (GtkCListClass, extend_selection),
478                     gtk_marshal_NONE__ENUM_FLOAT_BOOL,
479                     GTK_TYPE_NONE, 3,
480                     GTK_TYPE_ENUM, GTK_TYPE_FLOAT, GTK_TYPE_BOOL);
481   clist_signals[SCROLL_VERTICAL] =
482     gtk_signal_new ("scroll_vertical",
483                     GTK_RUN_LAST | GTK_RUN_ACTION,
484                     object_class->type,
485                     GTK_SIGNAL_OFFSET (GtkCListClass, scroll_vertical),
486                     gtk_marshal_NONE__ENUM_FLOAT,
487                     GTK_TYPE_NONE, 2, GTK_TYPE_ENUM, GTK_TYPE_FLOAT);
488   clist_signals[SCROLL_HORIZONTAL] =
489     gtk_signal_new ("scroll_horizontal",
490                     GTK_RUN_LAST | GTK_RUN_ACTION,
491                     object_class->type,
492                     GTK_SIGNAL_OFFSET (GtkCListClass, scroll_horizontal),
493                     gtk_marshal_NONE__ENUM_FLOAT,
494                     GTK_TYPE_NONE, 2, GTK_TYPE_ENUM, GTK_TYPE_FLOAT);
495   clist_signals[ABORT_COLUMN_RESIZE] =
496     gtk_signal_new ("abort_column_resize",
497                     GTK_RUN_LAST | GTK_RUN_ACTION,
498                     object_class->type,
499                     GTK_SIGNAL_OFFSET (GtkCListClass, abort_column_resize),
500                     gtk_marshal_NONE__NONE,
501                     GTK_TYPE_NONE, 0);
502
503
504   gtk_object_class_add_signals (object_class, clist_signals, LAST_SIGNAL);
505
506   object_class->destroy = gtk_clist_destroy;
507   object_class->finalize = gtk_clist_finalize;
508
509   widget_class->realize = gtk_clist_realize;
510   widget_class->unrealize = gtk_clist_unrealize;
511   widget_class->map = gtk_clist_map;
512   widget_class->unmap = gtk_clist_unmap;
513   widget_class->draw = gtk_clist_draw;
514   widget_class->button_press_event = gtk_clist_button_press;
515   widget_class->button_release_event = gtk_clist_button_release;
516   widget_class->motion_notify_event = gtk_clist_motion;
517   widget_class->expose_event = gtk_clist_expose;
518   widget_class->size_request = gtk_clist_size_request;
519   widget_class->size_allocate = gtk_clist_size_allocate;
520   widget_class->key_press_event = gtk_clist_key_press;
521   widget_class->focus_in_event = gtk_clist_focus_in;
522   widget_class->focus_out_event = gtk_clist_focus_out;
523   widget_class->draw_focus = gtk_clist_draw_focus;
524
525   /* container_class->add = NULL; use the default GtkContainerClass warning */
526   /* container_class->remove = NULL; use the default GtkContainerClass warning */
527   container_class->foreach = gtk_clist_foreach;
528   container_class->focus = gtk_clist_focus;
529   container_class->set_focus_child = gtk_clist_set_focus_child;
530
531   klass->select_row = real_select_row;
532   klass->unselect_row = real_unselect_row;
533   klass->undo_selection = real_undo_selection;
534   klass->resync_selection = resync_selection;
535   klass->selection_find = selection_find;
536   klass->click_column = NULL;
537   klass->draw_row = draw_row;
538   klass->clear = real_clear;
539   klass->select_all = real_select_all;
540   klass->unselect_all = real_unselect_all;
541   klass->fake_unselect_all = fake_unselect_all;
542   klass->scroll_horizontal = scroll_horizontal;
543   klass->scroll_vertical = scroll_vertical;
544   klass->extend_selection = extend_selection;
545   klass->toggle_focus_row = toggle_focus_row;
546   klass->toggle_add_mode = toggle_add_mode;
547   klass->start_selection = start_selection;
548   klass->end_selection = end_selection;
549   klass->abort_column_resize = abort_column_resize;
550
551   klass->scrollbar_spacing = 5;
552
553   {
554     GtkBindingSet *binding_set;
555
556     binding_set = gtk_binding_set_by_class (klass);
557     gtk_binding_entry_add_signal (binding_set, GDK_Up, 0,
558                                   "scroll_vertical", 2,
559                                   GTK_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
560                                   GTK_TYPE_FLOAT, 0.0);
561     gtk_binding_entry_add_signal (binding_set, GDK_Down, 0,
562                                   "scroll_vertical", 2,
563                                   GTK_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
564                                   GTK_TYPE_FLOAT, 0.0);
565     gtk_binding_entry_add_signal (binding_set, GDK_Page_Up, 0,
566                                   "scroll_vertical", 2,
567                                   GTK_TYPE_ENUM, GTK_SCROLL_PAGE_BACKWARD,
568                                   GTK_TYPE_FLOAT, 0.0);
569     gtk_binding_entry_add_signal (binding_set, GDK_Page_Down, 0,
570                                   "scroll_vertical", 2,
571                                   GTK_TYPE_ENUM, GTK_SCROLL_PAGE_FORWARD,
572                                   GTK_TYPE_FLOAT, 0.0);
573     gtk_binding_entry_add_signal (binding_set, GDK_Home, GDK_CONTROL_MASK,
574                                   "scroll_vertical", 2,
575                                   GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
576                                   GTK_TYPE_FLOAT, 0.0);
577     gtk_binding_entry_add_signal (binding_set, GDK_End, GDK_CONTROL_MASK,
578                                   "scroll_vertical", 2,
579                                   GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
580                                   GTK_TYPE_FLOAT, 1.0);
581
582     gtk_binding_entry_add_signal (binding_set, GDK_Up, GDK_SHIFT_MASK,
583                                   "extend_selection", 3,
584                                   GTK_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
585                                   GTK_TYPE_FLOAT, 0.0, GTK_TYPE_BOOL, TRUE);
586     gtk_binding_entry_add_signal (binding_set, GDK_Down, GDK_SHIFT_MASK,
587                                   "extend_selection", 3,
588                                   GTK_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
589                                   GTK_TYPE_FLOAT, 0.0, GTK_TYPE_BOOL, TRUE);
590     gtk_binding_entry_add_signal (binding_set, GDK_Page_Up, GDK_SHIFT_MASK,
591                                   "extend_selection", 3,
592                                   GTK_TYPE_ENUM, GTK_SCROLL_PAGE_BACKWARD,
593                                   GTK_TYPE_FLOAT, 0.0, GTK_TYPE_BOOL, TRUE);
594     gtk_binding_entry_add_signal (binding_set, GDK_Page_Down, GDK_SHIFT_MASK,
595                                   "extend_selection", 3,
596                                   GTK_TYPE_ENUM, GTK_SCROLL_PAGE_FORWARD,
597                                   GTK_TYPE_FLOAT, 0.0, GTK_TYPE_BOOL, TRUE);
598     gtk_binding_entry_add_signal (binding_set, GDK_Home,
599                                   GDK_SHIFT_MASK | GDK_CONTROL_MASK,
600                                   "extend_selection", 3,
601                                   GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
602                                   GTK_TYPE_FLOAT, 0.0, GTK_TYPE_BOOL, TRUE);
603     gtk_binding_entry_add_signal (binding_set, GDK_End,
604                                   GDK_SHIFT_MASK | GDK_CONTROL_MASK,
605                                   "extend_selection", 3,
606                                   GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
607                                   GTK_TYPE_FLOAT, 1.0, GTK_TYPE_BOOL, TRUE);
608
609
610     gtk_binding_entry_add_signal (binding_set, GDK_Left, 0,
611                                   "scroll_horizontal", 2,
612                                   GTK_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
613                                   GTK_TYPE_FLOAT, 0.0);
614     gtk_binding_entry_add_signal (binding_set, GDK_Right, 0,
615                                   "scroll_horizontal", 2,
616                                   GTK_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
617                                   GTK_TYPE_FLOAT, 0.0);
618     gtk_binding_entry_add_signal (binding_set, GDK_Home, 0,
619                                   "scroll_horizontal", 2,
620                                   GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
621                                   GTK_TYPE_FLOAT, 0.0);
622     gtk_binding_entry_add_signal (binding_set, GDK_End, 0,
623                                   "scroll_horizontal", 2,
624                                   GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
625                                   GTK_TYPE_FLOAT, 1.0);
626
627
628     gtk_binding_entry_add_signal (binding_set, GDK_Escape, 0,
629                                   "undo_selection", 0);
630     gtk_binding_entry_add_signal (binding_set, GDK_Escape, 0,
631                                   "abort_column_resize", 0);
632     gtk_binding_entry_add_signal (binding_set, GDK_space, 0,
633                                   "toggle_focus_row", 0);
634     gtk_binding_entry_add_signal (binding_set, GDK_space, GDK_CONTROL_MASK,
635                                   "toggle_add_mode", 0);
636     gtk_binding_entry_add_signal (binding_set, '/', GDK_CONTROL_MASK,
637                                   "select_all", 0);
638     gtk_binding_entry_add_signal (binding_set, '\\', GDK_CONTROL_MASK,
639                                   "unselect_all", 0);
640     gtk_binding_entry_add_signal (binding_set, GDK_Shift_L,
641                                   GDK_RELEASE_MASK | GDK_SHIFT_MASK,
642                                   "end_selection", 0);
643     gtk_binding_entry_add_signal (binding_set, GDK_Shift_R,
644                                   GDK_RELEASE_MASK | GDK_SHIFT_MASK,
645                                   "end_selection", 0);
646   }
647
648   /*
649   {
650     GtkBindingSet *binding_set;
651
652     binding_set = gtk_binding_set_by_class (klass);
653     gtk_binding_entry_add_signal (binding_set, GDK_Up, 0,
654                                   "scroll_vertical", 2,
655                                   GTK_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
656                                   GTK_TYPE_FLOAT, 0.0);
657     gtk_binding_entry_add_signal (binding_set, GDK_Down, 0,
658                                   "scroll_vertical", 2,
659                                   GTK_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
660                                   GTK_TYPE_FLOAT, 0.0);
661     gtk_binding_entry_add_signal (binding_set, GDK_Page_Up, 0,
662                                   "scroll_vertical", 2,
663                                   GTK_TYPE_ENUM, GTK_SCROLL_PAGE_BACKWARD,
664                                   GTK_TYPE_FLOAT, 0.0);
665     gtk_binding_entry_add_signal (binding_set, GDK_Page_Down, 0,
666                                   "scroll_vertical", 2,
667                                   GTK_TYPE_ENUM, GTK_SCROLL_PAGE_FORWARD,
668                                   GTK_TYPE_FLOAT, 0.0);
669     gtk_binding_entry_add_signal (binding_set, GDK_Home, GDK_CONTROL_MASK,
670                                   "scroll_vertical", 2,
671                                   GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
672                                   GTK_TYPE_FLOAT, 0.0);
673     gtk_binding_entry_add_signal (binding_set, GDK_End, GDK_CONTROL_MASK,
674                                   "scroll_vertical", 2,
675                                   GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
676                                   GTK_TYPE_FLOAT, 0);
677
678     gtk_binding_entry_add_signal (binding_set, GDK_KP_Up, 0,
679                                   "extend_selection", 3,
680                                   GTK_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
681                                   GTK_TYPE_FLOAT, 0.0, GTK_TYPE_BOOL, TRUE);
682     gtk_binding_entry_add_signal (binding_set, GDK_KP_Down, 0,
683                                   "extend_selection", 3,
684                                   GTK_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
685                                   GTK_TYPE_FLOAT, 0.0, GTK_TYPE_BOOL, TRUE);
686     gtk_binding_entry_add_signal (binding_set, GDK_KP_Page_Up, 0,
687                                   "extend_selection", 3,
688                                   GTK_TYPE_ENUM, GTK_SCROLL_PAGE_BACKWARD,
689                                   GTK_TYPE_FLOAT, 0.0, GTK_TYPE_BOOL, TRUE);
690     gtk_binding_entry_add_signal (binding_set, GDK_KP_Page_Down, 0,
691                                   "extend_selection", 3,
692                                   GTK_TYPE_ENUM, GTK_SCROLL_PAGE_FORWARD,
693                                   GTK_TYPE_FLOAT, 0.0, GTK_TYPE_BOOL, TRUE);
694     gtk_binding_entry_add_signal (binding_set, GDK_KP_Home, 0,
695                                   "extend_selection", 3,
696                                   GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
697                                   GTK_TYPE_FLOAT, 0.0, GTK_TYPE_BOOL, TRUE);
698     gtk_binding_entry_add_signal (binding_set, GDK_KP_End, 0,
699                                   "extend_selection", 3,
700                                   GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
701                                   GTK_TYPE_FLOAT, 1.0, GTK_TYPE_BOOL, TRUE);
702
703     gtk_binding_entry_add_signal (binding_set, GDK_Left, 0,
704                                   "scroll_horizontal", 2,
705                                   GTK_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
706                                   GTK_TYPE_FLOAT, 0.0);
707     gtk_binding_entry_add_signal (binding_set, GDK_KP_Left, 0,
708                                   "scroll_horizontal", 2,
709                                   GTK_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
710                                   GTK_TYPE_FLOAT, 0.0);
711     gtk_binding_entry_add_signal (binding_set, GDK_Right, 0,
712                                   "scroll_horizontal", 2,
713                                   GTK_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
714                                   GTK_TYPE_FLOAT, 0.0);
715     gtk_binding_entry_add_signal (binding_set, GDK_KP_Right, 0,
716                                   "scroll_horizontal", 2,
717                                   GTK_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
718                                   GTK_TYPE_FLOAT, 0.0);
719     gtk_binding_entry_add_signal (binding_set, GDK_Home, 0,
720                                   "scroll_horizontal", 2,
721                                   GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
722                                   GTK_TYPE_FLOAT, 0.0);
723     gtk_binding_entry_add_signal (binding_set, GDK_End, 0,
724                                   "sroll_horizontal", 2,
725                                   GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
726                                   GTK_TYPE_FLOAT, 1.0);
727
728     gtk_binding_entry_add_signal (binding_set, GDK_KP_Divide, 0,
729                                   "undo_selection", 0);
730     gtk_binding_entry_add_signal (binding_set, GDK_Escape, 0,
731                                   "abort_column_resize", 0);
732     gtk_binding_entry_add_signal (binding_set, GDK_space, 0,
733                                   "toggle_focus_row", 0);
734     gtk_binding_entry_add_signal (binding_set, GDK_KP_Multiply, 0,
735                                   "toggle_add_mode", 0);
736     gtk_binding_entry_add_signal (binding_set, GDK_KP_Add, 0,
737                                   "select_all", 0);
738     gtk_binding_entry_add_signal (binding_set, GDK_KP_Subtract, 0,
739                                   "unselect_all", 0);
740     gtk_binding_entry_add_signal (binding_set, GDK_KP_Enter, 0,
741                                   "end_selection", 0);
742   }
743   */
744 }
745
746 static void
747 gtk_clist_init (GtkCList * clist)
748 {
749   clist->flags = 0;
750
751   GTK_WIDGET_UNSET_FLAGS (clist, GTK_NO_WINDOW);
752   GTK_CLIST_SET_FLAG (clist, CLIST_FROZEN);
753
754   clist->row_mem_chunk = NULL;
755   clist->cell_mem_chunk = NULL;
756
757   clist->rows = 0;
758   clist->row_center_offset = 0;
759   clist->row_height = 0;
760   clist->row_list = NULL;
761   clist->row_list_end = NULL;
762
763   clist->columns = 0;
764
765   clist->title_window = NULL;
766   clist->column_title_area.x = 0;
767   clist->column_title_area.y = 0;
768   clist->column_title_area.width = 1;
769   clist->column_title_area.height = 1;
770
771   clist->clist_window = NULL;
772   clist->clist_window_width = 1;
773   clist->clist_window_height = 1;
774
775   clist->hoffset = 0;
776   clist->voffset = 0;
777
778   clist->shadow_type = GTK_SHADOW_IN;
779   clist->hscrollbar_policy = GTK_POLICY_ALWAYS;
780   clist->vscrollbar_policy = GTK_POLICY_ALWAYS;
781
782   clist->cursor_drag = NULL;
783   clist->xor_gc = NULL;
784   clist->fg_gc = NULL;
785   clist->bg_gc = NULL;
786   clist->x_drag = 0;
787
788   clist->selection_mode = GTK_SELECTION_SINGLE;
789   clist->selection = NULL;
790   clist->selection_end = NULL;
791   clist->undo_selection = NULL;
792   clist->undo_unselection = NULL;
793
794   GTK_WIDGET_SET_FLAGS (clist, GTK_CAN_FOCUS);
795   GTK_CLIST_SET_FLAG (clist, CLIST_CHILD_HAS_FOCUS);
796   clist->focus_row = -1;
797   clist->undo_anchor = -1;
798
799   clist->anchor = -1;
800   clist->anchor_state = GTK_STATE_SELECTED;
801   clist->drag_pos = -1;
802   clist->htimer = 0;
803   clist->vtimer = 0;
804 }
805
806 /* Constructors */
807 void
808 gtk_clist_construct (GtkCList * clist,
809                      gint columns,
810                      gchar *titles[])
811 {
812   int i;
813
814   g_return_if_fail (clist != NULL);
815   g_return_if_fail (GTK_IS_CLIST (clist));
816   g_return_if_fail (GTK_CLIST_CONSTRUCTED (clist) == FALSE);
817
818   GTK_CLIST_SET_FLAG (clist, CLIST_CONSTRUCTED);
819
820   /* initalize memory chunks, if this has not been done by any
821    * possibly derived widget
822    */
823   if (!clist->row_mem_chunk)
824     clist->row_mem_chunk = g_mem_chunk_new ("clist row mem chunk",
825                                             sizeof (GtkCListRow),
826                                             sizeof (GtkCListRow) * CLIST_OPTIMUM_SIZE, 
827                                             G_ALLOC_AND_FREE);
828
829   if (!clist->cell_mem_chunk)
830     clist->cell_mem_chunk = g_mem_chunk_new ("clist cell mem chunk",
831                                              sizeof (GtkCell) * columns,
832                                              sizeof (GtkCell) * columns * CLIST_OPTIMUM_SIZE, 
833                                              G_ALLOC_AND_FREE);
834
835   /* set number of columns, allocate memory */
836   clist->columns = columns;
837   clist->column = columns_new (clist);
838
839   /* there needs to be at least one column button 
840    * because there is alot of code that will break if it
841    * isn't there*/
842   column_button_create (clist, 0);
843
844   /* create scrollbars */
845   create_scrollbars (clist);
846
847   if (titles)
848     {
849       GTK_CLIST_SET_FLAG (clist, CLIST_SHOW_TITLES);
850       for (i = 0; i < columns; i++)
851         gtk_clist_set_column_title (clist, i, titles[i]);
852     }
853   else
854     {
855       GTK_CLIST_UNSET_FLAG (clist, CLIST_SHOW_TITLES);
856     }
857 }
858
859 /*
860  * GTKCLIST PUBLIC INTERFACE
861  *   gtk_clist_new_with_titles
862  *   gtk_clist_new
863  */
864 GtkWidget *
865 gtk_clist_new_with_titles (gint columns,
866                            gchar * titles[])
867 {
868   GtkWidget *widget;
869
870   g_return_val_if_fail (titles != NULL, NULL);
871   
872   widget = gtk_type_new (GTK_TYPE_CLIST);
873   
874   gtk_clist_construct (GTK_CLIST (widget), columns, titles);
875
876   return widget;
877 }
878
879 GtkWidget *
880 gtk_clist_new (gint columns)
881 {
882   GtkCList *clist;
883
884   if (columns < 1)
885     return NULL;
886
887   clist = gtk_type_new (GTK_TYPE_CLIST);
888   gtk_clist_construct (clist, columns, NULL);
889   return GTK_WIDGET (clist);
890 }
891
892 void
893 gtk_clist_set_border (GtkCList * clist,
894                       GtkShadowType border)
895 {
896   g_return_if_fail (clist != NULL);
897   g_return_if_fail (GTK_IS_CLIST (clist));
898
899   clist->shadow_type = border;
900
901   if (GTK_WIDGET_VISIBLE (clist))
902     gtk_widget_queue_resize (GTK_WIDGET (clist));
903 }
904
905 void
906 gtk_clist_set_selection_mode (GtkCList * clist,
907                               GtkSelectionMode mode)
908 {
909   g_return_if_fail (clist != NULL);
910   g_return_if_fail (GTK_IS_CLIST (clist));
911
912   if (mode == clist->selection_mode)
913     return;
914
915   clist->selection_mode = mode;
916   clist->anchor = -1;
917   clist->anchor_state = GTK_STATE_SELECTED;
918   clist->drag_pos = -1;
919   clist->undo_anchor = clist->focus_row;
920
921   g_list_free (clist->undo_selection);
922   g_list_free (clist->undo_unselection);
923   clist->undo_selection = NULL;
924   clist->undo_unselection = NULL;
925
926   switch (mode)
927     {
928     case GTK_SELECTION_MULTIPLE:
929     case GTK_SELECTION_EXTENDED:
930       return;
931     case GTK_SELECTION_BROWSE:
932     case GTK_SELECTION_SINGLE:
933       gtk_clist_unselect_all (clist);
934       break;
935     }
936 }
937
938 void
939 gtk_clist_freeze (GtkCList * clist)
940 {
941   g_return_if_fail (clist != NULL);
942   g_return_if_fail (GTK_IS_CLIST (clist));
943
944   GTK_CLIST_SET_FLAG (clist, CLIST_FROZEN);
945 }
946
947 void
948 gtk_clist_thaw (GtkCList * clist)
949 {
950   g_return_if_fail (clist != NULL);
951   g_return_if_fail (GTK_IS_CLIST (clist));
952
953   GTK_CLIST_UNSET_FLAG (clist, CLIST_FROZEN);
954
955   adjust_scrollbars (clist);
956   draw_rows (clist, NULL);
957 }
958
959 void
960 gtk_clist_column_titles_show (GtkCList * clist)
961 {
962   g_return_if_fail (clist != NULL);
963   g_return_if_fail (GTK_IS_CLIST (clist));
964
965   if (!GTK_CLIST_SHOW_TITLES (clist))
966     {
967       GTK_CLIST_SET_FLAG (clist, CLIST_SHOW_TITLES);
968       if (clist->title_window)
969               gdk_window_show (clist->title_window);
970       gtk_widget_queue_resize (GTK_WIDGET (clist));
971     }
972 }
973
974 void 
975 gtk_clist_column_titles_hide (GtkCList * clist)
976 {
977   g_return_if_fail (clist != NULL);
978   g_return_if_fail (GTK_IS_CLIST (clist));
979
980   if (GTK_CLIST_SHOW_TITLES (clist))
981     {
982       GTK_CLIST_UNSET_FLAG (clist, CLIST_SHOW_TITLES);
983       if (clist->title_window)
984               gdk_window_hide (clist->title_window);
985       gtk_widget_queue_resize (GTK_WIDGET (clist));
986     }
987 }
988
989 void
990 gtk_clist_column_title_active (GtkCList * clist,
991                                gint column)
992 {
993   g_return_if_fail (clist != NULL);
994   g_return_if_fail (GTK_IS_CLIST (clist));
995
996   if (column < 0 || column >= clist->columns)
997     return;
998
999   if (!GTK_WIDGET_SENSITIVE (clist->column[column].button) ||
1000       !GTK_WIDGET_CAN_FOCUS (clist->column[column].button))
1001     {
1002       GTK_WIDGET_SET_FLAGS (clist->column[column].button, GTK_SENSITIVE | GTK_CAN_FOCUS);
1003       if (GTK_WIDGET_VISIBLE (clist))
1004         gtk_widget_queue_draw (clist->column[column].button);
1005     }
1006 }
1007
1008 void
1009 gtk_clist_column_title_passive (GtkCList * clist,
1010                                 gint column)
1011 {
1012   g_return_if_fail (clist != NULL);
1013   g_return_if_fail (GTK_IS_CLIST (clist));
1014
1015   if (column < 0 || column >= clist->columns)
1016     return;
1017
1018   if (GTK_WIDGET_SENSITIVE (clist->column[column].button) ||
1019       GTK_WIDGET_CAN_FOCUS (clist->column[column].button))
1020     {
1021       GTK_WIDGET_UNSET_FLAGS (clist->column[column].button, GTK_SENSITIVE | GTK_CAN_FOCUS);
1022       if (GTK_WIDGET_VISIBLE (clist))
1023         gtk_widget_queue_draw (clist->column[column].button);
1024     }
1025 }
1026
1027 void
1028 gtk_clist_column_titles_active (GtkCList * clist)
1029 {
1030   gint i;
1031
1032   g_return_if_fail (clist != NULL);
1033   g_return_if_fail (GTK_IS_CLIST (clist));
1034
1035   for (i = 0; i < clist->columns; i++)
1036     if (clist->column[i].button)
1037       gtk_clist_column_title_active (clist, i);
1038 }
1039
1040 void
1041 gtk_clist_column_titles_passive (GtkCList * clist)
1042 {
1043   gint i;
1044
1045   g_return_if_fail (clist != NULL);
1046   g_return_if_fail (GTK_IS_CLIST (clist));
1047
1048   for (i = 0; i < clist->columns; i++)
1049     if (clist->column[i].button)
1050       gtk_clist_column_title_passive (clist, i);
1051 }
1052
1053 void
1054 gtk_clist_set_column_title (GtkCList * clist,
1055                             gint column,
1056                             gchar * title)
1057 {
1058   gint new_button = 0;
1059   GtkWidget *old_widget;
1060   GtkWidget *alignment = NULL;
1061   GtkWidget *label;
1062
1063   g_return_if_fail (clist != NULL);
1064   g_return_if_fail (GTK_IS_CLIST (clist));
1065
1066   if (column < 0 || column >= clist->columns)
1067     return;
1068
1069   /* if the column button doesn't currently exist,
1070    * it has to be created first */
1071   if (!clist->column[column].button)
1072     {
1073       column_button_create (clist, column);
1074       new_button = 1;
1075     }
1076
1077   column_title_new (clist, column, title);
1078
1079   /* remove and destroy the old widget */
1080   old_widget = GTK_BIN (clist->column[column].button)->child;
1081   if (old_widget)
1082     gtk_container_remove (GTK_CONTAINER (clist->column[column].button), old_widget);
1083
1084   /* create new alignment based no column justification */
1085   switch (clist->column[column].justification)
1086     {
1087     case GTK_JUSTIFY_LEFT:
1088       alignment = gtk_alignment_new (0.0, 0.5, 0.0, 0.0);
1089       break;
1090
1091     case GTK_JUSTIFY_RIGHT:
1092       alignment = gtk_alignment_new (1.0, 0.5, 0.0, 0.0);
1093       break;
1094
1095     case GTK_JUSTIFY_CENTER:
1096       alignment = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
1097       break;
1098
1099     case GTK_JUSTIFY_FILL:
1100       alignment = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
1101       break;
1102     }
1103
1104   label = gtk_label_new (clist->column[column].title);
1105   gtk_container_add (GTK_CONTAINER (alignment), label);
1106   gtk_container_add (GTK_CONTAINER (clist->column[column].button), alignment);
1107   gtk_widget_show (label);
1108   gtk_widget_show (alignment);
1109
1110   /* if this button didn't previously exist, then the
1111    * column button positions have to be re-computed */
1112   if (GTK_WIDGET_VISIBLE (clist) && new_button)
1113     size_allocate_title_buttons (clist);
1114 }
1115
1116 void
1117 gtk_clist_set_column_widget (GtkCList * clist,
1118                              gint column,
1119                              GtkWidget * widget)
1120 {
1121   gint new_button = 0;
1122   GtkWidget *old_widget;
1123
1124   g_return_if_fail (clist != NULL);
1125   g_return_if_fail (GTK_IS_CLIST (clist));
1126
1127   if (column < 0 || column >= clist->columns)
1128     return;
1129
1130   /* if the column button doesn't currently exist,
1131    * it has to be created first */
1132   if (!clist->column[column].button)
1133     {
1134       column_button_create (clist, column);
1135       new_button = 1;
1136     }
1137
1138   column_title_new (clist, column, NULL);
1139
1140   /* remove and destroy the old widget */
1141   old_widget = GTK_BIN (clist->column[column].button)->child;
1142   if (old_widget)
1143     gtk_container_remove (GTK_CONTAINER (clist->column[column].button), old_widget);
1144
1145   /* add and show the widget */
1146   if (widget)
1147     {
1148       gtk_container_add (GTK_CONTAINER (clist->column[column].button), widget);
1149       gtk_widget_show (widget);
1150     }
1151
1152   /* if this button didn't previously exist, then the
1153    * column button positions have to be re-computed */
1154   if (GTK_WIDGET_VISIBLE (clist) && new_button)
1155     size_allocate_title_buttons (clist);
1156 }
1157
1158 void
1159 gtk_clist_set_column_justification (GtkCList * clist,
1160                                     gint column,
1161                                     GtkJustification justification)
1162 {
1163   GtkWidget *alignment;
1164
1165   g_return_if_fail (clist != NULL);
1166   g_return_if_fail (GTK_IS_CLIST (clist));
1167
1168   if (column < 0 || column >= clist->columns)
1169     return;
1170
1171   clist->column[column].justification = justification;
1172
1173   /* change the alinment of the button title if it's not a
1174    * custom widget */
1175   if (clist->column[column].title)
1176     {
1177       alignment = GTK_BIN (clist->column[column].button)->child;
1178
1179       switch (clist->column[column].justification)
1180         {
1181         case GTK_JUSTIFY_LEFT:
1182           gtk_alignment_set (GTK_ALIGNMENT (alignment), 0.0, 0.5, 0.0, 0.0);
1183           break;
1184
1185         case GTK_JUSTIFY_RIGHT:
1186           gtk_alignment_set (GTK_ALIGNMENT (alignment), 1.0, 0.5, 0.0, 0.0);
1187           break;
1188
1189         case GTK_JUSTIFY_CENTER:
1190           gtk_alignment_set (GTK_ALIGNMENT (alignment), 0.5, 0.5, 0.0, 0.0);
1191           break;
1192
1193         case GTK_JUSTIFY_FILL:
1194           gtk_alignment_set (GTK_ALIGNMENT (alignment), 0.5, 0.5, 0.0, 0.0);
1195           break;
1196
1197         default:
1198           break;
1199         }
1200     }
1201
1202   if (!GTK_CLIST_FROZEN (clist))
1203     draw_rows (clist, NULL);
1204 }
1205
1206 void
1207 gtk_clist_set_column_width (GtkCList * clist,
1208                             gint column,
1209                             gint width)
1210 {
1211   g_return_if_fail (clist != NULL);
1212   g_return_if_fail (GTK_IS_CLIST (clist));
1213
1214   if (column < 0 || column >= clist->columns)
1215     return;
1216
1217   clist->column[column].width = width;
1218   clist->column[column].width_set = TRUE;
1219
1220   /* FIXME: this is quite expensive to do if the widget hasn't
1221    *        been size_allocated yet, and pointless. Should
1222    *        a flag be kept
1223    */
1224   size_allocate_columns (clist);
1225   size_allocate_title_buttons (clist);
1226
1227   if (!GTK_CLIST_FROZEN (clist))
1228     {
1229       adjust_scrollbars (clist);
1230       draw_rows (clist, NULL);
1231     }
1232 }
1233
1234 void
1235 gtk_clist_set_row_height (GtkCList * clist,
1236                           gint height)
1237 {
1238   gint text_height;
1239
1240   g_return_if_fail (clist != NULL);
1241   g_return_if_fail (GTK_IS_CLIST (clist));
1242
1243   if (height > 0)
1244     clist->row_height = height;
1245   else
1246     return;
1247
1248   GTK_CLIST_SET_FLAG (clist, CLIST_ROW_HEIGHT_SET);
1249   
1250   if (GTK_WIDGET_REALIZED (clist))
1251     {
1252       text_height = height - (GTK_WIDGET (clist)->style->font->ascent +
1253                               GTK_WIDGET (clist) ->style->font->descent + 1);
1254       clist->row_center_offset = (text_height / 2) + GTK_WIDGET (clist)->style->font->ascent + 1.5;
1255     }
1256       
1257   if (!GTK_CLIST_FROZEN (clist))
1258     {
1259       adjust_scrollbars (clist);
1260       draw_rows (clist, NULL);
1261     }
1262 }
1263
1264 void
1265 gtk_clist_moveto (GtkCList * clist,
1266                   gint row,
1267                   gint column,
1268                   gfloat row_align,
1269                   gfloat col_align)
1270 {
1271   g_return_if_fail (clist != NULL);
1272   g_return_if_fail (GTK_IS_CLIST (clist));
1273
1274   if (row < -1 || row >= clist->rows)
1275     return;
1276   if (column < -1 || column >= clist->columns)
1277     return;
1278
1279   row_align = CLAMP (row_align, 0, 1);
1280   col_align = CLAMP (col_align, 0, 1);
1281
1282   /* adjust horizontal scrollbar */
1283   if (column >= 0)
1284     {
1285       GtkAdjustment *adj;
1286       gint x;
1287
1288       adj = GTK_RANGE (clist->hscrollbar)->adjustment;
1289
1290       x = COLUMN_LEFT (clist, column) - CELL_SPACING - COLUMN_INSET -
1291         (col_align * (clist->clist_window_width - 2 * COLUMN_INSET -
1292                       CELL_SPACING - clist->column[column].area.width));
1293       if (x < 0)
1294         gtk_adjustment_set_value (adj, 0.0);
1295       else if (x > LIST_WIDTH (clist) - clist->clist_window_width)
1296         gtk_adjustment_set_value 
1297           (adj, LIST_WIDTH (clist) - clist->clist_window_width);
1298       else
1299         gtk_adjustment_set_value (adj, x);
1300     }
1301
1302   /* adjust vertical scrollbar */
1303   if (row >= 0)
1304     move_vertical (clist, row, row_align);
1305 }
1306
1307 GtkCellType 
1308 gtk_clist_get_cell_type (GtkCList * clist,
1309                          gint row,
1310                          gint column)
1311 {
1312   GtkCListRow *clist_row;
1313
1314   g_return_val_if_fail (clist != NULL, -1);
1315   g_return_val_if_fail (GTK_IS_CLIST (clist), -1);
1316
1317   if (row < 0 || row >= clist->rows)
1318     return -1;
1319   if (column < 0 || column >= clist->columns)
1320     return -1;
1321
1322   clist_row = (g_list_nth (clist->row_list, row))->data;
1323
1324   return clist_row->cell[column].type;
1325 }
1326
1327 void
1328 gtk_clist_set_text (GtkCList * clist,
1329                     gint row,
1330                     gint column,
1331                     gchar * text)
1332 {
1333   GtkCListRow *clist_row;
1334
1335   g_return_if_fail (clist != NULL);
1336   g_return_if_fail (GTK_IS_CLIST (clist));
1337
1338   if (row < 0 || row >= clist->rows)
1339     return;
1340   if (column < 0 || column >= clist->columns)
1341     return;
1342
1343   clist_row = (g_list_nth (clist->row_list, row))->data;
1344
1345   /* if text is null, then the cell is empty */
1346   if (text)
1347     cell_set_text (clist, clist_row, column, text);
1348   else
1349     cell_empty (clist, clist_row, column);
1350
1351   /* redraw the list if it's not frozen */
1352   if (!GTK_CLIST_FROZEN (clist))
1353     {
1354       if (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
1355         GTK_CLIST_CLASS_FW (clist)->draw_row (clist, NULL, row, clist_row);
1356     }
1357 }
1358
1359 gint
1360 gtk_clist_get_text (GtkCList * clist,
1361                     gint row,
1362                     gint column,
1363                     gchar ** text)
1364 {
1365   GtkCListRow *clist_row;
1366
1367   g_return_val_if_fail (clist != NULL, 0);
1368   g_return_val_if_fail (GTK_IS_CLIST (clist), 0);
1369
1370   if (row < 0 || row >= clist->rows)
1371     return 0;
1372   if (column < 0 || column >= clist->columns)
1373     return 0;
1374
1375   clist_row = (g_list_nth (clist->row_list, row))->data;
1376
1377   if (clist_row->cell[column].type != GTK_CELL_TEXT)
1378     return 0;
1379
1380   if (text)
1381     *text = GTK_CELL_TEXT (clist_row->cell[column])->text;
1382
1383   return 1;
1384 }
1385
1386 void
1387 gtk_clist_set_pixmap (GtkCList * clist,
1388                       gint row,
1389                       gint column,
1390                       GdkPixmap * pixmap,
1391                       GdkBitmap * mask)
1392 {
1393   GtkCListRow *clist_row;
1394
1395   g_return_if_fail (clist != NULL);
1396   g_return_if_fail (GTK_IS_CLIST (clist));
1397
1398   if (row < 0 || row >= clist->rows)
1399     return;
1400   if (column < 0 || column >= clist->columns)
1401     return;
1402
1403   clist_row = (g_list_nth (clist->row_list, row))->data;
1404   
1405   gdk_pixmap_ref (pixmap);
1406   
1407   if (mask) gdk_pixmap_ref (mask);
1408   
1409   cell_set_pixmap (clist, clist_row, column, pixmap, mask);
1410
1411   /* redraw the list if it's not frozen */
1412   if (!GTK_CLIST_FROZEN (clist))
1413     {
1414       if (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
1415         GTK_CLIST_CLASS_FW (clist)->draw_row (clist, NULL, row, clist_row);
1416     }
1417 }
1418
1419 gint
1420 gtk_clist_get_pixmap (GtkCList * clist,
1421                       gint row,
1422                       gint column,
1423                       GdkPixmap ** pixmap,
1424                       GdkBitmap ** mask)
1425 {
1426   GtkCListRow *clist_row;
1427
1428   g_return_val_if_fail (clist != NULL, 0);
1429   g_return_val_if_fail (GTK_IS_CLIST (clist), 0);
1430
1431   if (row < 0 || row >= clist->rows)
1432     return 0;
1433   if (column < 0 || column >= clist->columns)
1434     return 0;
1435
1436   clist_row = (g_list_nth (clist->row_list, row))->data;
1437
1438   if (clist_row->cell[column].type != GTK_CELL_PIXMAP)
1439     return 0;
1440
1441   if (pixmap)
1442   {
1443     *pixmap = GTK_CELL_PIXMAP (clist_row->cell[column])->pixmap;
1444     /* mask can be NULL */
1445     *mask = GTK_CELL_PIXMAP (clist_row->cell[column])->mask;
1446   }
1447
1448   return 1;
1449 }
1450
1451 void
1452 gtk_clist_set_pixtext (GtkCList * clist,
1453                        gint row,
1454                        gint column,
1455                        gchar * text,
1456                        guint8 spacing,
1457                        GdkPixmap * pixmap,
1458                        GdkBitmap * mask)
1459 {
1460   GtkCListRow *clist_row;
1461
1462   g_return_if_fail (clist != NULL);
1463   g_return_if_fail (GTK_IS_CLIST (clist));
1464
1465   if (row < 0 || row >= clist->rows)
1466     return;
1467   if (column < 0 || column >= clist->columns)
1468     return;
1469
1470   clist_row = (g_list_nth (clist->row_list, row))->data;
1471   
1472   gdk_pixmap_ref (pixmap);
1473   if (mask) gdk_pixmap_ref (mask);
1474   cell_set_pixtext (clist, clist_row, column, text, spacing, pixmap, mask);
1475
1476   /* redraw the list if it's not frozen */
1477   if (!GTK_CLIST_FROZEN (clist))
1478     {
1479       if (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
1480         GTK_CLIST_CLASS_FW (clist)->draw_row (clist, NULL, row, clist_row);
1481     }
1482 }
1483
1484 gint
1485 gtk_clist_get_pixtext (GtkCList * clist,
1486                        gint row,
1487                        gint column,
1488                        gchar ** text,
1489                        guint8 * spacing,
1490                        GdkPixmap ** pixmap,
1491                        GdkBitmap ** mask)
1492 {
1493   GtkCListRow *clist_row;
1494
1495   g_return_val_if_fail (clist != NULL, 0);
1496   g_return_val_if_fail (GTK_IS_CLIST (clist), 0);
1497
1498   if (row < 0 || row >= clist->rows)
1499     return 0;
1500   if (column < 0 || column >= clist->columns)
1501     return 0;
1502
1503   clist_row = (g_list_nth (clist->row_list, row))->data;
1504
1505   if (clist_row->cell[column].type != GTK_CELL_PIXTEXT)
1506     return 0;
1507
1508   if (text)
1509     *text = GTK_CELL_PIXTEXT (clist_row->cell[column])->text;
1510   if (spacing)
1511     *spacing = GTK_CELL_PIXTEXT (clist_row->cell[column])->spacing;
1512   if (pixmap)
1513     *pixmap = GTK_CELL_PIXTEXT (clist_row->cell[column])->pixmap;
1514
1515   /* mask can be NULL */
1516   *mask = GTK_CELL_PIXTEXT (clist_row->cell[column])->mask;
1517
1518   return 1;
1519 }
1520
1521 void
1522 gtk_clist_set_foreground (GtkCList * clist,
1523                           gint row,
1524                           GdkColor * color)
1525 {
1526   GtkCListRow *clist_row;
1527
1528   g_return_if_fail (clist != NULL);
1529   g_return_if_fail (GTK_IS_CLIST (clist));
1530
1531   if (row < 0 || row >= clist->rows)
1532     return;
1533
1534   clist_row = (g_list_nth (clist->row_list, row))->data;
1535
1536   if (color)
1537     {
1538       clist_row->foreground = *color;
1539       clist_row->fg_set = TRUE;
1540     }
1541   else
1542     clist_row->fg_set = FALSE;
1543
1544   if (!GTK_CLIST_FROZEN (clist)
1545       && (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE))
1546     GTK_CLIST_CLASS_FW (clist)->draw_row (clist, NULL, row, clist_row);
1547 }
1548
1549 void
1550 gtk_clist_set_background (GtkCList * clist,
1551                           gint row,
1552                           GdkColor * color)
1553 {
1554   GtkCListRow *clist_row;
1555
1556   g_return_if_fail (clist != NULL);
1557   g_return_if_fail (GTK_IS_CLIST (clist));
1558
1559   if (row < 0 || row >= clist->rows)
1560     return;
1561
1562   clist_row = (g_list_nth (clist->row_list, row))->data;
1563
1564   if (color)
1565     {
1566       clist_row->background = *color;
1567       clist_row->bg_set = TRUE;
1568     }
1569   else
1570     clist_row->bg_set = FALSE;
1571
1572   if (!GTK_CLIST_FROZEN (clist)
1573       && (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE))
1574     GTK_CLIST_CLASS_FW (clist)->draw_row (clist, NULL, row, clist_row);
1575 }
1576
1577 void
1578 gtk_clist_set_shift (GtkCList * clist,
1579                      gint row,
1580                      gint column,
1581                      gint vertical,
1582                      gint horizontal)
1583 {
1584   GtkCListRow *clist_row;
1585
1586   g_return_if_fail (clist != NULL);
1587   g_return_if_fail (GTK_IS_CLIST (clist));
1588
1589   if (row < 0 || row >= clist->rows)
1590     return;
1591   if (column < 0 || column >= clist->columns)
1592     return;
1593
1594   clist_row = (g_list_nth (clist->row_list, row))->data;
1595
1596   clist_row->cell[column].vertical = vertical;
1597   clist_row->cell[column].horizontal = horizontal;
1598
1599   if (!GTK_CLIST_FROZEN (clist)
1600       && (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE))
1601     GTK_CLIST_CLASS_FW (clist)->draw_row (clist, NULL, row, clist_row);
1602 }
1603
1604 gint
1605 gtk_clist_append (GtkCList * clist,
1606                   gchar * text[])
1607 {
1608   gint i;
1609   GtkCListRow *clist_row;
1610
1611   g_return_val_if_fail (clist != NULL, -1);
1612   g_return_val_if_fail (GTK_IS_CLIST (clist), -1);
1613
1614   clist_row = row_new (clist);
1615   clist->rows++;
1616
1617   /* set the text in the row's columns */
1618   if (text)
1619     for (i = 0; i < clist->columns; i++)
1620       if (text[i])
1621         cell_set_text (clist, clist_row, i, text[i]);
1622
1623   /* keeps track of the end of the list so the list 
1624    * doesn't have to be traversed every time a item is added */
1625   if (!clist->row_list)
1626     {
1627       clist->row_list = g_list_append (clist->row_list, clist_row);
1628       clist->row_list_end = clist->row_list;
1629
1630       /* check the selection mode to see if we should select
1631        * the first row automaticly */
1632       switch (clist->selection_mode)
1633         {
1634         case GTK_SELECTION_BROWSE:
1635           select_row (clist, 0, -1, NULL);
1636           break;
1637
1638         default:
1639           break;
1640         }
1641     }
1642   else
1643     clist->row_list_end = (g_list_append (clist->row_list_end, clist_row))->next;
1644   
1645   /* redraw the list if it's not frozen */
1646   if (!GTK_CLIST_FROZEN (clist))
1647     {
1648       adjust_scrollbars (clist);
1649
1650       if (gtk_clist_row_is_visible (clist, clist->rows - 1) != GTK_VISIBILITY_NONE)
1651         draw_rows (clist, NULL);
1652     }
1653
1654   /* return index of the row */
1655   return clist->rows - 1;
1656 }
1657
1658 void
1659 gtk_clist_insert (GtkCList * clist,
1660                   gint row,
1661                   gchar * text[])
1662 {
1663   gint i;
1664   GtkCListRow *clist_row;
1665
1666   g_return_if_fail (clist != NULL);
1667   g_return_if_fail (GTK_IS_CLIST (clist));
1668   g_return_if_fail (text != NULL);
1669
1670   /* return if out of bounds */
1671   if (row < 0 || row > clist->rows)
1672     return;
1673
1674   if (clist->rows == 0)
1675     gtk_clist_append (clist, text);
1676   else
1677     {
1678       /* create the row */
1679       clist_row = row_new (clist);
1680
1681       /* set the text in the row's columns */
1682       if (text)
1683         for (i = 0; i < clist->columns; i++)
1684           if (text[i])
1685             cell_set_text (clist, clist_row, i, text[i]);
1686       
1687       /* reset the row end pointer if we're inserting at the
1688        * end of the list */
1689       if (row == clist->rows)
1690         clist->row_list_end = (g_list_append (clist->row_list_end, clist_row))->next;
1691       else
1692         clist->row_list = g_list_insert (clist->row_list, clist_row, row);
1693
1694       clist->rows++;
1695
1696       if (row < ROW_FROM_YPIXEL (clist, 0))
1697         clist->voffset -= (clist->row_height + CELL_SPACING);
1698
1699       /* syncronize the selection list */
1700       sync_selection (clist, row, SYNC_INSERT);
1701     }
1702
1703   /* redraw the list if it isn't frozen */
1704   if (!GTK_CLIST_FROZEN (clist))
1705     {
1706       adjust_scrollbars (clist);
1707
1708       if (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
1709         draw_rows (clist, NULL);
1710     }
1711 }
1712
1713 void
1714 gtk_clist_remove (GtkCList * clist,
1715                   gint row)
1716 {
1717   gint was_visible, was_selected;
1718   GList *list;
1719   GtkCListRow *clist_row;
1720
1721   g_return_if_fail (clist != NULL);
1722   g_return_if_fail (GTK_IS_CLIST (clist));
1723
1724   /* return if out of bounds */
1725   if (row < 0 || row > (clist->rows - 1))
1726     return;
1727
1728   was_visible = (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE);
1729   was_selected = 0;
1730
1731   /* get the row we're going to delete */
1732   list = g_list_nth (clist->row_list, row);
1733   clist_row = list->data;
1734
1735   /* if we're removing a selected row, we have to make sure
1736    * it's properly unselected, and then sync up the clist->selected
1737    * list to reflect the deincrimented indexies of rows after the
1738    * removal */
1739   if (clist_row->state == GTK_STATE_SELECTED)
1740     {
1741       switch (clist->selection_mode)
1742         {
1743         case GTK_SELECTION_SINGLE:
1744         case GTK_SELECTION_MULTIPLE:
1745         case GTK_SELECTION_EXTENDED:
1746           unselect_row (clist, row, -1, NULL);
1747           break;
1748
1749         case GTK_SELECTION_BROWSE:
1750           select_row (clist, row - 1, -1, NULL);
1751           break;
1752
1753         default:
1754           break;
1755         }
1756     }
1757
1758   /* reset the row end pointer if we're removing at the
1759    * end of the list */
1760   if (row == clist->rows - 1)
1761     clist->row_list_end = list->prev;
1762   if (row >= clist->focus_row && clist->focus_row >=0)
1763     clist->focus_row--;
1764
1765   clist->row_list = g_list_remove (clist->row_list, clist_row);
1766   clist->rows--;
1767   
1768   if (row < ROW_FROM_YPIXEL (clist, 0))
1769     clist->voffset += clist->row_height + CELL_SPACING;
1770
1771   sync_selection (clist, row, SYNC_REMOVE);
1772
1773   /* toast the row */
1774   row_delete (clist, clist_row);
1775
1776   /* redraw the row if it isn't frozen */
1777   if (!GTK_CLIST_FROZEN (clist))
1778     {
1779       adjust_scrollbars (clist);
1780
1781       if (was_visible)
1782         draw_rows (clist, NULL);
1783     }
1784 }
1785
1786 static void
1787 sync_selection (GtkCList * clist,
1788                 gint row,
1789                 gint mode)
1790 {
1791   GList *list;
1792   gint d;
1793
1794   if (mode == SYNC_INSERT)
1795     d = 1;
1796   else
1797     d = -1;
1798       
1799   if (clist->focus_row >= row)
1800     {
1801       clist->focus_row += d;
1802       if (clist->focus_row == -1 && clist->rows >= 1)
1803         clist->focus_row = 0;
1804     }
1805
1806   if (clist->selection_mode == GTK_SELECTION_BROWSE && clist->anchor != -1)
1807     GTK_CLIST_CLASS_FW (clist)->resync_selection (clist, NULL);
1808
1809   g_list_free (clist->undo_selection);
1810   g_list_free (clist->undo_unselection);
1811   clist->undo_selection = NULL;
1812   clist->undo_unselection = NULL;
1813
1814   clist->anchor = -1;
1815   clist->drag_pos = -1;
1816   clist->undo_anchor = clist->focus_row;
1817
1818   list = clist->selection;
1819   while (list)
1820     {
1821       if (GPOINTER_TO_INT (list->data) >= row)
1822         list->data = ((gchar*) list->data) + d;
1823       list = list->next;
1824     }
1825 }
1826
1827 void
1828 gtk_clist_clear (GtkCList * clist)
1829 {
1830   GTK_CLIST_CLASS_FW (clist)->clear (clist);
1831 }
1832
1833 static void
1834 real_clear (GtkCList * clist)
1835 {
1836   GList *list;
1837
1838   g_return_if_fail (clist != NULL);
1839   g_return_if_fail (GTK_IS_CLIST (clist));
1840
1841   /* remove all the rows */
1842   for (list = clist->row_list; list; list = list->next)
1843     row_delete (clist, GTK_CLIST_ROW (list));
1844
1845   g_list_free (clist->row_list);
1846
1847   /* free up the selection list */
1848   g_list_free (clist->selection);
1849   g_list_free (clist->undo_selection);
1850   g_list_free (clist->undo_unselection);
1851
1852   clist->row_list = NULL;
1853   clist->row_list_end = NULL;
1854   clist->selection = NULL;
1855   clist->selection_end = NULL;
1856   clist->undo_selection = NULL;
1857   clist->undo_unselection = NULL;
1858   clist->voffset = 0;
1859   clist->rows = 0;
1860   clist->focus_row = -1;
1861   clist->anchor = -1;
1862   clist->undo_anchor = -1;
1863   clist->anchor_state = GTK_STATE_SELECTED;
1864   clist->drag_pos = -1;
1865
1866   /* zero-out the scrollbars */
1867   if (clist->vscrollbar)
1868     {
1869       GTK_RANGE (clist->vscrollbar)->adjustment->value = 0.0;
1870       gtk_signal_emit_by_name (GTK_OBJECT (GTK_RANGE (clist->vscrollbar)->adjustment), "changed");
1871       
1872       if (!GTK_CLIST_FROZEN (clist))
1873         gtk_clist_thaw (clist);
1874     }
1875 }
1876
1877 void 
1878 gtk_clist_swap_rows (GtkCList * clist,
1879                      gint row1, 
1880                      gint row2)
1881 {
1882   gint first, last;
1883   GList *list, *link1, *link2;
1884   gpointer swap;
1885   
1886   g_return_if_fail (clist != NULL);
1887   g_return_if_fail (GTK_IS_CLIST (clist));
1888
1889   if (row1 < 0 || row1 > (clist->rows - 1))
1890     return;
1891
1892   if (row2 < 0 || row2 > (clist->rows - 1))
1893     return;
1894
1895   first = MIN (row1, row2);
1896   last = MAX (row1, row2);
1897
1898   link1 = g_list_nth (clist->row_list, first);
1899   link2 = g_list_nth (link1, row2 - row1);
1900
1901   swap = link1->data;
1902   link1->data = link2->data;
1903   link2->data = swap;
1904
1905   list = clist->selection;
1906   while (list)
1907     {
1908       if (GPOINTER_TO_INT (list->data) == row1)
1909         GPOINTER_TO_INT (list->data) = row2;
1910
1911        if (GPOINTER_TO_INT (list->data) == row2)
1912         GPOINTER_TO_INT (list->data) = row1;
1913
1914       list = list->next;
1915     }
1916
1917   if (!GTK_CLIST_FROZEN (clist))
1918     {
1919       if (gtk_clist_row_is_visible (clist, row1) != GTK_VISIBILITY_NONE)
1920         GTK_CLIST_CLASS_FW (clist)->draw_row
1921           (clist, NULL, row1, GTK_CLIST_ROW (link2));
1922
1923       if (gtk_clist_row_is_visible (clist, row2) != GTK_VISIBILITY_NONE)
1924         GTK_CLIST_CLASS_FW (clist)->draw_row
1925           (clist, NULL, row2, GTK_CLIST_ROW (link1));
1926     }
1927 }
1928
1929 void
1930 gtk_clist_set_row_data (GtkCList * clist,
1931                         gint row,
1932                         gpointer data)
1933 {
1934   gtk_clist_set_row_data_full (clist, row, data, NULL);
1935 }
1936
1937 void
1938 gtk_clist_set_row_data_full (GtkCList * clist,
1939                              gint row,
1940                              gpointer data,
1941                              GtkDestroyNotify destroy)
1942 {
1943   GtkCListRow *clist_row;
1944
1945   g_return_if_fail (clist != NULL);
1946   g_return_if_fail (GTK_IS_CLIST (clist));
1947
1948   if (row < 0 || row > (clist->rows - 1))
1949     return;
1950
1951   clist_row = (g_list_nth (clist->row_list, row))->data;
1952   clist_row->data = data;
1953   clist_row->destroy = destroy;
1954
1955   /* re-send the selected signal if data is changed/added
1956    * so the application can respond to the new data -- 
1957    * this could be questionable behavior */
1958   if (clist_row->state == GTK_STATE_SELECTED)
1959     select_row (clist, row, -1, NULL);
1960 }
1961
1962 gpointer
1963 gtk_clist_get_row_data (GtkCList * clist,
1964                         gint row)
1965 {
1966   GtkCListRow *clist_row;
1967
1968   g_return_val_if_fail (clist != NULL, NULL);
1969   g_return_val_if_fail (GTK_IS_CLIST (clist), NULL);
1970
1971   if (row < 0 || row > (clist->rows - 1))
1972     return NULL;
1973
1974   clist_row = (g_list_nth (clist->row_list, row))->data;
1975   return clist_row->data;
1976 }
1977
1978 gint
1979 gtk_clist_find_row_from_data (GtkCList * clist,
1980                               gpointer data)
1981 {
1982   GList *list;
1983   gint n;
1984
1985   g_return_val_if_fail (clist != NULL, -1);
1986   g_return_val_if_fail (GTK_IS_CLIST (clist), -1);
1987
1988   if (clist->rows < 1)
1989     return -1; /* is this an optimization or just worthless? */
1990
1991   for (n = 0, list = clist->row_list; list; n++, list = list->next)
1992     if (GTK_CLIST_ROW (list)->data == data)
1993       return n;
1994
1995   return -1;
1996 }
1997
1998 void
1999 gtk_clist_select_row (GtkCList * clist,
2000                       gint row,
2001                       gint column)
2002 {
2003   g_return_if_fail (clist != NULL);
2004   g_return_if_fail (GTK_IS_CLIST (clist));
2005
2006   if (row < 0 || row >= clist->rows)
2007     return;
2008
2009   if (column < -1 || column >= clist->columns)
2010     return;
2011
2012   select_row (clist, row, column, NULL);
2013 }
2014
2015 void
2016 gtk_clist_unselect_row (GtkCList * clist,
2017                         gint row,
2018                         gint column)
2019 {
2020   g_return_if_fail (clist != NULL);
2021   g_return_if_fail (GTK_IS_CLIST (clist));
2022
2023   if (row < 0 || row >= clist->rows)
2024     return;
2025
2026   if (column < -1 || column >= clist->columns)
2027     return;
2028
2029   unselect_row (clist, row, column, NULL);
2030 }
2031
2032 GtkVisibility
2033 gtk_clist_row_is_visible (GtkCList * clist,
2034                           gint row)
2035 {
2036   gint top;
2037
2038   g_return_val_if_fail (clist != NULL, 0);
2039   g_return_val_if_fail (GTK_IS_CLIST (clist), 0);
2040
2041   if (row < 0 || row >= clist->rows)
2042     return GTK_VISIBILITY_NONE;
2043
2044   if (clist->row_height == 0)
2045     return GTK_VISIBILITY_NONE;
2046
2047   if (row < ROW_FROM_YPIXEL (clist, 0))
2048     return GTK_VISIBILITY_NONE;
2049
2050   if (row > ROW_FROM_YPIXEL (clist, clist->clist_window_height))
2051     return GTK_VISIBILITY_NONE;
2052
2053   top = ROW_TOP_YPIXEL (clist, row);
2054
2055   if ((top < 0)
2056       || ((top + clist->row_height) >= clist->clist_window_height))
2057     return GTK_VISIBILITY_PARTIAL;
2058
2059   return GTK_VISIBILITY_FULL;
2060 }
2061
2062 GtkAdjustment *
2063 gtk_clist_get_vadjustment (GtkCList * clist)
2064 {
2065   g_return_val_if_fail (clist != NULL, NULL);
2066   g_return_val_if_fail (GTK_IS_CLIST (clist), NULL);
2067
2068   return gtk_range_get_adjustment (GTK_RANGE (clist->vscrollbar));
2069 }
2070
2071 GtkAdjustment *
2072 gtk_clist_get_hadjustment (GtkCList * clist)
2073 {
2074   g_return_val_if_fail (clist != NULL, NULL);
2075   g_return_val_if_fail (GTK_IS_CLIST (clist), NULL);
2076
2077   return gtk_range_get_adjustment (GTK_RANGE (clist->hscrollbar));
2078 }
2079
2080 void
2081 gtk_clist_set_policy (GtkCList * clist,
2082                       GtkPolicyType vscrollbar_policy,
2083                       GtkPolicyType hscrollbar_policy)
2084 {
2085   g_return_if_fail (clist != NULL);
2086   g_return_if_fail (GTK_IS_CLIST (clist));
2087
2088   if (clist->vscrollbar_policy != vscrollbar_policy)
2089     {
2090       clist->vscrollbar_policy = vscrollbar_policy;
2091
2092       if (GTK_WIDGET (clist)->parent)
2093         gtk_widget_queue_resize (GTK_WIDGET (clist));
2094     }
2095
2096   if (clist->hscrollbar_policy != hscrollbar_policy)
2097     {
2098       clist->hscrollbar_policy = hscrollbar_policy;
2099
2100       if (GTK_WIDGET (clist)->parent)
2101         gtk_widget_queue_resize (GTK_WIDGET (clist));
2102     }
2103 }
2104
2105 void
2106 gtk_clist_undo_selection (GtkCList *clist)
2107 {
2108   g_return_if_fail (clist != NULL);
2109   g_return_if_fail (GTK_IS_CLIST (clist));
2110
2111   if (clist->selection_mode == GTK_SELECTION_EXTENDED &&
2112       (clist->undo_selection || clist->undo_unselection))
2113     gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNDO_SELECTION]);
2114 }
2115
2116 static void
2117 real_undo_selection (GtkCList *clist)
2118 {
2119   GList *work;
2120
2121   g_return_if_fail (clist != NULL);
2122   g_return_if_fail (GTK_IS_CLIST (clist));
2123
2124   if (gdk_pointer_is_grabbed () ||
2125       clist->selection_mode != GTK_SELECTION_EXTENDED)
2126     return;
2127
2128   if (clist->anchor >= 0)
2129     GTK_CLIST_CLASS_FW (clist)->resync_selection (clist, NULL);
2130
2131   if (!(clist->undo_selection || clist->undo_unselection))
2132     {
2133       gtk_clist_unselect_all (clist);
2134       return;
2135     }
2136
2137   for (work = clist->undo_selection; work; work = work->next)
2138     gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
2139                      GPOINTER_TO_INT (work->data), -1, NULL);
2140
2141   for (work = clist->undo_unselection; work; work = work->next)
2142     gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW], 
2143                      GPOINTER_TO_INT (work->data), -1, NULL);
2144
2145   if (GTK_WIDGET_HAS_FOCUS (clist) && clist->focus_row != clist->undo_anchor)
2146     {
2147       gtk_clist_draw_focus (GTK_WIDGET (clist));
2148       clist->focus_row = clist->undo_anchor;
2149       gtk_clist_draw_focus (GTK_WIDGET (clist));
2150     }
2151   else
2152     clist->focus_row = clist->undo_anchor;
2153   
2154   clist->undo_anchor = -1;
2155  
2156   g_list_free (clist->undo_selection);
2157   g_list_free (clist->undo_unselection);
2158   clist->undo_selection = NULL;
2159   clist->undo_unselection = NULL;
2160
2161   if (ROW_TOP_YPIXEL (clist, clist->focus_row) + clist->row_height >
2162       clist->clist_window_height)
2163     gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
2164   else if (ROW_TOP_YPIXEL (clist, clist->focus_row) < 0)
2165     gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
2166 }
2167
2168 /*
2169  * GTKOBJECT
2170  *   gtk_clist_destroy
2171  *   gtk_clist_finalize
2172  */
2173 static void
2174 gtk_clist_destroy (GtkObject * object)
2175 {
2176   gint i;
2177   GtkCList *clist;
2178
2179   g_return_if_fail (object != NULL);
2180   g_return_if_fail (GTK_IS_CLIST (object));
2181
2182   clist = GTK_CLIST (object);
2183
2184   /* freeze the list */
2185   GTK_CLIST_SET_FLAG (clist, CLIST_FROZEN);
2186
2187   /* get rid of all the rows */
2188   gtk_clist_clear (clist);
2189
2190   /* Since we don't have a _remove method, unparent the children
2191    * instead of destroying them so the focus will be unset properly.
2192    * (For other containers, the _remove method takes care of the
2193    * unparent) The destroy will happen when the refcount drops
2194    * to zero.
2195    */
2196
2197   /* destroy the scrollbars */
2198   if (clist->vscrollbar)
2199     {
2200       gtk_widget_unparent (clist->vscrollbar);
2201       clist->vscrollbar = NULL;
2202     }
2203   if (clist->hscrollbar)
2204     {
2205       gtk_widget_unparent (clist->hscrollbar);
2206       clist->hscrollbar = NULL;
2207     }
2208
2209   if (clist->htimer)
2210     {
2211       gtk_timeout_remove (clist->htimer);
2212       clist->htimer = 0;
2213     }
2214   if (clist->vtimer)
2215     {
2216       gtk_timeout_remove (clist->vtimer);
2217       clist->vtimer = 0;
2218     }
2219
2220   /* destroy the column buttons */
2221   for (i = 0; i < clist->columns; i++)
2222     if (clist->column[i].button)
2223       {
2224         gtk_widget_unparent (clist->column[i].button);
2225         clist->column[i].button = NULL;
2226       }
2227
2228   if (GTK_OBJECT_CLASS (parent_class)->destroy)
2229     (*GTK_OBJECT_CLASS (parent_class)->destroy) (object);
2230 }
2231
2232 static void
2233 gtk_clist_finalize (GtkObject * object)
2234 {
2235   GtkCList *clist;
2236
2237   g_return_if_fail (object != NULL);
2238   g_return_if_fail (GTK_IS_CLIST (object));
2239
2240   clist = GTK_CLIST (object);
2241
2242   columns_delete (clist);
2243
2244   g_mem_chunk_destroy (clist->cell_mem_chunk);
2245   g_mem_chunk_destroy (clist->row_mem_chunk);
2246
2247   if (GTK_OBJECT_CLASS (parent_class)->finalize)
2248     (*GTK_OBJECT_CLASS (parent_class)->finalize) (object);
2249 }
2250
2251 /*
2252  * GTKWIDGET
2253  *   gtk_clist_realize
2254  *   gtk_clist_unrealize
2255  *   gtk_clist_map
2256  *   gtk_clist_unmap
2257  *   gtk_clist_draw
2258  *   gtk_clist_expose
2259  *   gtk_clist_button_press
2260  *   gtk_clist_button_release
2261  *   gtk_clist_button_motion
2262  *   gtk_clist_size_request
2263  *   gtk_clist_size_allocate
2264  */
2265 static void
2266 gtk_clist_realize (GtkWidget * widget)
2267 {
2268   gint i;
2269   GtkCList *clist;
2270   GdkWindowAttr attributes;
2271   gint attributes_mask;
2272   GdkGCValues values;
2273   gint border_width;
2274
2275   g_return_if_fail (widget != NULL);
2276   g_return_if_fail (GTK_IS_CLIST (widget));
2277
2278   clist = GTK_CLIST (widget);
2279
2280   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
2281
2282   add_style_data (clist);
2283
2284   border_width = GTK_CONTAINER (widget)->border_width;
2285   
2286   attributes.window_type = GDK_WINDOW_CHILD;
2287   attributes.x = widget->allocation.x + border_width;
2288   attributes.y = widget->allocation.y + border_width;
2289   attributes.width = widget->allocation.width - border_width * 2;
2290   attributes.height = widget->allocation.height - border_width * 2;
2291   attributes.wclass = GDK_INPUT_OUTPUT;
2292   attributes.visual = gtk_widget_get_visual (widget);
2293   attributes.colormap = gtk_widget_get_colormap (widget);
2294   attributes.event_mask = gtk_widget_get_events (widget);
2295   attributes.event_mask |= (GDK_EXPOSURE_MASK |
2296                             GDK_BUTTON_PRESS_MASK |
2297                             GDK_BUTTON_RELEASE_MASK |
2298                             GDK_KEY_PRESS_MASK |
2299                             GDK_KEY_RELEASE_MASK);
2300   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
2301
2302   /* main window */
2303   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
2304   gdk_window_set_user_data (widget->window, clist);
2305
2306   widget->style = gtk_style_attach (widget->style, widget->window);
2307
2308   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
2309
2310   /* column-title window */
2311
2312   attributes.x = clist->column_title_area.x;
2313   attributes.y = clist->column_title_area.y;
2314   attributes.width = clist->column_title_area.width;
2315   attributes.height = clist->column_title_area.height;
2316   
2317   clist->title_window = gdk_window_new (widget->window, &attributes, attributes_mask);
2318   gdk_window_set_user_data (clist->title_window, clist);
2319
2320   gtk_style_set_background (widget->style, clist->title_window, GTK_STATE_SELECTED);
2321   gdk_window_show (clist->title_window);
2322
2323   /* set things up so column buttons are drawn in title window */
2324   for (i = 0; i < clist->columns; i++)
2325     if (clist->column[i].button)
2326       gtk_widget_set_parent_window (clist->column[i].button, clist->title_window);
2327
2328   /* clist-window */
2329   attributes.x = clist->internal_allocation.x + widget->style->klass->xthickness;
2330   attributes.y = clist->internal_allocation.y + widget->style->klass->ythickness +
2331     clist->column_title_area.height;
2332   attributes.width = clist->clist_window_width;
2333   attributes.height = clist->clist_window_height;
2334   
2335   clist->clist_window = gdk_window_new (widget->window, &attributes, attributes_mask);
2336   gdk_window_set_user_data (clist->clist_window, clist);
2337
2338   gdk_window_set_background (clist->clist_window, &widget->style->bg[GTK_STATE_PRELIGHT]);
2339   gdk_window_show (clist->clist_window);
2340   gdk_window_get_size (clist->clist_window, &clist->clist_window_width,
2341                        &clist->clist_window_height);
2342
2343   /* create resize windows */
2344   attributes.wclass = GDK_INPUT_ONLY;
2345   attributes.event_mask = (GDK_BUTTON_PRESS_MASK |
2346                            GDK_BUTTON_RELEASE_MASK |
2347                            GDK_POINTER_MOTION_MASK |
2348                            GDK_POINTER_MOTION_HINT_MASK |
2349                            GDK_KEY_PRESS_MASK);
2350   attributes.cursor = clist->cursor_drag = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW);
2351   attributes_mask = GDK_WA_CURSOR;
2352   
2353   for (i = 0; i < clist->columns; i++)
2354     {
2355       clist->column[i].window = gdk_window_new (clist->title_window, &attributes, attributes_mask);
2356       gdk_window_set_user_data (clist->column[i].window, clist);
2357     }
2358
2359   /* This is slightly less efficient than creating them with the
2360    * right size to begin with, but easier
2361    */
2362   size_allocate_title_buttons (clist);
2363
2364   /* GCs */
2365   clist->fg_gc = gdk_gc_new (widget->window);
2366   clist->bg_gc = gdk_gc_new (widget->window);
2367   
2368   /* We'll use this gc to do scrolling as well */
2369   gdk_gc_set_exposures (clist->fg_gc, TRUE);
2370
2371   values.foreground = widget->style->white;
2372   values.function = GDK_XOR;
2373   values.subwindow_mode = GDK_INCLUDE_INFERIORS;
2374   clist->xor_gc = gdk_gc_new_with_values (widget->window,
2375                                           &values,
2376                                           GDK_GC_FOREGROUND |
2377                                           GDK_GC_FUNCTION |
2378                                           GDK_GC_SUBWINDOW);
2379 }
2380
2381 static void
2382 gtk_clist_unrealize (GtkWidget * widget)
2383 {
2384   gint i;
2385   GtkCList *clist;
2386
2387   g_return_if_fail (widget != NULL);
2388   g_return_if_fail (GTK_IS_CLIST (widget));
2389
2390   clist = GTK_CLIST (widget);
2391
2392   GTK_CLIST_SET_FLAG (clist, CLIST_FROZEN);
2393
2394   gdk_cursor_destroy (clist->cursor_drag);
2395   gdk_gc_destroy (clist->xor_gc);
2396   gdk_gc_destroy (clist->fg_gc);
2397   gdk_gc_destroy (clist->bg_gc);
2398
2399   for (i = 0; i < clist->columns; i++)
2400     if (clist->column[i].window)
2401       {
2402         gdk_window_set_user_data (clist->column[i].window, NULL);
2403         gdk_window_destroy (clist->column[i].window);
2404         clist->column[i].window = NULL;
2405       }
2406
2407   gdk_window_set_user_data (clist->clist_window, NULL);
2408   gdk_window_destroy (clist->clist_window);
2409   clist->clist_window = NULL;
2410
2411   gdk_window_set_user_data (clist->title_window, NULL);
2412   gdk_window_destroy (clist->title_window);
2413   clist->title_window = NULL;
2414
2415   clist->cursor_drag = NULL;
2416   clist->xor_gc = NULL;
2417   clist->fg_gc = NULL;
2418   clist->bg_gc = NULL;
2419
2420   if (GTK_WIDGET_CLASS (parent_class)->unrealize)
2421     (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
2422 }
2423
2424 static void
2425 gtk_clist_map (GtkWidget * widget)
2426 {
2427   gint i;
2428   GtkCList *clist;
2429
2430   g_return_if_fail (widget != NULL);
2431   g_return_if_fail (GTK_IS_CLIST (widget));
2432
2433   clist = GTK_CLIST (widget);
2434
2435   if (!GTK_WIDGET_MAPPED (widget))
2436     {
2437       GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
2438
2439       gdk_window_show (widget->window);
2440       gdk_window_show (clist->title_window);
2441       gdk_window_show (clist->clist_window);
2442
2443       /* map column buttons */
2444       for (i = 0; i < clist->columns; i++)
2445         if (clist->column[i].button &&
2446             GTK_WIDGET_VISIBLE (clist->column[i].button) &&
2447             !GTK_WIDGET_MAPPED (clist->column[i].button))
2448           gtk_widget_map (clist->column[i].button);
2449       
2450       /* map resize windows AFTER column buttons (above) */
2451       for (i = 0; i < clist->columns; i++)
2452         if (clist->column[i].window && clist->column[i].button)
2453           gdk_window_show (clist->column[i].window);
2454        
2455       /* map vscrollbars */
2456       if (GTK_WIDGET_VISIBLE (clist->vscrollbar) &&
2457           !GTK_WIDGET_MAPPED (clist->vscrollbar))
2458         gtk_widget_map (clist->vscrollbar);
2459
2460       if (GTK_WIDGET_VISIBLE (clist->hscrollbar) &&
2461           !GTK_WIDGET_MAPPED (clist->hscrollbar))
2462         gtk_widget_map (clist->hscrollbar);
2463
2464       /* unfreeze the list */
2465       GTK_CLIST_UNSET_FLAG (clist, CLIST_FROZEN);
2466     }
2467 }
2468
2469 static void
2470 gtk_clist_unmap (GtkWidget * widget)
2471 {
2472   gint i;
2473   GtkCList *clist;
2474
2475   g_return_if_fail (widget != NULL);
2476   g_return_if_fail (GTK_IS_CLIST (widget));
2477
2478   clist = GTK_CLIST (widget);
2479
2480   if (GTK_WIDGET_MAPPED (widget))
2481     {
2482       GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
2483
2484       for (i = 0; i < clist->columns; i++)
2485         if (clist->column[i].window)
2486           gdk_window_hide (clist->column[i].window);
2487
2488       gdk_window_hide (clist->clist_window);
2489       gdk_window_hide (clist->title_window);
2490       gdk_window_hide (widget->window);
2491
2492       /* unmap scrollbars */
2493       if (GTK_WIDGET_MAPPED (clist->vscrollbar))
2494         gtk_widget_unmap (clist->vscrollbar);
2495
2496       if (GTK_WIDGET_MAPPED (clist->hscrollbar))
2497         gtk_widget_unmap (clist->hscrollbar);
2498
2499       /* unmap column buttons */
2500       for (i = 0; i < clist->columns; i++)
2501         if (clist->column[i].button &&
2502             GTK_WIDGET_MAPPED (clist->column[i].button))
2503           gtk_widget_unmap (clist->column[i].button);
2504
2505       /* freeze the list */
2506       GTK_CLIST_SET_FLAG (clist, CLIST_FROZEN);
2507     }
2508 }
2509
2510 static void
2511 gtk_clist_draw (GtkWidget * widget,
2512                 GdkRectangle * area)
2513 {
2514   GtkCList *clist;
2515   gint border_width;
2516
2517   g_return_if_fail (widget != NULL);
2518   g_return_if_fail (GTK_IS_CLIST (widget));
2519   g_return_if_fail (area != NULL);
2520
2521   if (GTK_WIDGET_DRAWABLE (widget))
2522     {
2523       clist = GTK_CLIST (widget);
2524       border_width = GTK_CONTAINER (widget)->border_width;
2525
2526       gdk_window_clear_area (widget->window,
2527                              area->x - border_width, 
2528                              area->y - border_width,
2529                              area->width, area->height);
2530
2531       /* draw list shadow/border */
2532       gtk_draw_shadow (widget->style, widget->window,
2533                        GTK_STATE_NORMAL, clist->shadow_type,
2534                        0, 0, 
2535                        clist->clist_window_width + (2 * widget->style->klass->xthickness),
2536                        clist->clist_window_height + (2 * widget->style->klass->ythickness) +
2537                        clist->column_title_area.height);
2538
2539       gdk_window_clear_area (clist->clist_window,
2540                              0, 0, -1, -1);
2541
2542       draw_rows (clist, NULL);
2543     }
2544 }
2545
2546 static gint
2547 gtk_clist_expose (GtkWidget * widget,
2548                   GdkEventExpose * event)
2549 {
2550   GtkCList *clist;
2551
2552   g_return_val_if_fail (widget != NULL, FALSE);
2553   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
2554   g_return_val_if_fail (event != NULL, FALSE);
2555
2556   if (GTK_WIDGET_DRAWABLE (widget))
2557     {
2558       clist = GTK_CLIST (widget);
2559
2560       /* draw border */
2561       if (event->window == widget->window)
2562         gtk_draw_shadow (widget->style, widget->window,
2563                          GTK_STATE_NORMAL, clist->shadow_type,
2564                          0, 0,
2565                          clist->clist_window_width + (2 * widget->style->klass->xthickness),
2566                          clist->clist_window_height + (2 * widget->style->klass->ythickness) +
2567                          clist->column_title_area.height);
2568
2569       /* exposure events on the list */
2570       if (event->window == clist->clist_window)
2571         draw_rows (clist, &event->area);
2572     }
2573
2574   return FALSE;
2575 }
2576
2577 static gint
2578 gtk_clist_button_press (GtkWidget * widget,
2579                         GdkEventButton * event)
2580 {
2581   gint i;
2582   GtkCList *clist;
2583   gint x;
2584   gint y;
2585   gint row;
2586   gint column;
2587
2588   g_return_val_if_fail (widget != NULL, FALSE);
2589   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
2590   g_return_val_if_fail (event != NULL, FALSE);
2591
2592   clist = GTK_CLIST (widget);
2593
2594   /* we don't handle button 2 and 3 */
2595   if (event->button != 1)
2596     return FALSE;
2597   
2598   /* selections on the list */
2599   if (event->window == clist->clist_window)
2600     {
2601       x = event->x;
2602       y = event->y;
2603
2604       if (get_selection_info (clist, x, y, &row, &column))
2605         {
2606           gint old_row = clist->focus_row;
2607           gboolean no_focus_row = FALSE;
2608
2609           if (clist->focus_row == -1)
2610             {
2611               old_row = row;
2612               no_focus_row = TRUE;
2613             }
2614
2615           if (event->type == GDK_BUTTON_PRESS)
2616             {
2617               GTK_CLIST_SET_FLAG (clist, CLIST_DRAG_SELECTION);
2618               gdk_pointer_grab (clist->clist_window, FALSE,
2619                                 GDK_POINTER_MOTION_HINT_MASK |
2620                                 GDK_BUTTON1_MOTION_MASK |
2621                                 GDK_BUTTON_RELEASE_MASK,
2622                                 NULL, NULL, event->time);
2623             }
2624           else if (gdk_pointer_is_grabbed ())
2625             {
2626               GTK_CLIST_UNSET_FLAG (clist, CLIST_DRAG_SELECTION);
2627               gdk_pointer_ungrab (event->time);
2628             }
2629
2630           if (GTK_CLIST_ADD_MODE (clist))
2631             {
2632               GTK_CLIST_UNSET_FLAG (clist, CLIST_ADD_MODE);
2633               if (GTK_WIDGET_HAS_FOCUS (widget))
2634                 {
2635                   gtk_clist_draw_focus (widget);
2636                   gdk_gc_set_line_attributes (clist->xor_gc, 1,
2637                                               GDK_LINE_SOLID, 0, 0);
2638                   clist->focus_row = row;
2639                   gtk_clist_draw_focus (widget);
2640                 }
2641               else
2642                 {
2643                   gdk_gc_set_line_attributes (clist->xor_gc, 1,
2644                                               GDK_LINE_SOLID, 0, 0);
2645                   clist->focus_row = row;
2646                 }
2647             }
2648           else if (row != clist->focus_row)
2649             {
2650               if (GTK_WIDGET_HAS_FOCUS (widget))
2651                 {
2652                   gtk_clist_draw_focus (widget);
2653                   clist->focus_row = row;
2654                   gtk_clist_draw_focus (widget);
2655                 }
2656               else
2657                 clist->focus_row = row;
2658             }
2659
2660           if (!GTK_WIDGET_HAS_FOCUS (widget))
2661             gtk_widget_grab_focus (widget);
2662
2663           switch (clist->selection_mode)
2664             {
2665             case GTK_SELECTION_SINGLE:
2666             case GTK_SELECTION_MULTIPLE:
2667               if (event->type != GDK_BUTTON_PRESS)
2668                 select_row (clist, row, column, (GdkEvent *) event);
2669               else
2670                 clist->anchor = row;
2671               break;
2672
2673             case GTK_SELECTION_BROWSE:
2674               select_row (clist, row, column, (GdkEvent *) event);
2675               break;
2676
2677             case GTK_SELECTION_EXTENDED:
2678               if (event->type != GDK_BUTTON_PRESS)
2679                 {
2680                   if (clist->anchor != -1)
2681                     {
2682                       update_extended_selection (clist, clist->focus_row);
2683                       GTK_CLIST_CLASS_FW (clist)->resync_selection
2684                         (clist, (GdkEvent *) event);
2685                     }
2686                   select_row (clist, row, column, (GdkEvent *) event);
2687                   break;
2688                 }
2689               
2690               if (event->state & GDK_CONTROL_MASK)
2691                 {
2692                   if (event->state & GDK_SHIFT_MASK)
2693                     {
2694                       set_anchor (clist, TRUE, old_row, old_row);
2695                       update_extended_selection (clist, clist->focus_row);
2696                     }
2697                   else
2698                     {
2699                       if (clist->anchor == -1)
2700                         set_anchor (clist, TRUE, row, old_row);
2701                       else
2702                         update_extended_selection (clist, clist->focus_row);
2703                     }
2704                   break;
2705                 }
2706
2707               if (event->state & GDK_SHIFT_MASK)
2708                 {
2709                   set_anchor (clist, FALSE, old_row, old_row);
2710                   update_extended_selection (clist, clist->focus_row);
2711                   break;
2712                 }
2713
2714               if (clist->anchor == -1)
2715                 set_anchor (clist, FALSE, row, old_row);
2716               else
2717                 update_extended_selection (clist, clist->focus_row);
2718               break;
2719
2720             default:
2721               break;
2722             }
2723         }
2724
2725       return FALSE;
2726     }
2727
2728   /* press on resize windows */
2729   for (i = 0; i < clist->columns; i++)
2730     if (clist->column[i].window && event->window == clist->column[i].window)
2731       {
2732         GTK_CLIST_SET_FLAG (clist, CLIST_IN_DRAG);
2733         gtk_widget_get_pointer (widget, &clist->x_drag, NULL);
2734
2735         gdk_pointer_grab (clist->column[i].window, FALSE,
2736                           GDK_POINTER_MOTION_HINT_MASK |
2737                           GDK_BUTTON1_MOTION_MASK |
2738                           GDK_BUTTON_RELEASE_MASK,
2739                           NULL, NULL, event->time);
2740
2741         if (GTK_CLIST_ADD_MODE (clist))
2742           gdk_gc_set_line_attributes (clist->xor_gc, 1, GDK_LINE_SOLID, 0, 0);
2743
2744         draw_xor_line (clist);
2745         return FALSE;
2746       }
2747
2748   return FALSE;
2749 }
2750
2751 static gint
2752 gtk_clist_button_release (GtkWidget * widget,
2753                           GdkEventButton * event)
2754 {
2755   gint i, x, width, visible;
2756   GtkCList *clist;
2757
2758   g_return_val_if_fail (widget != NULL, FALSE);
2759   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
2760   g_return_val_if_fail (event != NULL, FALSE);
2761
2762   clist = GTK_CLIST (widget);
2763
2764   /* we don't handle button 2 and 3 */
2765   if (event->button != 1)
2766     return FALSE;
2767
2768   /* release on resize windows */
2769   if (GTK_CLIST_IN_DRAG (clist))
2770     for (i = 0; i < clist->columns; i++)
2771       if (clist->column[i].window && event->window == clist->column[i].window)
2772         {
2773           GTK_CLIST_UNSET_FLAG (clist, CLIST_IN_DRAG);
2774           gtk_widget_get_pointer (widget, &x, NULL);
2775           width = new_column_width (clist, i, &x, &visible);
2776           gdk_pointer_ungrab (event->time);
2777
2778           if (visible)
2779             draw_xor_line (clist);
2780
2781           if (GTK_CLIST_ADD_MODE (clist))
2782             {
2783               gdk_gc_set_line_attributes (clist->xor_gc, 1, 
2784                                           GDK_LINE_ON_OFF_DASH, 0, 0);
2785               gdk_gc_set_dashes (clist->xor_gc, 0, "\4\4", 2);
2786             }
2787
2788           resize_column (clist, i, width);
2789           return FALSE;
2790         }
2791
2792   if (GTK_CLIST_DRAG_SELECTION (clist))
2793     {
2794       gint row;
2795       gint column;
2796
2797       GTK_CLIST_UNSET_FLAG (clist, CLIST_DRAG_SELECTION);
2798       gdk_pointer_ungrab (event->time);
2799       if (clist->htimer)
2800         {
2801           gtk_timeout_remove (clist->htimer);
2802           clist->htimer = 0;
2803         }
2804       if (clist->vtimer)
2805         {
2806           gtk_timeout_remove (clist->vtimer);
2807           clist->vtimer = 0;
2808         }
2809       switch (clist->selection_mode)
2810         {
2811         case GTK_SELECTION_EXTENDED:
2812           if (!(event->state & GDK_SHIFT_MASK) ||
2813               event->x < 0 || event->x >= clist->clist_window_width ||
2814               event->y < 0 || event->y >= clist->clist_window_height)
2815             GTK_CLIST_CLASS_FW (clist)->resync_selection
2816               (clist, (GdkEvent *) event);
2817           break;
2818
2819         case GTK_SELECTION_SINGLE:
2820         case GTK_SELECTION_MULTIPLE:
2821           if (get_selection_info (clist, event->x, event->y, &row, &column))
2822             {
2823               if (clist->anchor == clist->focus_row)
2824                 toggle_row (clist, row, column, (GdkEvent *) event);
2825             }
2826           clist->anchor = -1;
2827           break;
2828
2829         default:
2830           break;
2831         }
2832     }
2833
2834   return FALSE;
2835 }
2836
2837 static gint
2838 horizontal_timeout (GtkCList *clist)
2839 {
2840   gint x, y;
2841   GdkEventMotion event;
2842   GdkModifierType mask;
2843
2844   g_return_val_if_fail (GTK_IS_CLIST (clist), FALSE);
2845
2846   clist->htimer = 0;
2847   gdk_window_get_pointer (clist->clist_window, &x, &y, &mask);
2848
2849   event.is_hint = 0;
2850   event.x = x;
2851   event.y = y;
2852   event.state = mask;
2853
2854   gtk_clist_motion (GTK_WIDGET (clist), &event);
2855
2856   return FALSE;
2857 }
2858
2859 static gint
2860 vertical_timeout (GtkCList *clist)
2861 {
2862   gint x, y;
2863   GdkEventMotion event;
2864   GdkModifierType mask;
2865
2866   g_return_val_if_fail (GTK_IS_CLIST (clist), FALSE);
2867
2868   clist->vtimer = 0;
2869   gdk_window_get_pointer (clist->clist_window, &x, &y, &mask);
2870
2871   event.is_hint = 0;
2872   event.x = x;
2873   event.y = y;
2874   event.state = mask;
2875
2876   gtk_clist_motion (GTK_WIDGET (clist), &event);
2877
2878   return FALSE;
2879 }
2880
2881 static void
2882 move_vertical (GtkCList *clist,
2883                gint row,
2884                gfloat align)
2885 {
2886   gint y;
2887   GtkAdjustment *adj;
2888
2889   adj = GTK_RANGE (clist->vscrollbar)->adjustment;
2890
2891   y = ROW_TOP_YPIXEL (clist, row) - clist->voffset;
2892   
2893   y = y - align * (clist->clist_window_height - clist->row_height)
2894     + (2 * align - 1) * CELL_SPACING;
2895   
2896   if (y + adj->page_size > adj->upper)
2897     adj->value = adj->upper - adj->page_size;
2898   else
2899     adj->value = y;
2900
2901   gtk_signal_emit_by_name (GTK_OBJECT (adj), "value_changed");
2902 }
2903
2904 static void
2905 move_horizontal (GtkCList *clist,
2906                  gint diff)
2907 {
2908   gfloat upper;
2909   GtkAdjustment *adj;
2910
2911   adj = GTK_RANGE (clist->hscrollbar)->adjustment;
2912
2913   adj->value += diff;
2914
2915   upper = adj->upper - adj->page_size;
2916   adj->value = MIN (adj->value, upper);
2917   adj->value = MAX (adj->value, 0.0);
2918
2919   gtk_signal_emit_by_name (GTK_OBJECT (adj), "value_changed");
2920 }
2921
2922 static gint
2923 gtk_clist_motion (GtkWidget * widget,
2924                   GdkEventMotion * event)
2925 {
2926   gint i, x, y, visible;
2927   GtkCList *clist;
2928
2929   g_return_val_if_fail (widget != NULL, FALSE);
2930   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
2931
2932   clist = GTK_CLIST (widget);
2933
2934   if (GTK_CLIST_IN_DRAG (clist))
2935     for (i = 0; i < clist->columns; i++)
2936       if (clist->column[i].window && event->window == clist->column[i].window)
2937         {
2938           if (event->is_hint || event->window != widget->window)
2939             gtk_widget_get_pointer (widget, &x, NULL);
2940           else
2941             x = event->x;
2942
2943           new_column_width (clist, i, &x, &visible);
2944           /* Welcome to my hack!  I'm going to use a value of x_drage = -99999 to
2945            * indicate the the xor line is already no visible */
2946           if (!visible && clist->x_drag != -99999)
2947             {
2948               draw_xor_line (clist);
2949               clist->x_drag = -99999;
2950             }
2951
2952           if (x != clist->x_drag && visible)
2953             {
2954               if (clist->x_drag != -99999)
2955                 draw_xor_line (clist);
2956
2957               clist->x_drag = x;
2958               draw_xor_line (clist);
2959             }
2960         }
2961
2962   if (GTK_CLIST_DRAG_SELECTION (clist))
2963     {
2964       gint row;
2965
2966       if (event->is_hint || event->window != clist->clist_window)
2967         gdk_window_get_pointer (clist->clist_window, &x, &y, NULL);
2968
2969       /* horizontal autoscrolling */
2970       if (LIST_WIDTH (clist) > clist->clist_window_width &&
2971           (x < 0 || x >= clist->clist_window_width))
2972         {
2973           if (clist->htimer == 0)
2974             {
2975               clist->htimer = gtk_timeout_add
2976                 (SCROLL_TIME, (GtkFunction) horizontal_timeout, clist);
2977
2978               if (!((x < 0 && GTK_RANGE (clist->hscrollbar)->adjustment->value 
2979                    == 0) || (x >= clist->clist_window_width &&
2980                   GTK_RANGE (clist->hscrollbar)->adjustment->value ==
2981                   LIST_WIDTH (clist) - clist->clist_window_width)))
2982                 {
2983                   if (x < 0)
2984                     move_horizontal (clist, -1 + (x/2));
2985                   else
2986                     move_horizontal (clist, 
2987                                      1 + (x - clist->clist_window_width) / 2);
2988                 }
2989             }
2990           else
2991             return FALSE;
2992         }
2993
2994       row = ROW_FROM_YPIXEL (clist, y);
2995
2996       /* don't scroll on last pixel row if it's a cell spacing */
2997       if (y == clist->clist_window_height-1 &&
2998           y == ROW_TOP_YPIXEL (clist, row-1) + clist->row_height)
2999         return FALSE;
3000
3001       /* vertical autoscrolling */
3002       if (LIST_HEIGHT (clist) > clist->clist_window_height &&
3003           (y < 0 || y >= clist->clist_window_height))
3004         {
3005           if (clist->vtimer == 0)
3006             {
3007               clist->vtimer = gtk_timeout_add (SCROLL_TIME, 
3008                                         (GtkFunction) vertical_timeout,
3009                                          clist);
3010
3011               if ((y < 0 && clist->focus_row == 0) || 
3012                   (y >= clist->clist_window_height && 
3013                    clist->focus_row == clist->rows-1))
3014                 return FALSE;
3015
3016               if (row < 0 && clist->focus_row > 0)
3017                 {
3018                   gtk_clist_draw_focus (widget);
3019                   clist->focus_row = 0;
3020                   gtk_clist_draw_focus (widget);
3021                 }
3022               else if (row > clist->rows - 1 && clist->focus_row 
3023                        < clist->rows - 1)
3024                 {
3025                   gtk_clist_draw_focus (widget);
3026                   clist->focus_row = clist->rows - 1;
3027                   gtk_clist_draw_focus (widget);
3028                 }
3029               else if (row >= 0 && row <= clist->rows - 1)
3030                 {
3031                   gtk_clist_draw_focus (widget);
3032                   clist->focus_row = row;
3033                   gtk_clist_draw_focus (widget);
3034                 }
3035               else
3036                 return FALSE;
3037
3038               switch (clist->selection_mode)
3039                 {
3040                 case GTK_SELECTION_BROWSE:
3041                   select_row (clist, clist->focus_row, -1, (GdkEvent *) event);
3042                   break;
3043
3044                 case GTK_SELECTION_EXTENDED:
3045                   update_extended_selection (clist, clist->focus_row);
3046                   break;
3047
3048                 default:
3049                   break;
3050                 }
3051
3052               if (y < 0)
3053                 move_vertical (clist, row, 0);
3054               else
3055                 move_vertical (clist, row, 1);
3056             }
3057           else
3058             return FALSE;
3059         }
3060
3061       if (row == clist->focus_row)
3062         return FALSE;
3063       
3064       /* dragging inside clist_window */
3065       if (row < 0 && clist->focus_row > 0)
3066         {
3067           gtk_clist_draw_focus (widget);
3068           clist->focus_row = 0;
3069           gtk_clist_draw_focus (widget);
3070         }
3071       else if (row > clist->rows-1 && clist->focus_row < clist->rows-1)
3072         {
3073           gtk_clist_draw_focus (widget);
3074           clist->focus_row = clist->rows-1;
3075           gtk_clist_draw_focus (widget);
3076         }
3077       else if (row >= 0 && row <= clist->rows-1)
3078         {
3079           gtk_clist_draw_focus (widget);
3080           clist->focus_row = row;
3081           gtk_clist_draw_focus (widget);
3082         }
3083       else
3084         return FALSE;
3085       
3086       switch (clist->selection_mode)
3087         {
3088         case GTK_SELECTION_EXTENDED:
3089           update_extended_selection (clist, clist->focus_row);
3090           return FALSE;
3091           
3092         case  GTK_SELECTION_BROWSE:
3093           select_row (clist, clist->focus_row, -1, (GdkEvent *) event);
3094           break;
3095           
3096         default:
3097           break;
3098         }
3099           
3100       if (ROW_TOP_YPIXEL(clist, clist->focus_row) + clist->row_height <= 0)
3101         gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
3102       else if (ROW_TOP_YPIXEL (clist, clist->focus_row) < 0)
3103         gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
3104       else if (ROW_TOP_YPIXEL (clist, clist->focus_row) >=
3105                clist->clist_window_height)
3106         gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
3107       else if (ROW_TOP_YPIXEL (clist, clist->focus_row) + clist->row_height >
3108                clist->clist_window_height)
3109         gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
3110     }
3111   return FALSE;
3112 }
3113
3114 static void
3115 gtk_clist_size_request (GtkWidget * widget,
3116                         GtkRequisition * requisition)
3117 {
3118   gint i;
3119   GtkCList *clist;
3120
3121   g_return_if_fail (widget != NULL);
3122   g_return_if_fail (GTK_IS_CLIST (widget));
3123   g_return_if_fail (requisition != NULL);
3124
3125   clist = GTK_CLIST (widget);
3126
3127   add_style_data (clist);
3128
3129   requisition->width = 0;
3130   requisition->height = 0;
3131
3132   /* compute the size of the column title (title) area */
3133   clist->column_title_area.height = 0;
3134   if (GTK_CLIST_SHOW_TITLES (clist))
3135     for (i = 0; i < clist->columns; i++)
3136       if (clist->column[i].button)
3137         {
3138           gtk_widget_size_request (clist->column[i].button, &clist->column[i].button->requisition);
3139           clist->column_title_area.height = MAX (clist->column_title_area.height,
3140                                                  clist->column[i].button->requisition.height);
3141         }
3142   requisition->height += clist->column_title_area.height;
3143
3144   /* add the vscrollbar space */
3145   if ((clist->vscrollbar_policy == GTK_POLICY_AUTOMATIC) ||
3146       GTK_WIDGET_VISIBLE (clist->vscrollbar))
3147     {
3148       gtk_widget_size_request (clist->vscrollbar, &clist->vscrollbar->requisition);
3149
3150       requisition->width += clist->vscrollbar->requisition.width + SCROLLBAR_SPACING (clist);
3151       requisition->height = MAX (requisition->height,
3152                                  clist->vscrollbar->requisition.height);
3153     }
3154
3155   /* add the hscrollbar space */
3156   if ((clist->hscrollbar_policy == GTK_POLICY_AUTOMATIC) ||
3157       GTK_WIDGET_VISIBLE (clist->hscrollbar))
3158     {
3159       gtk_widget_size_request (clist->hscrollbar, &clist->hscrollbar->requisition);
3160
3161       requisition->height += clist->hscrollbar->requisition.height + SCROLLBAR_SPACING (clist);
3162       requisition->width = MAX (clist->hscrollbar->requisition.width, 
3163                                 requisition->width - 
3164                                 clist->vscrollbar->requisition.width);
3165
3166     }
3167
3168   requisition->width += widget->style->klass->xthickness * 2 +
3169     GTK_CONTAINER (widget)->border_width * 2;
3170   requisition->height += widget->style->klass->ythickness * 2 +
3171     GTK_CONTAINER (widget)->border_width * 2;
3172 }
3173
3174 static void
3175 gtk_clist_size_allocate (GtkWidget * widget,
3176                          GtkAllocation * allocation)
3177 {
3178   GtkCList *clist;
3179   GtkAllocation clist_allocation;
3180   GtkAllocation child_allocation;
3181   gint i, vscrollbar_vis, hscrollbar_vis;
3182
3183   g_return_if_fail (widget != NULL);
3184   g_return_if_fail (GTK_IS_CLIST (widget));
3185   g_return_if_fail (allocation != NULL);
3186
3187   clist = GTK_CLIST (widget);
3188   widget->allocation = *allocation;
3189
3190   if (GTK_WIDGET_REALIZED (widget))
3191     {
3192       gdk_window_move_resize (widget->window,
3193                               allocation->x + GTK_CONTAINER (widget)->border_width,
3194                               allocation->y + GTK_CONTAINER (widget)->border_width,
3195                               allocation->width - GTK_CONTAINER (widget)->border_width * 2,
3196                               allocation->height - GTK_CONTAINER (widget)->border_width * 2);
3197     }
3198
3199   /* use internal allocation structure for all the math
3200    * because it's easier than always subtracting the container
3201    * border width */
3202   clist->internal_allocation.x = 0;
3203   clist->internal_allocation.y = 0;
3204   clist->internal_allocation.width = MAX (1, allocation->width -
3205     GTK_CONTAINER (widget)->border_width * 2);
3206   clist->internal_allocation.height = MAX (1, allocation->height -
3207     GTK_CONTAINER (widget)->border_width * 2);
3208         
3209   /* allocate clist window assuming no scrollbars */
3210   clist_allocation.x = clist->internal_allocation.x + widget->style->klass->xthickness;
3211   clist_allocation.y = clist->internal_allocation.y + widget->style->klass->ythickness +
3212     clist->column_title_area.height;
3213   clist_allocation.width = MAX (1, clist->internal_allocation.width - 
3214     (2 * widget->style->klass->xthickness));
3215   clist_allocation.height = MAX (1, clist->internal_allocation.height -
3216     (2 * widget->style->klass->ythickness) -
3217     clist->column_title_area.height);
3218   
3219   /* 
3220    * here's where we decide to show/not show the scrollbars
3221    */
3222   vscrollbar_vis = 0;
3223   hscrollbar_vis = 0;
3224   
3225   for (i = 0; i <= 1; i++)
3226     {
3227       if (LIST_HEIGHT (clist) <= clist_allocation.height &&
3228           clist->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
3229         {
3230           vscrollbar_vis = 0;
3231         }
3232       else
3233         {
3234           if (!vscrollbar_vis)
3235             {
3236               vscrollbar_vis = 1;
3237               clist_allocation.width = MAX (1, clist_allocation.width - 
3238                 (clist->vscrollbar->requisition.width +
3239                  SCROLLBAR_SPACING (clist)));
3240             }  
3241         }
3242       
3243       if (LIST_WIDTH (clist) <= clist_allocation.width &&
3244           clist->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
3245         {
3246           hscrollbar_vis = 0;
3247         }
3248       else
3249         {
3250           if (!hscrollbar_vis)
3251             {
3252               hscrollbar_vis = 1;
3253               clist_allocation.height = MAX (1, clist_allocation.height - 
3254                 (clist->hscrollbar->requisition.height +
3255                  SCROLLBAR_SPACING (clist)));
3256             }  
3257         }
3258     }
3259   
3260   clist->clist_window_width = clist_allocation.width;
3261   clist->clist_window_height = clist_allocation.height;
3262   
3263   if (GTK_WIDGET_REALIZED (widget))
3264     {
3265       gdk_window_move_resize (clist->clist_window,
3266                               clist_allocation.x,
3267                               clist_allocation.y,
3268                               clist_allocation.width,
3269                               clist_allocation.height);
3270     }
3271   
3272   /* position the window which holds the column title buttons */
3273   clist->column_title_area.x = widget->style->klass->xthickness;
3274   clist->column_title_area.y = widget->style->klass->ythickness;
3275   clist->column_title_area.width = clist_allocation.width;
3276   
3277   if (GTK_WIDGET_REALIZED (widget))
3278     {
3279       gdk_window_move_resize (clist->title_window,
3280                               clist->column_title_area.x,
3281                               clist->column_title_area.y,
3282                               clist->column_title_area.width,
3283                               clist->column_title_area.height);
3284     }
3285   
3286   /* column button allocation */
3287   size_allocate_columns (clist);
3288
3289   if (GTK_WIDGET_REALIZED (widget))
3290     size_allocate_title_buttons (clist);
3291
3292   adjust_scrollbars (clist);
3293   
3294   /* allocate the vscrollbar */
3295   if (vscrollbar_vis)
3296     {
3297       if (!GTK_WIDGET_VISIBLE (clist->vscrollbar))
3298         gtk_widget_show (clist->vscrollbar);
3299       
3300       child_allocation.x = clist->internal_allocation.x + 
3301         clist->internal_allocation.width -
3302         clist->vscrollbar->requisition.width;
3303       child_allocation.y = clist->internal_allocation.y;
3304       child_allocation.width = clist->vscrollbar->requisition.width;
3305       child_allocation.height = MAX (1, clist->internal_allocation.height -
3306         (hscrollbar_vis ? (clist->hscrollbar->requisition.height + SCROLLBAR_SPACING (clist)) : 0));
3307       
3308       gtk_widget_size_allocate (clist->vscrollbar, &child_allocation);
3309     }
3310   else
3311     {
3312       if (GTK_WIDGET_VISIBLE (clist->vscrollbar))
3313         gtk_widget_hide (clist->vscrollbar);
3314     }
3315   
3316   if (hscrollbar_vis)
3317     {
3318       if (!GTK_WIDGET_VISIBLE (clist->hscrollbar))
3319         gtk_widget_show (clist->hscrollbar);
3320       
3321       child_allocation.x = clist->internal_allocation.x;
3322       child_allocation.y = clist->internal_allocation.y +
3323         clist->internal_allocation.height -
3324         clist->hscrollbar->requisition.height;
3325       child_allocation.width = MAX (1, clist->internal_allocation.width -
3326         (vscrollbar_vis ? (clist->vscrollbar->requisition.width + SCROLLBAR_SPACING (clist)) : 0));
3327       child_allocation.height = clist->hscrollbar->requisition.height;
3328       
3329       gtk_widget_size_allocate (clist->hscrollbar, &child_allocation);
3330     }
3331   else
3332     {
3333       if (GTK_WIDGET_VISIBLE (clist->hscrollbar))
3334         gtk_widget_hide (clist->hscrollbar);
3335     }
3336
3337   /* set the vscrollbar adjustments */
3338   adjust_scrollbars (clist);
3339 }
3340
3341 /* 
3342  * GTKCONTAINER
3343  *   gtk_clist_foreach
3344  */
3345 static void
3346 gtk_clist_foreach (GtkContainer * container,
3347                    GtkCallback callback,
3348                    gpointer callback_data)
3349 {
3350   gint i;
3351   GtkCList *clist;
3352
3353   g_return_if_fail (container != NULL);
3354   g_return_if_fail (GTK_IS_CLIST (container));
3355   g_return_if_fail (callback != NULL);
3356
3357   clist = GTK_CLIST (container);
3358
3359   /* callback for the column buttons */
3360   for (i = 0; i < clist->columns; i++)
3361     if (clist->column[i].button)
3362       (*callback) (clist->column[i].button, callback_data);
3363
3364   /* callbacks for the scrollbars */
3365   if (clist->vscrollbar)
3366     (*callback) (clist->vscrollbar, callback_data);
3367   if (clist->hscrollbar)
3368     (*callback) (clist->hscrollbar, callback_data);
3369 }
3370
3371 /*
3372  * DRAWING
3373  *   draw_row
3374  *   draw_rows
3375  */
3376 static void
3377 draw_row (GtkCList * clist,
3378           GdkRectangle * area,
3379           gint row,
3380           GtkCListRow * clist_row)
3381 {
3382   GtkWidget *widget;
3383   GdkGC *fg_gc, *bg_gc;
3384   GdkRectangle row_rectangle, cell_rectangle, clip_rectangle, intersect_rectangle,
3385    *rect;
3386   gint i, offset = 0, width, height, pixmap_width = 0;
3387   gint xsrc, ysrc, xdest, ydest;
3388
3389   g_return_if_fail (clist != NULL);
3390
3391   /* bail now if we arn't drawable yet */
3392   if (!GTK_WIDGET_DRAWABLE (clist))
3393     return;
3394
3395   if (row < 0 || row >= clist->rows)
3396     return;
3397
3398   widget = GTK_WIDGET (clist);
3399
3400   /* if the function is passed the pointer to the row instead of null,
3401    * it avoids this expensive lookup */
3402   if (!clist_row)
3403     clist_row = (g_list_nth (clist->row_list, row))->data;
3404
3405   /* rectangle of the entire row */
3406   row_rectangle.x = 0;
3407   row_rectangle.y = ROW_TOP_YPIXEL (clist, row);
3408   row_rectangle.width = clist->clist_window_width;
3409   row_rectangle.height = clist->row_height;
3410
3411   /* rectangle of the cell spacing above the row */
3412   cell_rectangle.x = 0;
3413   cell_rectangle.y = row_rectangle.y - CELL_SPACING;
3414   cell_rectangle.width = row_rectangle.width;
3415   cell_rectangle.height = CELL_SPACING;
3416
3417   /* rectangle used to clip drawing operations, it's y and height
3418    * positions only need to be set once, so we set them once here. 
3419    * the x and width are set withing the drawing loop below once per
3420    * column */
3421   clip_rectangle.y = row_rectangle.y;
3422   clip_rectangle.height = row_rectangle.height;
3423
3424   /* select GC for background rectangle */
3425   if (clist_row->state == GTK_STATE_SELECTED)
3426     {
3427       fg_gc = widget->style->fg_gc[GTK_STATE_SELECTED];
3428       bg_gc = widget->style->bg_gc[GTK_STATE_SELECTED];
3429     }
3430   else
3431     {
3432       if (clist_row->fg_set)
3433         {
3434           gdk_gc_set_foreground (clist->fg_gc, &clist_row->foreground);
3435           fg_gc = clist->fg_gc;
3436         }
3437       else
3438         fg_gc = widget->style->fg_gc[GTK_STATE_NORMAL];
3439         
3440       if (clist_row->bg_set)
3441         {
3442           gdk_gc_set_foreground (clist->bg_gc, &clist_row->background);
3443           bg_gc = clist->bg_gc;
3444         }
3445       else
3446         bg_gc = widget->style->bg_gc[GTK_STATE_PRELIGHT];
3447     }
3448
3449   /* draw the cell borders and background */
3450   if (area)
3451     {
3452       if (gdk_rectangle_intersect (area, &cell_rectangle, &intersect_rectangle))
3453         gdk_draw_rectangle (clist->clist_window,
3454                             widget->style->base_gc[GTK_STATE_NORMAL],
3455                             TRUE,
3456                             intersect_rectangle.x,
3457                             intersect_rectangle.y,
3458                             intersect_rectangle.width,
3459                             intersect_rectangle.height);
3460
3461       /* the last row has to clear it's bottom cell spacing too */
3462       if (clist_row == clist->row_list_end->data)
3463         {
3464           cell_rectangle.y += clist->row_height + CELL_SPACING;
3465
3466           if (gdk_rectangle_intersect (area, &cell_rectangle, &intersect_rectangle))
3467             gdk_draw_rectangle (clist->clist_window,
3468                                 widget->style->base_gc[GTK_STATE_NORMAL],
3469                                 TRUE,
3470                                 intersect_rectangle.x,
3471                                 intersect_rectangle.y,
3472                                 intersect_rectangle.width,
3473                                 intersect_rectangle.height);
3474         }
3475
3476       if (!gdk_rectangle_intersect (area, &row_rectangle, &intersect_rectangle))
3477         return;
3478
3479       if (clist_row->state == GTK_STATE_SELECTED || clist_row->bg_set)
3480         gdk_draw_rectangle (clist->clist_window,
3481                             bg_gc,
3482                             TRUE,
3483                             intersect_rectangle.x,
3484                             intersect_rectangle.y,
3485                             intersect_rectangle.width,
3486                             intersect_rectangle.height);
3487       else
3488         gdk_window_clear_area (clist->clist_window,
3489                                intersect_rectangle.x,
3490                                intersect_rectangle.y,
3491                                intersect_rectangle.width,
3492                                intersect_rectangle.height);
3493     }
3494   else
3495     {
3496       gdk_draw_rectangle (clist->clist_window,
3497                           widget->style->base_gc[GTK_STATE_NORMAL],
3498                           TRUE,
3499                           cell_rectangle.x,
3500                           cell_rectangle.y,
3501                           cell_rectangle.width,
3502                           cell_rectangle.height);
3503
3504       /* the last row has to clear it's bottom cell spacing too */
3505       if (clist_row == clist->row_list_end->data)
3506         {
3507           cell_rectangle.y += clist->row_height + CELL_SPACING;
3508
3509           gdk_draw_rectangle (clist->clist_window,
3510                               widget->style->base_gc[GTK_STATE_NORMAL],
3511                               TRUE,
3512                               cell_rectangle.x,
3513                               cell_rectangle.y,
3514                               cell_rectangle.width,
3515                               cell_rectangle.height);     
3516         }         
3517
3518       if (clist_row->state == GTK_STATE_SELECTED || clist_row->bg_set)
3519         gdk_draw_rectangle (clist->clist_window,
3520                             bg_gc,
3521                             TRUE,
3522                             row_rectangle.x,
3523                             row_rectangle.y,
3524                             row_rectangle.width,
3525                             row_rectangle.height);
3526       else
3527         gdk_window_clear_area (clist->clist_window,
3528                                row_rectangle.x,
3529                                row_rectangle.y,
3530                                row_rectangle.width,
3531                                row_rectangle.height);
3532     }
3533
3534   /* iterate and draw all the columns (row cells) and draw their contents */
3535   for (i = 0; i < clist->columns; i++)
3536     {
3537       clip_rectangle.x = clist->column[i].area.x + clist->hoffset;
3538       clip_rectangle.width = clist->column[i].area.width;
3539
3540       /* calculate clipping region clipping region */
3541       if (!area)
3542         {
3543           rect = &clip_rectangle;
3544         }
3545       else
3546         {
3547           if (!gdk_rectangle_intersect (area, &clip_rectangle, 
3548                                         &intersect_rectangle))
3549             continue;
3550           rect = &intersect_rectangle;
3551         }
3552
3553       /* calculate real width for column justification */
3554       switch (clist_row->cell[i].type)
3555         {
3556         case GTK_CELL_EMPTY:
3557           continue;
3558           break;
3559
3560         case GTK_CELL_TEXT:
3561           width = gdk_string_width (GTK_WIDGET (clist)->style->font,
3562                                     GTK_CELL_TEXT (clist_row->cell[i])->text);
3563           break;
3564
3565         case GTK_CELL_PIXMAP:
3566           gdk_window_get_size (GTK_CELL_PIXMAP (clist_row->cell[i])->pixmap, &width, &height);
3567           pixmap_width = width;
3568           break;
3569
3570         case GTK_CELL_PIXTEXT:
3571           gdk_window_get_size (GTK_CELL_PIXTEXT (clist_row->cell[i])->pixmap, &width, &height);
3572           pixmap_width = width;
3573           width += GTK_CELL_PIXTEXT (clist_row->cell[i])->spacing;
3574           width = gdk_string_width (GTK_WIDGET (clist)->style->font,
3575                                     GTK_CELL_PIXTEXT (clist_row->cell[i])->text);
3576           break;
3577
3578         case GTK_CELL_WIDGET:
3579           /* unimplimented */
3580           continue;
3581           break;
3582
3583         default:
3584           continue;
3585           break;
3586         }
3587
3588       switch (clist->column[i].justification)
3589         {
3590         case GTK_JUSTIFY_LEFT:
3591           offset = clip_rectangle.x;
3592           break;
3593
3594         case GTK_JUSTIFY_RIGHT:
3595           offset = (clip_rectangle.x + clip_rectangle.width) - width;
3596           break;
3597
3598         case GTK_JUSTIFY_CENTER:
3599           offset = (clip_rectangle.x + (clip_rectangle.width / 2)) - (width / 2);
3600           break;
3601
3602         case GTK_JUSTIFY_FILL:
3603           offset = (clip_rectangle.x + (clip_rectangle.width / 2)) - (width / 2);
3604           break;
3605
3606         default:
3607           offset = 0;
3608           break;
3609         };
3610
3611       /* Draw Text or Pixmap */
3612       switch (clist_row->cell[i].type)
3613         {
3614         case GTK_CELL_EMPTY:
3615           continue;
3616           break;
3617
3618         case GTK_CELL_TEXT:
3619           gdk_gc_set_clip_rectangle (fg_gc, rect);
3620
3621           gdk_draw_string (clist->clist_window, 
3622                            widget->style->font,
3623                            fg_gc,
3624                            offset + clist_row->cell[i].horizontal,
3625                            row_rectangle.y + clist->row_center_offset + 
3626                            clist_row->cell[i].vertical,
3627                            GTK_CELL_TEXT (clist_row->cell[i])->text);
3628
3629           gdk_gc_set_clip_rectangle (fg_gc, NULL);
3630           break;
3631
3632         case GTK_CELL_PIXMAP:
3633           xsrc = 0;
3634           ysrc = 0;
3635           xdest = offset + clist_row->cell[i].horizontal;
3636           ydest = (clip_rectangle.y + (clip_rectangle.height / 2)) - height / 2 +
3637             clist_row->cell[i].vertical;
3638
3639           if (xdest < clip_rectangle.x)
3640             {
3641               xsrc = clip_rectangle.x - xdest;
3642               pixmap_width -= xsrc;
3643               xdest = clip_rectangle.x;
3644             }
3645
3646           if (xdest + pixmap_width > clip_rectangle.x + clip_rectangle.width)
3647             pixmap_width = (clip_rectangle.x + clip_rectangle.width) - xdest;
3648
3649           if (ydest < clip_rectangle.y)
3650             {
3651               ysrc = clip_rectangle.y - ydest;
3652               height -= ysrc;
3653               ydest = clip_rectangle.y;
3654             }
3655
3656           if (ydest + height > clip_rectangle.y + clip_rectangle.height)
3657             height = (clip_rectangle.y + clip_rectangle.height) - ydest;
3658
3659           if (GTK_CELL_PIXMAP (clist_row->cell[i])->mask)
3660           {
3661               gdk_gc_set_clip_mask (fg_gc, GTK_CELL_PIXMAP (clist_row->cell[i])->mask);
3662               gdk_gc_set_clip_origin (fg_gc, xdest, ydest);
3663           }
3664           gdk_draw_pixmap (clist->clist_window,
3665                            fg_gc,
3666                            GTK_CELL_PIXMAP (clist_row->cell[i])->pixmap,
3667                            xsrc, ysrc,
3668                            xdest, ydest,
3669                            pixmap_width, height);
3670
3671           if (GTK_CELL_PIXMAP (clist_row->cell[i])->mask)
3672           {
3673               gdk_gc_set_clip_origin (fg_gc, 0, 0);
3674               gdk_gc_set_clip_mask (fg_gc, NULL);
3675           }
3676           break;
3677
3678         case GTK_CELL_PIXTEXT:
3679           /* draw the pixmap */
3680           xsrc = 0;
3681           ysrc = 0;
3682           xdest = offset + clist_row->cell[i].horizontal;
3683           ydest = (clip_rectangle.y + (clip_rectangle.height / 2)) - height / 2 +
3684             clist_row->cell[i].vertical;
3685
3686           if (xdest < clip_rectangle.x)
3687             {
3688               xsrc = clip_rectangle.x - xdest;
3689               pixmap_width -= xsrc;
3690               xdest = clip_rectangle.x;
3691             }
3692
3693           if (xdest + pixmap_width > clip_rectangle.x + clip_rectangle.width)
3694             pixmap_width = (clip_rectangle.x + clip_rectangle.width) - xdest;
3695
3696           if (ydest < clip_rectangle.y)
3697             {
3698               ysrc = clip_rectangle.y - ydest;
3699               height -= ysrc;
3700               ydest = clip_rectangle.y;
3701             }
3702
3703           if (ydest + height > clip_rectangle.y + clip_rectangle.height)
3704             height = (clip_rectangle.y + clip_rectangle.height) - ydest;
3705
3706           if (GTK_CELL_PIXTEXT (clist_row->cell[i])->mask)
3707           {
3708               gdk_gc_set_clip_mask (fg_gc, GTK_CELL_PIXTEXT (clist_row->cell[i])->mask);
3709               gdk_gc_set_clip_origin (fg_gc, xdest, ydest);
3710           }
3711               
3712           gdk_draw_pixmap (clist->clist_window,
3713                            fg_gc,
3714                            GTK_CELL_PIXTEXT (clist_row->cell[i])->pixmap,
3715                            xsrc, ysrc,
3716                            xdest,
3717                            ydest,
3718                            pixmap_width, height);
3719
3720           gdk_gc_set_clip_origin (fg_gc, 0, 0);
3721
3722           offset += pixmap_width + GTK_CELL_PIXTEXT (clist_row->cell[i])->spacing;
3723           
3724           /* draw the string */
3725           gdk_gc_set_clip_rectangle (fg_gc, rect);
3726
3727           gdk_draw_string (clist->clist_window, 
3728                            widget->style->font,
3729                            fg_gc,
3730                            offset + clist_row->cell[i].horizontal,
3731                            row_rectangle.y + clist->row_center_offset + 
3732                            clist_row->cell[i].vertical,
3733                            GTK_CELL_PIXTEXT (clist_row->cell[i])->text);
3734
3735           gdk_gc_set_clip_rectangle (fg_gc, NULL);
3736           break;
3737
3738         case GTK_CELL_WIDGET:
3739           /* unimplimented */
3740           continue;
3741           break;
3742
3743         default:
3744           continue;
3745           break;
3746         }
3747     }
3748   if (clist->focus_row == row && GTK_WIDGET_HAS_FOCUS (widget))
3749     {
3750       if (area)
3751         {
3752           if (gdk_rectangle_intersect (area, &row_rectangle,
3753                                        &intersect_rectangle))
3754             {
3755               gdk_gc_set_clip_rectangle (clist->xor_gc, &intersect_rectangle);
3756               gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE,
3757                                   row_rectangle.x, row_rectangle.y,
3758                                   row_rectangle.width - 1,
3759                                   row_rectangle.height - 1);
3760               gdk_gc_set_clip_rectangle (clist->xor_gc, NULL);
3761             }
3762         }
3763       else
3764         gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE,
3765                             row_rectangle.x, row_rectangle.y,
3766                             row_rectangle.width - 1, row_rectangle.height - 1);
3767     }
3768 }
3769
3770 static void
3771 draw_rows (GtkCList * clist,
3772            GdkRectangle * area)
3773 {
3774   GList *list;
3775   GtkCListRow *clist_row;
3776   int i, first_row, last_row;
3777
3778   g_return_if_fail (clist != NULL);
3779   g_return_if_fail (GTK_IS_CLIST (clist));
3780
3781   if (clist->row_height == 0 ||
3782       !GTK_WIDGET_DRAWABLE (clist))
3783     return;
3784
3785   if (area)
3786     {
3787       first_row = ROW_FROM_YPIXEL (clist, area->y);
3788       last_row = ROW_FROM_YPIXEL (clist, area->y + area->height);
3789     }
3790   else
3791     {
3792       first_row = ROW_FROM_YPIXEL (clist, 0);
3793       last_row = ROW_FROM_YPIXEL (clist, clist->clist_window_height);
3794     }
3795
3796   /* this is a small special case which exposes the bottom cell line
3797    * on the last row -- it might go away if I change the wall the cell spacings
3798    * are drawn */
3799   if (clist->rows == first_row)
3800     first_row--;
3801
3802   list = g_list_nth (clist->row_list, first_row);
3803   i = first_row;
3804   while (list)
3805     {
3806       clist_row = list->data;
3807       list = list->next;
3808
3809       if (i > last_row)
3810         return;
3811
3812       GTK_CLIST_CLASS_FW (clist)->draw_row (clist, area, i, clist_row);
3813       i++;
3814     }
3815
3816   if (!area)
3817     gdk_window_clear_area (clist->clist_window, 0, ROW_TOP_YPIXEL (clist, i), -1, -1);
3818 }
3819
3820 /*
3821  * SIZE ALLOCATION
3822  *   size_allocate_title_buttons
3823  *   size_allocate_columns
3824  */
3825 static void
3826 size_allocate_title_buttons (GtkCList * clist)
3827 {
3828   gint i, last_button = 0;
3829   GtkAllocation button_allocation;
3830
3831   if (!GTK_WIDGET_REALIZED (clist))
3832     return;
3833
3834   button_allocation.x = clist->hoffset;
3835   button_allocation.y = 0;
3836   button_allocation.width = 0;
3837   button_allocation.height = clist->column_title_area.height;
3838
3839   for (i = 0; i < clist->columns; i++)
3840     {
3841       button_allocation.width += clist->column[i].area.width;
3842
3843       if (i == clist->columns - 1)
3844         button_allocation.width += 2 * (CELL_SPACING + COLUMN_INSET);
3845       else
3846         button_allocation.width += CELL_SPACING + (2 * COLUMN_INSET);
3847
3848       if (i == (clist->columns - 1) || clist->column[i + 1].button)
3849         {
3850           gtk_widget_size_allocate (clist->column[last_button].button, &button_allocation);
3851           button_allocation.x += button_allocation.width;
3852           button_allocation.width = 0;
3853
3854           gdk_window_show (clist->column[last_button].window);
3855           gdk_window_move_resize (clist->column[last_button].window,
3856                                   button_allocation.x - (DRAG_WIDTH / 2), 
3857                                   0, DRAG_WIDTH, clist->column_title_area.height);
3858           
3859           last_button = i + 1;
3860         }
3861       else
3862         {
3863           gdk_window_hide (clist->column[i].window);
3864         }
3865     }
3866 }
3867
3868 static void
3869 size_allocate_columns (GtkCList * clist)
3870 {
3871   gint i, xoffset = 0;
3872
3873   for (i = 0; i < clist->columns; i++)
3874     {
3875       clist->column[i].area.x = xoffset + CELL_SPACING + COLUMN_INSET;
3876
3877       if (i == clist->columns - 1)
3878         {
3879           gint width;
3880
3881           if (clist->column[i].width_set)
3882             {
3883               width = clist->column[i].width;
3884             }
3885           else
3886             {
3887               if (clist->column[i].title)
3888                 width = gdk_string_width (GTK_WIDGET (clist)->style->font, 
3889                                           clist->column[i].title);
3890               else
3891                 width = 0;
3892             }
3893
3894           clist->column[i].area.width = MAX (width,
3895                                              clist->clist_window_width -
3896                                              xoffset - (2 * (CELL_SPACING + COLUMN_INSET)));
3897                                             
3898         }
3899       else
3900         {
3901           clist->column[i].area.width = clist->column[i].width;
3902         }
3903
3904       xoffset += clist->column[i].area.width + CELL_SPACING + (2 * COLUMN_INSET);
3905     }
3906 }
3907
3908 /*
3909  * SELECTION
3910  *   select_row
3911  *   real_select_row
3912  *   real_unselect_row
3913  *   get_selection_info
3914  */
3915 static void
3916 toggle_row (GtkCList * clist,
3917             gint row,
3918             gint column,
3919             GdkEvent * event)
3920 {
3921   GtkCListRow *clist_row;
3922
3923   switch (clist->selection_mode)
3924     {
3925     case GTK_SELECTION_EXTENDED:
3926     case GTK_SELECTION_MULTIPLE:
3927     case GTK_SELECTION_SINGLE:
3928
3929       clist_row = g_list_nth (clist->row_list, row)->data;
3930       if (clist_row->state == GTK_STATE_SELECTED)
3931         {
3932           unselect_row (clist, row, column, event);
3933           return;
3934         }
3935
3936     case GTK_SELECTION_BROWSE:
3937       select_row (clist, row, column, event);
3938       break;
3939     }
3940 }
3941
3942 static void
3943 select_row (GtkCList * clist,
3944             gint row,
3945             gint column,
3946             GdkEvent * event)
3947 {
3948   gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
3949                    row, column, event);
3950 }
3951
3952 static void
3953 unselect_row (GtkCList * clist,
3954               gint row,
3955               gint column,
3956               GdkEvent * event)
3957 {
3958   gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW],
3959                    row, column, event);
3960 }
3961
3962 static void
3963 real_select_row (GtkCList * clist,
3964                  gint row,
3965                  gint column,
3966                  GdkEvent * event)
3967 {
3968   GtkCListRow *clist_row;
3969   GList *list;
3970   gint sel_row;
3971   gboolean row_selected;
3972
3973   g_return_if_fail (clist != NULL);
3974   g_return_if_fail (GTK_IS_CLIST (clist));
3975
3976   if (row < 0 || row > (clist->rows - 1))
3977     return;
3978
3979   switch (clist->selection_mode)
3980     {
3981     case GTK_SELECTION_SINGLE:
3982     case GTK_SELECTION_BROWSE:
3983
3984       row_selected = FALSE;
3985       list = clist->selection;
3986
3987       while (list)
3988         {
3989           sel_row = GPOINTER_TO_INT (list->data);
3990           list = list->next;
3991
3992           if (row == sel_row)
3993             row_selected = TRUE;
3994           else
3995             gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW], 
3996                              sel_row, column, event);
3997         }
3998
3999       if (row_selected)
4000         return;
4001       
4002     default:
4003       break;
4004     }
4005
4006   clist_row = (g_list_nth (clist->row_list, row))->data;
4007
4008   if (clist_row->state != GTK_STATE_NORMAL)
4009     return;
4010
4011   clist_row->state = GTK_STATE_SELECTED;
4012   if (!clist->selection)
4013     {
4014       clist->selection = g_list_append (clist->selection,
4015                                         GINT_TO_POINTER (row));
4016       clist->selection_end = clist->selection;
4017     }
4018   else
4019     clist->selection_end = 
4020       g_list_append (clist->selection_end, GINT_TO_POINTER (row))->next;
4021   
4022   if (!GTK_CLIST_FROZEN (clist)
4023       && (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE))
4024     GTK_CLIST_CLASS_FW (clist)->draw_row (clist, NULL, row, clist_row);
4025 }
4026
4027 static void
4028 real_unselect_row (GtkCList * clist,
4029                    gint row,
4030                    gint column,
4031                    GdkEvent * event)
4032 {
4033   GtkCListRow *clist_row;
4034
4035   g_return_if_fail (clist != NULL);
4036   g_return_if_fail (GTK_IS_CLIST (clist));
4037
4038   if (row < 0 || row > (clist->rows - 1))
4039     return;
4040
4041   clist_row = (g_list_nth (clist->row_list, row))->data;
4042
4043   if (clist_row->state == GTK_STATE_SELECTED)
4044     {
4045       clist_row->state = GTK_STATE_NORMAL;
4046
4047       if (clist->selection_end && 
4048           clist->selection_end->data == GINT_TO_POINTER (row))
4049         clist->selection_end = clist->selection_end->prev;
4050
4051       clist->selection = g_list_remove (clist->selection,
4052                                         GINT_TO_POINTER (row));
4053       
4054       if (!GTK_CLIST_FROZEN (clist)
4055           && (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE))
4056         GTK_CLIST_CLASS_FW (clist)->draw_row (clist, NULL, row, clist_row);
4057     }
4058 }
4059
4060 static gint
4061 get_selection_info (GtkCList * clist,
4062                     gint x,
4063                     gint y,
4064                     gint * row,
4065                     gint * column)
4066 {
4067   gint trow, tcol;
4068
4069   g_return_val_if_fail (clist != NULL, 0);
4070   g_return_val_if_fail (GTK_IS_CLIST (clist), 0);
4071
4072   /* bounds checking, return false if the user clicked 
4073    * on a blank area */
4074   trow = ROW_FROM_YPIXEL (clist, y);
4075   if (trow >= clist->rows)
4076     return 0;
4077
4078   if (row)
4079     *row = trow;
4080
4081   tcol = COLUMN_FROM_XPIXEL (clist, x);
4082   if (tcol >= clist->columns)
4083     return 0;
4084
4085   if (column)
4086     *column = tcol;
4087
4088   return 1;
4089 }
4090
4091 gint
4092 gtk_clist_get_selection_info (GtkCList *clist, 
4093                               gint      x, 
4094                               gint      y, 
4095                               gint *    row, 
4096                               gint *    column)
4097 {
4098   g_return_val_if_fail (clist != NULL, 0);
4099   g_return_val_if_fail (GTK_IS_CLIST (clist), 0);
4100   return get_selection_info (clist, x, y, row, column);
4101 }
4102
4103 /* 
4104  * RESIZE COLUMNS
4105  *   draw_xor_line
4106  *   new_column_width
4107  *   resize_column
4108  */
4109 static void                          
4110 draw_xor_line (GtkCList * clist)
4111 {
4112   GtkWidget *widget;
4113   
4114   g_return_if_fail (clist != NULL);
4115   
4116   widget = GTK_WIDGET (clist);
4117
4118   gdk_draw_line (widget->window, clist->xor_gc,  
4119                  clist->x_drag,                                       
4120                  widget->style->klass->ythickness,                               
4121                  clist->x_drag,                                             
4122                  clist->column_title_area.height + clist->clist_window_height + 1);
4123 }
4124
4125 /* this function returns the new width of the column being resized given
4126  * the column and x position of the cursor; the x cursor position is passed
4127  * in as a pointer and automagicly corrected if it's beyond min/max limits */
4128 static gint
4129 new_column_width (GtkCList * clist,
4130                   gint column,
4131                   gint * x,
4132                   gint * visible)
4133 {
4134   gint cx, rx, width;
4135
4136   cx = *x;
4137
4138   /* first translate the x position from widget->window
4139    * to clist->clist_window */
4140   cx -= GTK_WIDGET (clist)->style->klass->xthickness;
4141
4142   /* rx is x from the list beginning */
4143   rx = cx - clist->hoffset;
4144
4145   /* you can't shrink a column to less than its minimum width */
4146   if (cx < (COLUMN_LEFT_XPIXEL (clist, column) + CELL_SPACING + COLUMN_INSET + COLUMN_MIN_WIDTH))
4147     {
4148       *x = cx = COLUMN_LEFT_XPIXEL (clist, column) + CELL_SPACING + COLUMN_INSET + COLUMN_MIN_WIDTH +
4149         GTK_WIDGET (clist)->style->klass->xthickness;
4150       cx -= GTK_WIDGET (clist)->style->klass->xthickness;
4151       rx = cx - clist->hoffset;
4152     }
4153
4154   if (cx > clist->clist_window_width)
4155     *visible = 0;
4156   else
4157     *visible = 1;
4158
4159   /* calculate new column width making sure it doesn't end up
4160    * less than the minimum width */
4161   width = (rx - COLUMN_LEFT (clist, column)) - COLUMN_INSET -
4162     ((clist->columns == (column - 1)) ? CELL_SPACING : 0);
4163   if (width < COLUMN_MIN_WIDTH)
4164     width = COLUMN_MIN_WIDTH;
4165
4166   return width;
4167 }
4168
4169 /* this will do more later */
4170 static void
4171 resize_column (GtkCList * clist,
4172                gint column,
4173                gint width)
4174 {
4175   gtk_clist_set_column_width (clist, column, width);
4176 }
4177
4178 /* BUTTONS */
4179 static void
4180 column_button_create (GtkCList * clist,
4181                       gint column)
4182 {
4183   GtkWidget *button;
4184
4185   button = clist->column[column].button = gtk_button_new ();
4186   gtk_widget_set_parent (button, GTK_WIDGET (clist));
4187   if (GTK_WIDGET_REALIZED (clist) && clist->title_window)
4188     gtk_widget_set_parent_window (clist->column[column].button, clist->title_window);
4189   
4190   gtk_signal_connect (GTK_OBJECT (button), "clicked",
4191                       (GtkSignalFunc) column_button_clicked,
4192                       (gpointer) clist);
4193
4194   gtk_widget_show (button);
4195 }
4196
4197 static void
4198 column_button_clicked (GtkWidget * widget,
4199                        gpointer data)
4200 {
4201   gint i;
4202   GtkCList *clist;
4203
4204   g_return_if_fail (widget != NULL);
4205   g_return_if_fail (GTK_IS_CLIST (data));
4206
4207   clist = GTK_CLIST (data);
4208
4209   /* find the column who's button was pressed */
4210   for (i = 0; i < clist->columns; i++)
4211     if (clist->column[i].button == widget)
4212       break;
4213
4214   gtk_signal_emit (GTK_OBJECT (clist), clist_signals[CLICK_COLUMN], i);
4215 }
4216
4217 /* 
4218  * SCROLLBARS
4219  *
4220  * functions:
4221  *   create_scrollbars
4222  *   adjust_scrollbars
4223  *   vadjustment_changed
4224  *   hadjustment_changed
4225  *   vadjustment_value_changed
4226  *   hadjustment_value_changed 
4227  */
4228 static void
4229 create_scrollbars (GtkCList * clist)
4230 {
4231   GtkAdjustment *adjustment;
4232
4233   clist->vscrollbar = gtk_vscrollbar_new (NULL);
4234
4235   adjustment = gtk_range_get_adjustment (GTK_RANGE (clist->vscrollbar));
4236
4237   gtk_signal_connect (GTK_OBJECT (adjustment), "changed",
4238                       (GtkSignalFunc) vadjustment_changed,
4239                       (gpointer) clist);
4240
4241   gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
4242                       (GtkSignalFunc) vadjustment_value_changed,
4243                       (gpointer) clist);
4244
4245   gtk_widget_set_parent (clist->vscrollbar, GTK_WIDGET (clist));
4246   gtk_widget_show (clist->vscrollbar);
4247
4248   clist->hscrollbar = gtk_hscrollbar_new (NULL);
4249
4250   adjustment = gtk_range_get_adjustment (GTK_RANGE (clist->hscrollbar));
4251
4252   gtk_signal_connect (GTK_OBJECT (adjustment), "changed",
4253                       (GtkSignalFunc) hadjustment_changed,
4254                       (gpointer) clist);
4255
4256   gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
4257                       (GtkSignalFunc) hadjustment_value_changed,
4258                       (gpointer) clist);
4259
4260   gtk_widget_set_parent (clist->hscrollbar, GTK_WIDGET (clist));
4261   gtk_widget_show (clist->hscrollbar);
4262 }
4263
4264 static void
4265 adjust_scrollbars (GtkCList * clist)
4266 {
4267   GTK_RANGE (clist->vscrollbar)->adjustment->page_size = clist->clist_window_height;
4268   GTK_RANGE (clist->vscrollbar)->adjustment->page_increment = clist->clist_window_height / 2;
4269   GTK_RANGE (clist->vscrollbar)->adjustment->step_increment = 10;
4270   GTK_RANGE (clist->vscrollbar)->adjustment->lower = 0;
4271   GTK_RANGE (clist->vscrollbar)->adjustment->upper = LIST_HEIGHT (clist);
4272
4273   if (clist->clist_window_height - clist->voffset > LIST_HEIGHT (clist))
4274     {
4275       GTK_RANGE (clist->vscrollbar)->adjustment->value = MAX (0, LIST_HEIGHT (clist) - 
4276         clist->clist_window_height);
4277       gtk_signal_emit_by_name (GTK_OBJECT (GTK_RANGE (clist->vscrollbar)->adjustment), 
4278                                "value_changed");
4279     }
4280
4281   GTK_RANGE (clist->hscrollbar)->adjustment->page_size = clist->clist_window_width;
4282   GTK_RANGE (clist->hscrollbar)->adjustment->page_increment = clist->clist_window_width / 2;
4283   GTK_RANGE (clist->hscrollbar)->adjustment->step_increment = 10;
4284   GTK_RANGE (clist->hscrollbar)->adjustment->lower = 0;
4285   GTK_RANGE (clist->hscrollbar)->adjustment->upper = LIST_WIDTH (clist);
4286
4287   if (clist->clist_window_width - clist->hoffset > LIST_WIDTH (clist))
4288     {
4289       GTK_RANGE (clist->hscrollbar)->adjustment->value = MAX (0, LIST_WIDTH (clist) - 
4290         clist->clist_window_width);
4291       gtk_signal_emit_by_name (GTK_OBJECT (GTK_RANGE (clist->hscrollbar)->adjustment), 
4292                                "value_changed");
4293     }
4294
4295   if (LIST_HEIGHT (clist) <= clist->clist_window_height &&
4296       clist->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
4297     {
4298       if (GTK_WIDGET_VISIBLE (clist->vscrollbar))
4299         {
4300           gtk_widget_hide (clist->vscrollbar);
4301           gtk_widget_queue_resize (GTK_WIDGET (clist));
4302         }
4303     }
4304   else
4305     {
4306       if (!GTK_WIDGET_VISIBLE (clist->vscrollbar))
4307         {
4308           gtk_widget_show (clist->vscrollbar);
4309           gtk_widget_queue_resize (GTK_WIDGET (clist));
4310         }
4311     }
4312
4313   if (LIST_WIDTH (clist) <= clist->clist_window_width &&
4314       clist->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
4315     {
4316       if (GTK_WIDGET_VISIBLE (clist->hscrollbar))
4317         {
4318           gtk_widget_hide (clist->hscrollbar);
4319           gtk_widget_queue_resize (GTK_WIDGET (clist));
4320         }
4321     }
4322   else
4323     {
4324       if (!GTK_WIDGET_VISIBLE (clist->hscrollbar))
4325         {
4326           gtk_widget_show (clist->hscrollbar);
4327           gtk_widget_queue_resize (GTK_WIDGET (clist));
4328         }
4329     }
4330
4331   gtk_signal_emit_by_name (GTK_OBJECT (GTK_RANGE (clist->vscrollbar)->adjustment), "changed");
4332   gtk_signal_emit_by_name (GTK_OBJECT (GTK_RANGE (clist->hscrollbar)->adjustment), "changed");
4333 }
4334
4335 static void
4336 vadjustment_changed (GtkAdjustment * adjustment,
4337                                gpointer data)
4338 {
4339   GtkCList *clist;
4340
4341   g_return_if_fail (adjustment != NULL);
4342   g_return_if_fail (data != NULL);
4343
4344   clist = GTK_CLIST (data);
4345 }
4346
4347 static void
4348 hadjustment_changed (GtkAdjustment * adjustment,
4349                                gpointer data)
4350 {
4351   GtkCList *clist;
4352
4353   g_return_if_fail (adjustment != NULL);
4354   g_return_if_fail (data != NULL);
4355
4356   clist = GTK_CLIST (data);
4357 }
4358
4359 static void
4360 check_exposures (GtkCList *clist)
4361 {
4362   GdkEvent *event;
4363
4364   if (!GTK_WIDGET_REALIZED (clist))
4365     return;
4366
4367   /* Make sure graphics expose events are processed before scrolling
4368    * again */
4369   while ((event = gdk_event_get_graphics_expose (clist->clist_window)) != NULL)
4370     {
4371       gtk_widget_event (GTK_WIDGET (clist), event);
4372       if (event->expose.count == 0)
4373         {
4374           gdk_event_free (event);
4375           break;
4376         }
4377       gdk_event_free (event);
4378     }
4379 }
4380
4381 static void
4382 vadjustment_value_changed (GtkAdjustment * adjustment,
4383                                      gpointer data)
4384 {
4385   GtkCList *clist;
4386   GdkRectangle area;
4387   gint diff, value;
4388
4389   g_return_if_fail (adjustment != NULL);
4390   g_return_if_fail (data != NULL);
4391   g_return_if_fail (GTK_IS_CLIST (data));
4392
4393   clist = GTK_CLIST (data);
4394
4395   if (!GTK_WIDGET_DRAWABLE (clist))
4396     return;
4397
4398   value = adjustment->value;
4399
4400   if (adjustment == gtk_range_get_adjustment (GTK_RANGE (clist->vscrollbar)))
4401     {
4402       if (value > -clist->voffset)
4403         {
4404           /* scroll down */
4405           diff = value + clist->voffset;
4406
4407           /* we have to re-draw the whole screen here... */
4408           if (diff >= clist->clist_window_height)
4409             {
4410               clist->voffset = -value;
4411               draw_rows (clist, NULL);
4412               return;
4413             }
4414
4415           if ((diff != 0) && (diff != clist->clist_window_height))
4416             gdk_window_copy_area (clist->clist_window,
4417                                   clist->fg_gc,
4418                                   0, 0,
4419                                   clist->clist_window,
4420                                   0,
4421                                   diff,
4422                                   clist->clist_window_width,
4423                                   clist->clist_window_height - diff);
4424
4425           area.x = 0;
4426           area.y = clist->clist_window_height - diff;
4427           area.width = clist->clist_window_width;
4428           area.height = diff;
4429         }
4430       else
4431         {
4432           /* scroll up */
4433           diff = -clist->voffset - value;
4434
4435           /* we have to re-draw the whole screen here... */
4436           if (diff >= clist->clist_window_height)
4437             {
4438               clist->voffset = -value;
4439               draw_rows (clist, NULL);
4440               return;
4441             }
4442
4443           if ((diff != 0) && (diff != clist->clist_window_height))
4444             gdk_window_copy_area (clist->clist_window,
4445                                   clist->fg_gc,
4446                                   0, diff,
4447                                   clist->clist_window,
4448                                   0,
4449                                   0,
4450                                   clist->clist_window_width,
4451                                   clist->clist_window_height - diff);
4452
4453           area.x = 0;
4454           area.y = 0;
4455           area.width = clist->clist_window_width;
4456           area.height = diff;
4457
4458         }
4459
4460       clist->voffset = -value;
4461       if ((diff != 0) && (diff != clist->clist_window_height))
4462         check_exposures (clist);
4463     }
4464
4465   draw_rows (clist, &area);
4466 }
4467
4468 static void
4469 hadjustment_value_changed (GtkAdjustment * adjustment,
4470                                      gpointer data)
4471 {
4472   GtkCList *clist;
4473   GdkRectangle area;
4474   gint i;
4475   gint y = 0;
4476   gint diff = 0;
4477   gint value;
4478
4479   g_return_if_fail (adjustment != NULL);
4480   g_return_if_fail (data != NULL);
4481   g_return_if_fail (GTK_IS_CLIST (data));
4482
4483   clist = GTK_CLIST (data);
4484
4485   if (!GTK_WIDGET_DRAWABLE (clist) ||
4486       adjustment != gtk_range_get_adjustment (GTK_RANGE (clist->hscrollbar)))
4487     return;
4488
4489   value = adjustment->value;
4490   
4491   /* move the column buttons and resize windows */
4492   for (i = 0; i < clist->columns; i++)
4493     {
4494       if (clist->column[i].button)
4495         {
4496           clist->column[i].button->allocation.x -= value + clist->hoffset;
4497           
4498           if (clist->column[i].button->window)
4499             {
4500               gdk_window_move (clist->column[i].button->window,
4501                                clist->column[i].button->allocation.x,
4502                                clist->column[i].button->allocation.y);
4503               
4504               if (clist->column[i].window)
4505                 gdk_window_move (clist->column[i].window,
4506                                  clist->column[i].button->allocation.x +
4507                                  clist->column[i].button->allocation.width - 
4508                                  (DRAG_WIDTH / 2), 0); 
4509             }
4510         }
4511     }
4512
4513   if (value > -clist->hoffset)
4514     {
4515       /* scroll right */
4516       diff = value + clist->hoffset;
4517       
4518       clist->hoffset = -value;
4519       
4520       /* we have to re-draw the whole screen here... */
4521       if (diff >= clist->clist_window_width)
4522         {
4523           draw_rows (clist, NULL);
4524           return;
4525         }
4526
4527       if (GTK_WIDGET_HAS_FOCUS (clist) && !GTK_CLIST_CHILD_HAS_FOCUS (clist) &&
4528           GTK_CLIST_ADD_MODE (clist))
4529         {
4530           y = ROW_TOP_YPIXEL (clist, clist->focus_row);
4531               
4532           gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE, 0, y,
4533                               clist->clist_window_width - 1,
4534                               clist->row_height - 1);
4535         }
4536       gdk_window_copy_area (clist->clist_window,
4537                             clist->fg_gc,
4538                             0, 0,
4539                             clist->clist_window,
4540                             diff,
4541                             0,
4542                             clist->clist_window_width - diff,
4543                             clist->clist_window_height);
4544
4545       area.x = clist->clist_window_width - diff;
4546     }
4547   else
4548     {
4549       /* scroll left */
4550       if (!(diff = -clist->hoffset - value))
4551         return;
4552
4553       clist->hoffset = -value;
4554       
4555       /* we have to re-draw the whole screen here... */
4556       if (diff >= clist->clist_window_width)
4557         {
4558           draw_rows (clist, NULL);
4559           return;
4560         }
4561       
4562       if (GTK_WIDGET_HAS_FOCUS (clist) && !GTK_CLIST_CHILD_HAS_FOCUS (clist) &&
4563           GTK_CLIST_ADD_MODE (clist))
4564         {
4565           y = ROW_TOP_YPIXEL (clist, clist->focus_row);
4566           
4567           gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE, 0, y,
4568                               clist->clist_window_width - 1,
4569                               clist->row_height - 1);
4570         }
4571
4572       gdk_window_copy_area (clist->clist_window,
4573                             clist->fg_gc,
4574                             diff, 0,
4575                             clist->clist_window,
4576                             0,
4577                             0,
4578                             clist->clist_window_width - diff,
4579                             clist->clist_window_height);
4580           
4581       area.x = 0;
4582     }
4583
4584   area.y = 0;
4585   area.width = diff;
4586   area.height = clist->clist_window_height;
4587
4588   check_exposures (clist);
4589
4590   if (GTK_WIDGET_HAS_FOCUS (clist) && !GTK_CLIST_CHILD_HAS_FOCUS (clist))
4591     {
4592       if (GTK_CLIST_ADD_MODE (clist))
4593         {
4594           gint focus_row;
4595           
4596           focus_row = clist->focus_row;
4597           clist->focus_row = -1;
4598           draw_rows (clist, &area);
4599           clist->focus_row = focus_row;
4600           
4601           gdk_draw_rectangle (clist->clist_window, clist->xor_gc,
4602                               FALSE, 0, y, clist->clist_window_width - 1,
4603                               clist->row_height - 1);
4604           return;
4605         }
4606       else
4607         {
4608           gint x0;
4609           gint x1;
4610           
4611           if (area.x == 0)
4612             {
4613               x0 = clist->clist_window_width - 1;
4614               x1 = diff;
4615             }
4616           else
4617             {
4618               x0 = 0;
4619               x1 = area.x - 1;
4620             }
4621           
4622           y = ROW_TOP_YPIXEL (clist, clist->focus_row);
4623           gdk_draw_line (clist->clist_window, clist->xor_gc,
4624                          x0, y + 1, x0, y + clist->row_height - 2);
4625           gdk_draw_line (clist->clist_window, clist->xor_gc,
4626                          x1, y + 1, x1, y + clist->row_height - 2);
4627           
4628         }
4629     }
4630   draw_rows (clist, &area);
4631 }
4632
4633 /* 
4634  * Memory Allocation/Distruction Routines for GtkCList stuctures
4635  *
4636  * functions:
4637  *   columns_new
4638  *   column_title_new
4639  *   columns_delete
4640  *   row_new
4641  *   row_delete
4642  *   cell_empty
4643  *   cell_set_text
4644  *   cell_set_pixmap 
4645  */
4646 static GtkCListColumn *
4647 columns_new (GtkCList * clist)
4648 {
4649   gint i;
4650   GtkCListColumn *column;
4651
4652   column = g_new (GtkCListColumn, clist->columns);
4653
4654   for (i = 0; i < clist->columns; i++)
4655     {
4656       column[i].area.x = 0;
4657       column[i].area.y = 0;
4658       column[i].area.width = 0;
4659       column[i].area.height = 0;
4660       column[i].title = NULL;
4661       column[i].button = NULL;
4662       column[i].window = NULL;
4663       column[i].width = 0;
4664       column[i].width_set = FALSE;
4665       column[i].justification = GTK_JUSTIFY_LEFT;
4666     }
4667
4668   return column;
4669 }
4670
4671 static void
4672 column_title_new (GtkCList * clist,
4673                   gint column,
4674                   gchar * title)
4675 {
4676   if (clist->column[column].title)
4677     g_free (clist->column[column].title);
4678
4679   clist->column[column].title = g_strdup (title);
4680 }
4681
4682 static void
4683 columns_delete (GtkCList * clist)
4684 {
4685   gint i;
4686
4687   for (i = 0; i < clist->columns; i++)
4688     if (clist->column[i].title)
4689       g_free (clist->column[i].title);
4690       
4691   g_free (clist->column);
4692 }
4693
4694 static GtkCListRow *
4695 row_new (GtkCList * clist)
4696 {
4697   int i;
4698   GtkCListRow *clist_row;
4699
4700   clist_row = g_chunk_new (GtkCListRow, clist->row_mem_chunk);
4701   clist_row->cell = g_chunk_new (GtkCell, clist->cell_mem_chunk);
4702
4703   for (i = 0; i < clist->columns; i++)
4704     {
4705       clist_row->cell[i].type = GTK_CELL_EMPTY;
4706       clist_row->cell[i].vertical = 0;
4707       clist_row->cell[i].horizontal = 0;
4708     }
4709
4710   clist_row->fg_set = FALSE;
4711   clist_row->bg_set = FALSE;
4712   clist_row->state = GTK_STATE_NORMAL;
4713   clist_row->data = NULL;
4714   clist_row->destroy = NULL;
4715
4716   return clist_row;
4717 }
4718
4719 static void
4720 row_delete (GtkCList * clist,
4721             GtkCListRow * clist_row)
4722 {
4723   gint i;
4724
4725   for (i = 0; i < clist->columns; i++)
4726     cell_empty (clist, clist_row, i);
4727
4728   if (clist_row->destroy)
4729     clist_row->destroy (clist_row->data);
4730
4731   g_mem_chunk_free (clist->cell_mem_chunk, clist_row->cell);
4732   g_mem_chunk_free (clist->row_mem_chunk, clist_row);
4733 }
4734
4735 static void
4736 cell_empty (GtkCList * clist,
4737             GtkCListRow * clist_row,
4738             gint column)
4739 {
4740   switch (clist_row->cell[column].type)
4741     {
4742     case GTK_CELL_EMPTY:
4743       break;
4744       
4745     case GTK_CELL_TEXT:
4746       g_free (GTK_CELL_TEXT (clist_row->cell[column])->text);
4747       break;
4748       
4749     case GTK_CELL_PIXMAP:
4750       gdk_pixmap_unref (GTK_CELL_PIXMAP (clist_row->cell[column])->pixmap);
4751       if (GTK_CELL_PIXMAP (clist_row->cell[column])->mask)
4752           gdk_bitmap_unref (GTK_CELL_PIXMAP (clist_row->cell[column])->mask);
4753       break;
4754       
4755     case GTK_CELL_PIXTEXT:
4756       g_free (GTK_CELL_PIXTEXT (clist_row->cell[column])->text);
4757       gdk_pixmap_unref (GTK_CELL_PIXTEXT (clist_row->cell[column])->pixmap);
4758       if (GTK_CELL_PIXTEXT (clist_row->cell[column])->mask)
4759           gdk_bitmap_unref (GTK_CELL_PIXTEXT (clist_row->cell[column])->mask);
4760       break;
4761
4762     case GTK_CELL_WIDGET:
4763       /* unimplimented */
4764       break;
4765       
4766     default:
4767       break;
4768     }
4769
4770   clist_row->cell[column].type = GTK_CELL_EMPTY;
4771 }
4772
4773 static void
4774 cell_set_text (GtkCList * clist,
4775                GtkCListRow * clist_row,
4776                gint column,
4777                gchar * text)
4778 {
4779   cell_empty (clist, clist_row, column);
4780
4781   if (text)
4782     {
4783       clist_row->cell[column].type = GTK_CELL_TEXT;
4784       GTK_CELL_TEXT (clist_row->cell[column])->text = g_strdup (text);
4785     }
4786 }
4787
4788 static void
4789 cell_set_pixmap (GtkCList * clist,
4790                  GtkCListRow * clist_row,
4791                  gint column,
4792                  GdkPixmap * pixmap,
4793                  GdkBitmap * mask)
4794 {
4795   cell_empty (clist, clist_row, column);
4796
4797   if (pixmap)
4798     {
4799       clist_row->cell[column].type = GTK_CELL_PIXMAP;
4800       GTK_CELL_PIXMAP (clist_row->cell[column])->pixmap = pixmap;
4801       /* We set the mask even if it is NULL */
4802       GTK_CELL_PIXMAP (clist_row->cell[column])->mask = mask;
4803     }
4804 }
4805
4806 static void
4807 cell_set_pixtext (GtkCList * clist,
4808                   GtkCListRow * clist_row,
4809                   gint column,
4810                   gchar * text,
4811                   guint8 spacing,
4812                   GdkPixmap * pixmap,
4813                   GdkBitmap * mask)
4814 {
4815   cell_empty (clist, clist_row, column);
4816
4817   if (text && pixmap)
4818     {
4819       clist_row->cell[column].type = GTK_CELL_PIXTEXT;
4820       GTK_CELL_PIXTEXT (clist_row->cell[column])->text = g_strdup (text);
4821       GTK_CELL_PIXTEXT (clist_row->cell[column])->spacing = spacing;
4822       GTK_CELL_PIXTEXT (clist_row->cell[column])->pixmap = pixmap;
4823       GTK_CELL_PIXTEXT (clist_row->cell[column])->mask = mask;
4824     }
4825 }
4826
4827 /* Fill in data after widget has correct style */
4828
4829 static void 
4830 add_style_data (GtkCList * clist)
4831 {
4832   GtkWidget *widget;
4833
4834   widget = GTK_WIDGET(clist);
4835
4836   /* text properties */
4837   if (!GTK_CLIST_ROW_HEIGHT_SET (clist))
4838     {
4839       clist->row_height = widget->style->font->ascent + widget->style->font->descent + 1;
4840       clist->row_center_offset = widget->style->font->ascent + 1.5;
4841     }
4842   else
4843     {
4844       gint text_height;
4845       text_height = clist->row_height - (GTK_WIDGET (clist)->style->font->ascent +
4846                           GTK_WIDGET (clist) ->style->font->descent + 1);
4847       clist->row_center_offset = (text_height / 2) + GTK_WIDGET (clist)->style->font->ascent + 1.5;
4848     }
4849
4850   /* Column widths */
4851 }
4852
4853
4854 /* focus functions */
4855
4856 static void
4857 gtk_clist_draw_focus (GtkWidget *widget)
4858 {
4859   GtkCList *clist;
4860
4861   g_return_if_fail (widget != NULL);
4862   g_return_if_fail (GTK_IS_CLIST (widget));
4863
4864   if (!GTK_WIDGET_DRAWABLE (widget))
4865     return;
4866
4867   clist = GTK_CLIST (widget);
4868   if (clist->focus_row >= 0)
4869     gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE,
4870                         0, ROW_TOP_YPIXEL(clist, clist->focus_row),
4871                         clist->clist_window_width - 1,
4872                         clist->row_height - 1);
4873 }
4874
4875 static void
4876 gtk_clist_set_focus_child (GtkContainer *container,
4877                            GtkWidget    *child)
4878 {
4879   g_return_if_fail (container != NULL);
4880   g_return_if_fail (GTK_IS_CLIST (container));
4881
4882   if (child)
4883     {
4884       g_return_if_fail (GTK_IS_WIDGET (child));
4885       GTK_CLIST_SET_FLAG (GTK_CLIST (container), CLIST_CHILD_HAS_FOCUS);
4886     }
4887
4888   parent_class->set_focus_child (container, child);
4889 }
4890
4891 static gint
4892 gtk_clist_focus_in (GtkWidget     *widget,
4893                     GdkEventFocus *event)
4894 {
4895   GtkCList *clist;
4896
4897   g_return_val_if_fail (widget != NULL, FALSE);
4898   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
4899   g_return_val_if_fail (event != NULL, FALSE);
4900
4901   GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
4902   GTK_CLIST_UNSET_FLAG (widget, CLIST_CHILD_HAS_FOCUS);
4903
4904   clist = GTK_CLIST (widget);
4905
4906   if (clist->selection_mode == GTK_SELECTION_BROWSE &&
4907       clist->selection == NULL && clist->focus_row > -1)
4908     select_row (clist, clist->focus_row, -1, (GdkEvent *) event);
4909   else
4910     gtk_widget_draw_focus (widget);
4911
4912   return FALSE;
4913 }
4914
4915 static gint
4916 gtk_clist_focus_out (GtkWidget     *widget,
4917                      GdkEventFocus *event)
4918 {
4919   GtkCList *clist;
4920
4921   g_return_val_if_fail (widget != NULL, FALSE);
4922   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
4923   g_return_val_if_fail (event != NULL, FALSE);
4924
4925   GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
4926   gtk_widget_draw_focus (widget);
4927   
4928   clist = GTK_CLIST (widget);
4929
4930   if (clist->anchor != -1 && clist->selection_mode == GTK_SELECTION_EXTENDED)
4931     GTK_CLIST_CLASS_FW (widget)->resync_selection (clist, (GdkEvent *) event);
4932
4933   return FALSE;
4934 }
4935
4936 static void
4937 toggle_add_mode (GtkCList *clist)
4938 {
4939   g_return_if_fail (clist != 0);
4940   g_return_if_fail (GTK_IS_CLIST (clist));
4941   
4942   if (gdk_pointer_is_grabbed () ||
4943       clist->selection_mode != GTK_SELECTION_EXTENDED)
4944     return;
4945
4946   gtk_clist_draw_focus (GTK_WIDGET (clist));
4947   if (!GTK_CLIST_ADD_MODE (clist))
4948     {
4949       GTK_CLIST_SET_FLAG (clist, CLIST_ADD_MODE);
4950       gdk_gc_set_line_attributes (clist->xor_gc, 1,
4951                                   GDK_LINE_ON_OFF_DASH, 0, 0);
4952       gdk_gc_set_dashes (clist->xor_gc, 0, "\4\4", 2);
4953     }
4954   else
4955     {
4956       GTK_CLIST_UNSET_FLAG (clist, CLIST_ADD_MODE);
4957       gdk_gc_set_line_attributes (clist->xor_gc, 1, GDK_LINE_SOLID, 0, 0);
4958       clist->anchor_state = GTK_STATE_SELECTED;
4959     }
4960   gtk_clist_draw_focus (GTK_WIDGET (clist));
4961 }
4962
4963 static void
4964 toggle_focus_row (GtkCList *clist)
4965 {
4966   g_return_if_fail (clist != 0);
4967   g_return_if_fail (GTK_IS_CLIST (clist));
4968
4969   if (gdk_pointer_is_grabbed ())
4970     return;
4971
4972   switch (clist->selection_mode)
4973     {
4974     case  GTK_SELECTION_SINGLE:
4975     case  GTK_SELECTION_MULTIPLE:
4976       
4977       toggle_row (clist, clist->focus_row, 0, NULL);
4978       break;
4979       
4980     case GTK_SELECTION_EXTENDED:
4981       g_list_free (clist->undo_selection);
4982       g_list_free (clist->undo_unselection);
4983       clist->undo_selection = NULL;
4984       clist->undo_unselection = NULL;
4985
4986       if (GTK_CLIST_ADD_MODE (clist))
4987         {
4988           clist->anchor = clist->focus_row;
4989           clist->drag_pos = clist->focus_row;
4990           clist->undo_anchor = clist->focus_row;
4991           fake_toggle_row (clist, clist->focus_row);
4992           GTK_CLIST_CLASS_FW (clist)->resync_selection (clist, NULL);
4993         }
4994       else
4995         {
4996           clist->anchor = clist->focus_row;
4997           clist->drag_pos = clist->focus_row;
4998           clist->undo_anchor = clist->focus_row;
4999           GTK_CLIST_CLASS_FW (clist)->fake_unselect_all (clist,
5000                                                          clist->focus_row);
5001           GTK_CLIST_CLASS_FW (clist)->resync_selection (clist, NULL);
5002         }
5003       break;
5004       
5005     default:
5006       break;
5007     }
5008 }
5009
5010 static void
5011 move_focus_row (GtkCList      *clist,
5012                 GtkScrollType  scroll_type,
5013                 gfloat         position)
5014 {
5015   GtkWidget *widget;
5016
5017   g_return_if_fail (clist != 0);
5018   g_return_if_fail (GTK_IS_CLIST (clist));
5019
5020   widget = GTK_WIDGET (clist);
5021
5022   switch (scroll_type)
5023     {
5024     case GTK_SCROLL_STEP_BACKWARD:
5025       if (clist->focus_row <= 0)
5026         return;
5027       gtk_clist_draw_focus (widget);
5028       clist->focus_row--;
5029       gtk_clist_draw_focus (widget);
5030       break;
5031     case GTK_SCROLL_STEP_FORWARD:
5032       if (clist->focus_row >= clist->rows - 1)
5033         return;
5034       gtk_clist_draw_focus (widget);
5035       clist->focus_row++;
5036       gtk_clist_draw_focus (widget);
5037       break;
5038     case GTK_SCROLL_PAGE_BACKWARD:
5039       if (clist->focus_row <= 0)
5040         return;
5041       gtk_clist_draw_focus (widget);
5042       clist->focus_row = MAX (0, clist->focus_row -
5043                               (2 * clist->clist_window_height -
5044                                clist->row_height - CELL_SPACING) / 
5045                               (2 * (clist->row_height + CELL_SPACING)));
5046       gtk_clist_draw_focus (widget);
5047       break;
5048     case GTK_SCROLL_PAGE_FORWARD:
5049       if (clist->focus_row >= clist->rows - 1)
5050         return;
5051       gtk_clist_draw_focus (widget);
5052       clist->focus_row = MIN (clist->rows - 1, clist->focus_row + 
5053                               (2 * clist->clist_window_height -
5054                                clist->row_height - CELL_SPACING) / 
5055                               (2 * (clist->row_height + CELL_SPACING)));
5056       gtk_clist_draw_focus (widget);
5057       break;
5058     case GTK_SCROLL_JUMP:
5059       if (position >= 0 && position <= 1)
5060         {
5061           gtk_clist_draw_focus (widget);
5062           clist->focus_row = position * (clist->rows - 1);
5063           gtk_clist_draw_focus (widget);
5064         }
5065       break;
5066     default:
5067       break;
5068     }
5069 }
5070
5071 static void
5072 scroll_horizontal (GtkCList      *clist,
5073                    GtkScrollType  scroll_type,
5074                    gfloat         position)
5075 {
5076   gint column = 0;
5077
5078   g_return_if_fail (clist != 0);
5079   g_return_if_fail (GTK_IS_CLIST (clist));
5080
5081   if (gdk_pointer_is_grabbed ())
5082     return;
5083
5084   switch (scroll_type)
5085     {
5086     case GTK_SCROLL_STEP_BACKWARD:
5087       column = COLUMN_FROM_XPIXEL (clist, 0);
5088       if (COLUMN_LEFT_XPIXEL (clist, column) - CELL_SPACING - COLUMN_INSET >= 0
5089           && column > 0)
5090         column--;
5091       break;
5092     case GTK_SCROLL_STEP_FORWARD:
5093       column =  COLUMN_FROM_XPIXEL (clist, clist->clist_window_width);
5094       if (column < 0)
5095         return;
5096       if (COLUMN_LEFT_XPIXEL (clist, column) + clist->column[column].area.width
5097           + CELL_SPACING + COLUMN_INSET - 1 <= clist->clist_window_width &&
5098           column < clist->columns - 1)
5099         column++;
5100       break;
5101     case GTK_SCROLL_PAGE_BACKWARD:
5102     case GTK_SCROLL_PAGE_FORWARD:
5103       return;
5104     case GTK_SCROLL_JUMP:
5105       if (position >= 0 && position <= 1)
5106         column = position * (clist->columns - 1);
5107       else
5108         return;
5109       break;
5110     default:
5111       break;
5112     }
5113
5114   if (COLUMN_LEFT_XPIXEL (clist, column) < CELL_SPACING + COLUMN_INSET)
5115     gtk_clist_moveto (clist, -1, column, 0, 0);
5116   else if (COLUMN_LEFT_XPIXEL (clist, column) + CELL_SPACING + COLUMN_INSET - 1
5117            + clist->column[column].area.width > clist->clist_window_width)
5118     {
5119       if (column == clist->columns - 1)
5120         gtk_clist_moveto (clist, -1, column, 0, 0);
5121       else
5122         gtk_clist_moveto (clist, -1, column, 0, 1);
5123     }
5124 }
5125
5126 static void
5127 scroll_vertical (GtkCList      *clist,
5128                  GtkScrollType  scroll_type,
5129                  gfloat         position)
5130 {
5131   gint old_focus_row;
5132
5133   g_return_if_fail (clist != NULL);
5134   g_return_if_fail (GTK_IS_CLIST (clist));
5135
5136   if (gdk_pointer_is_grabbed ())
5137     return;
5138
5139   switch (clist->selection_mode)
5140     {
5141     case GTK_SELECTION_EXTENDED:
5142       if (clist->anchor >= 0)
5143         return;
5144
5145     case GTK_SELECTION_BROWSE:
5146
5147       old_focus_row = clist->focus_row;
5148       move_focus_row (clist, scroll_type, position);
5149
5150       if (old_focus_row != clist->focus_row)
5151         {
5152           if (clist->selection_mode == GTK_SELECTION_BROWSE)
5153             unselect_row (clist,old_focus_row, -1, NULL);
5154           else if (!GTK_CLIST_ADD_MODE (clist))
5155             {
5156               gtk_clist_unselect_all (clist);
5157               clist->undo_anchor = old_focus_row;
5158             }
5159         }
5160
5161       switch (gtk_clist_row_is_visible (clist, clist->focus_row))
5162         {
5163         case GTK_VISIBILITY_NONE:
5164           if (old_focus_row != clist->focus_row &&
5165               !(clist->selection_mode == GTK_SELECTION_EXTENDED &&
5166                 GTK_CLIST_ADD_MODE (clist)))
5167             select_row (clist, clist->focus_row, -1, NULL);
5168           switch (scroll_type)
5169             {
5170             case GTK_SCROLL_STEP_BACKWARD:
5171             case GTK_SCROLL_PAGE_BACKWARD:
5172               gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
5173               break;
5174             case GTK_SCROLL_STEP_FORWARD:
5175             case GTK_SCROLL_PAGE_FORWARD:
5176               gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
5177               break;
5178             case GTK_SCROLL_JUMP:
5179               gtk_clist_moveto (clist, clist->focus_row, -1, 0.5, 0);
5180               break;
5181             default:
5182               break;
5183             }
5184           break;
5185
5186         case GTK_VISIBILITY_PARTIAL:
5187           switch (scroll_type)
5188             {
5189             case GTK_SCROLL_STEP_BACKWARD:
5190             case GTK_SCROLL_PAGE_BACKWARD:
5191               gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
5192               break;
5193             case GTK_SCROLL_STEP_FORWARD:
5194             case GTK_SCROLL_PAGE_FORWARD:
5195               gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
5196               break;
5197             case GTK_SCROLL_JUMP:
5198               gtk_clist_moveto (clist, clist->focus_row, -1, 0.5, 0);
5199               break;
5200             default:
5201               break;
5202             }
5203
5204         default:
5205           if (old_focus_row != clist->focus_row &&
5206               !(clist->selection_mode == GTK_SELECTION_EXTENDED &&
5207                 GTK_CLIST_ADD_MODE (clist)))
5208             select_row (clist, clist->focus_row, -1, NULL);
5209           break;
5210         }
5211       break;
5212
5213     default:
5214       move_focus_row (clist, scroll_type, position);
5215
5216       if (ROW_TOP_YPIXEL (clist, clist->focus_row) + clist->row_height >
5217           clist->clist_window_height)
5218         gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
5219       else if (ROW_TOP_YPIXEL (clist, clist->focus_row) < 0)
5220         gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
5221       break;
5222     }
5223 }
5224
5225 static void
5226 set_anchor (GtkCList *clist,
5227             gboolean  add_mode,
5228             gint      anchor,
5229             gint      undo_anchor)
5230 {
5231   g_return_if_fail (clist != NULL);
5232   g_return_if_fail (GTK_IS_CLIST (clist));
5233   
5234   if (clist->selection_mode != GTK_SELECTION_EXTENDED || clist->anchor >= 0)
5235     return;
5236
5237   g_list_free (clist->undo_selection);
5238   g_list_free (clist->undo_unselection);
5239   clist->undo_selection = NULL;
5240   clist->undo_unselection = NULL;
5241
5242   if (add_mode)
5243     fake_toggle_row (clist, anchor);
5244   else
5245     {
5246       GTK_CLIST_CLASS_FW (clist)->fake_unselect_all (clist, anchor);
5247       clist->anchor_state = GTK_STATE_SELECTED;
5248     }
5249
5250   clist->anchor = anchor;
5251   clist->drag_pos = anchor;
5252   clist->undo_anchor = undo_anchor;
5253 }
5254
5255 static void
5256 resync_selection (GtkCList *clist,
5257                   GdkEvent *event)
5258 {
5259   gint i;
5260   gint e;
5261   gint row;
5262   gboolean thaw = FALSE;
5263   GList *list;
5264   GtkCListRow *clist_row;
5265
5266   if (clist->anchor < 0)
5267     return;
5268
5269   if (!GTK_CLIST_FROZEN (clist))
5270     {
5271       GTK_CLIST_SET_FLAG (clist, CLIST_FROZEN);
5272       thaw = TRUE;
5273     }
5274
5275   i = MIN (clist->anchor, clist->drag_pos);
5276   e = MAX (clist->anchor, clist->drag_pos);
5277
5278   if (clist->undo_selection)
5279     {
5280
5281       list = clist->selection;
5282       clist->selection = clist->undo_selection;
5283       clist->selection_end = g_list_last (clist->selection);
5284       clist->undo_selection = list;
5285       list = clist->selection;
5286       while (list)
5287         {
5288           row = GPOINTER_TO_INT (list->data);
5289           list = list->next;
5290           if (row < i || row > e)
5291             {
5292               clist_row = g_list_nth (clist->row_list, row)->data;
5293               clist_row->state = GTK_STATE_SELECTED;
5294               unselect_row (clist, row, -1, event);
5295               clist->undo_selection = g_list_prepend
5296                 (clist->undo_selection, GINT_TO_POINTER (row));
5297             }
5298         }
5299     }    
5300
5301   for (list = g_list_nth (clist->row_list, i); i <= e; i++, list = list->next)
5302     if (g_list_find (clist->selection, GINT_TO_POINTER(i)))
5303       {
5304         if (GTK_CLIST_ROW (list)->state == GTK_STATE_NORMAL)
5305           {
5306             GTK_CLIST_ROW (list)->state = GTK_STATE_SELECTED;
5307             unselect_row (clist, i, -1, event);
5308             clist->undo_selection = g_list_prepend (clist->undo_selection,
5309                                                     GINT_TO_POINTER (i));
5310           }
5311       }
5312     else if (GTK_CLIST_ROW (list)->state == GTK_STATE_SELECTED)
5313       {
5314         GTK_CLIST_ROW (list)->state = GTK_STATE_NORMAL;
5315         clist->undo_unselection = g_list_prepend (clist->undo_unselection,
5316                                                   GINT_TO_POINTER (i));
5317       }
5318
5319   for (list = clist->undo_unselection; list; list = list->next)
5320     gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
5321                      GPOINTER_TO_INT (list->data), -1, event);
5322
5323   clist->anchor = -1;
5324   clist->drag_pos = -1;
5325
5326   if (thaw)
5327     GTK_CLIST_UNSET_FLAG (clist, CLIST_FROZEN);
5328 }
5329
5330 static void
5331 update_extended_selection (GtkCList *clist,
5332                            gint      row)
5333 {
5334   gint i;
5335   GList *list;
5336   GdkRectangle area;
5337   gint s1 = -1;
5338   gint s2 = -1;
5339   gint e1 = -1;
5340   gint e2 = -1;
5341   gint y1 = clist->clist_window_height;
5342   gint y2 = clist->clist_window_height;
5343   gint h1 = 0;
5344   gint h2 = 0;
5345   gint top;
5346
5347   if (clist->selection_mode != GTK_SELECTION_EXTENDED || clist->anchor == -1)
5348     return;
5349
5350   if (row < 0)
5351     row = 0;
5352   if (row >= clist->rows)
5353     row = clist->rows - 1;
5354
5355   /* extending downwards */
5356   if (row > clist->drag_pos && clist->anchor <= clist->drag_pos)
5357     {
5358       s2 = clist->drag_pos + 1;
5359       e2 = row;
5360     }
5361   /* extending upwards */
5362   else if (row < clist->drag_pos && clist->anchor >= clist->drag_pos)
5363     {
5364       s2 = row;
5365       e2 = clist->drag_pos - 1;
5366     }
5367   else if (row < clist->drag_pos && clist->anchor < clist->drag_pos)
5368     {
5369       e1 = clist->drag_pos;
5370       /* row and drag_pos on different sides of anchor :
5371          take back the selection between anchor and drag_pos,
5372          select between anchor and row */
5373       if (row < clist->anchor)
5374         {
5375           s1 = clist->anchor + 1;
5376           s2 = row;
5377           e2 = clist->anchor - 1;
5378         }
5379       /* take back the selection between anchor and drag_pos */
5380       else
5381         s1 = row + 1;
5382     }
5383   else if (row > clist->drag_pos && clist->anchor > clist->drag_pos)
5384     {
5385       s1 = clist->drag_pos;
5386       /* row and drag_pos on different sides of anchor :
5387          take back the selection between anchor and drag_pos,
5388          select between anchor and row */
5389       if (row > clist->anchor)
5390         {
5391           e1 = clist->anchor - 1;
5392           s2 = clist->anchor + 1;
5393           e2 = row;
5394         }
5395       /* take back the selection between anchor and drag_pos */
5396       else
5397         e1 = row - 1;
5398     }
5399
5400   clist->drag_pos = row;
5401
5402   area.x = 0;
5403   area.width = clist->clist_window_width;
5404
5405   /* restore the elements between s1 and e1 */
5406   if (s1 >= 0)
5407     {
5408       for (i = s1, list = g_list_nth (clist->row_list, i); i <= e1;
5409            i++, list = list->next)
5410         {
5411           if (GTK_CLIST_CLASS_FW (clist)->selection_find (clist, i, list))
5412             GTK_CLIST_ROW (list)->state = GTK_STATE_SELECTED;
5413           else
5414             GTK_CLIST_ROW (list)->state = GTK_STATE_NORMAL;
5415         }
5416
5417       top = ROW_TOP_YPIXEL (clist, clist->focus_row);
5418
5419       if (top + clist->row_height <= 0)
5420         {
5421           area.y = 0;
5422           area.height = ROW_TOP_YPIXEL (clist, e1) + clist->row_height;
5423           draw_rows (clist, &area);
5424           gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
5425         }
5426       else if (top >= clist->clist_window_height)
5427         {
5428           area.y = ROW_TOP_YPIXEL (clist, s1);
5429           area.height = clist->clist_window_height - area.y;
5430           draw_rows (clist, &area);
5431           gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
5432         }
5433       else if (top < 0)
5434         gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
5435       else if (top + clist->row_height > clist->clist_window_height)
5436         gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
5437
5438       y1 = ROW_TOP_YPIXEL (clist, s1);
5439       h1 = (e1 - s1 + 1) * (clist->row_height + CELL_SPACING);
5440     }
5441
5442   /* extend the selection between s2 and e2 */
5443   if (s2 >= 0)
5444     {
5445       for (i = s2, list = g_list_nth (clist->row_list, i); i <= e2;
5446            i++, list = list->next)
5447         if (GTK_CLIST_ROW (list)->state != clist->anchor_state)
5448           GTK_CLIST_ROW (list)->state = clist->anchor_state;
5449
5450       top = ROW_TOP_YPIXEL (clist, clist->focus_row);
5451
5452       if (top + clist->row_height <= 0)
5453         {
5454           area.y = 0;
5455           area.height = ROW_TOP_YPIXEL (clist, e2) + clist->row_height;
5456           draw_rows (clist, &area);
5457           gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
5458         }
5459       else if (top >= clist->clist_window_height)
5460         {
5461           area.y = ROW_TOP_YPIXEL (clist, s2);
5462           area.height = clist->clist_window_height - area.y;
5463           draw_rows (clist, &area);
5464           gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
5465         }
5466       else if (top < 0)
5467         gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
5468       else if (top + clist->row_height > clist->clist_window_height)
5469         gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
5470
5471       y2 = ROW_TOP_YPIXEL (clist, s2);
5472       h2 = (e2-s2+1) * (clist->row_height + CELL_SPACING);
5473     }
5474
5475   area.y = MAX (0, MIN (y1, y2));
5476   if (area.y > clist->clist_window_height)
5477     area.y = 0;
5478   area.height = MIN (clist->clist_window_height, h1 + h2);
5479   if (s1 >= 0 && s2 >= 0)
5480     area.height += (clist->row_height + CELL_SPACING);
5481   draw_rows (clist, &area);
5482 }
5483
5484 static void
5485 start_selection (GtkCList *clist)
5486 {
5487   g_return_if_fail (clist != NULL);
5488   g_return_if_fail (GTK_IS_CLIST (clist));
5489
5490   if (gdk_pointer_is_grabbed ())
5491     return;
5492
5493   set_anchor (clist, GTK_CLIST_ADD_MODE (clist), clist->focus_row,
5494               clist->focus_row);
5495 }
5496
5497 static void
5498 end_selection (GtkCList *clist)
5499 {
5500   g_return_if_fail (clist != NULL);
5501   g_return_if_fail (GTK_IS_CLIST (clist));
5502
5503   if (gdk_pointer_is_grabbed () || clist->anchor == -1)
5504     return;
5505   
5506   GTK_CLIST_CLASS_FW (clist)->resync_selection (clist, NULL);
5507 }
5508
5509 static void
5510 extend_selection (GtkCList      *clist,
5511                   GtkScrollType  scroll_type,
5512                   gfloat         position,
5513                   gboolean       auto_start_selection)
5514 {
5515   g_return_if_fail (clist != NULL);
5516   g_return_if_fail (GTK_IS_CLIST (clist));
5517
5518   if (gdk_pointer_is_grabbed () ||
5519       clist->selection_mode != GTK_SELECTION_EXTENDED)
5520     return;
5521
5522   if (auto_start_selection)
5523     set_anchor (clist, GTK_CLIST_ADD_MODE (clist), clist->focus_row,
5524                 clist->focus_row);
5525   else if (clist->anchor == -1)
5526     return;
5527
5528   move_focus_row (clist, scroll_type, position);
5529
5530   if (ROW_TOP_YPIXEL (clist, clist->focus_row) + clist->row_height >
5531       clist->clist_window_height)
5532     gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
5533   else if (ROW_TOP_YPIXEL (clist, clist->focus_row) < 0)
5534     gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
5535
5536   update_extended_selection (clist, clist->focus_row);
5537 }
5538
5539 static void
5540 abort_column_resize (GtkCList *clist)
5541 {
5542   g_return_if_fail (clist != NULL);
5543   g_return_if_fail (GTK_IS_CLIST (clist));
5544
5545   if (!GTK_CLIST_IN_DRAG (clist))
5546     return;
5547
5548   GTK_CLIST_UNSET_FLAG (clist, CLIST_IN_DRAG);
5549   gdk_pointer_ungrab (gdk_time_get());
5550
5551   if (clist->x_drag >= 0 && clist->x_drag <= clist->clist_window_width - 1)
5552     draw_xor_line (clist);
5553
5554   if (GTK_CLIST_ADD_MODE (clist))
5555     {
5556       gdk_gc_set_line_attributes (clist->xor_gc, 1, GDK_LINE_ON_OFF_DASH, 0,0);
5557       gdk_gc_set_dashes (clist->xor_gc, 0, "\4\4", 2);
5558     }
5559 }
5560
5561 static gint
5562 gtk_clist_key_press (GtkWidget   * widget,
5563                      GdkEventKey * event)
5564 {
5565   GtkCList *clist;
5566   gboolean handled = FALSE;
5567
5568   g_return_val_if_fail (widget != NULL, FALSE);
5569   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
5570   g_return_val_if_fail (event != NULL, FALSE);
5571
5572   clist = GTK_CLIST (widget);
5573
5574
5575   if (event->keyval == GDK_Escape && GTK_CLIST_IN_DRAG (clist))
5576     {
5577       GTK_CLIST_UNSET_FLAG (clist, CLIST_IN_DRAG);
5578       gdk_pointer_ungrab (event->time);
5579
5580       if (clist->x_drag >= 0 && clist->x_drag <= clist->clist_window_width - 1)
5581         draw_xor_line (clist);
5582
5583       if (GTK_CLIST_ADD_MODE (clist))
5584         {
5585           gdk_gc_set_line_attributes (clist->xor_gc, 1, GDK_LINE_ON_OFF_DASH,
5586                                       0, 0);
5587           gdk_gc_set_dashes (clist->xor_gc, 0, "\4\4", 2);
5588         }
5589       return TRUE;
5590     }
5591
5592   if (GTK_WIDGET_CLASS (parent_class)->key_press_event)
5593     handled = GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event);
5594
5595   if (handled)
5596     return handled;
5597
5598   switch (event->keyval)
5599     {
5600     case GDK_Tab:
5601     case GDK_ISO_Left_Tab:
5602       if (event->state & GDK_SHIFT_MASK)
5603         return gtk_container_focus (GTK_CONTAINER (widget),
5604                                     GTK_DIR_TAB_BACKWARD);
5605       else
5606         return gtk_container_focus (GTK_CONTAINER (widget),
5607                                     GTK_DIR_TAB_FORWARD);
5608
5609     default:
5610       break;
5611     }
5612   
5613   return FALSE;
5614 }
5615
5616 static gboolean
5617 title_focus (GtkCList * clist,
5618              gint       dir)
5619 {
5620   GtkWidget *focus_child;
5621   gboolean return_val = FALSE;
5622   gint d = 1;
5623   gint i = 0;
5624   gint j;
5625
5626   if (!GTK_CLIST_SHOW_TITLES (clist))
5627     return FALSE;
5628
5629   focus_child = GTK_CONTAINER (clist)->focus_child;
5630   
5631   switch (dir)
5632     {
5633     case GTK_DIR_TAB_BACKWARD:
5634     case GTK_DIR_UP:
5635       if (!focus_child || focus_child == clist->hscrollbar ||
5636           focus_child == clist->hscrollbar ||
5637           !GTK_CLIST_CHILD_HAS_FOCUS (clist))
5638         {
5639           if (dir == GTK_DIR_UP)
5640             i = COLUMN_FROM_XPIXEL (clist, 0);
5641           else
5642             i = clist->columns - 1;
5643           focus_child = clist->column[i].button;
5644           dir = GTK_DIR_TAB_FORWARD;
5645         }
5646       else
5647         d = -1;
5648       break;
5649     case GTK_DIR_LEFT:
5650       d = -1;
5651       if (!focus_child || focus_child == clist->hscrollbar ||
5652           focus_child == clist->hscrollbar)
5653         {
5654           i = clist->columns - 1;
5655           focus_child = clist->column[i].button;
5656         }
5657       break;
5658     case GTK_DIR_RIGHT:
5659       if (!focus_child || focus_child == clist->hscrollbar ||
5660           focus_child == clist->hscrollbar)
5661         {
5662           i = 0;
5663           focus_child = clist->column[i].button;
5664         }
5665       break;
5666     }
5667
5668   if (focus_child)
5669     while (i < clist->columns)
5670       {
5671         if (clist->column[i].button == focus_child)
5672           {
5673             if (clist->column[i].button && 
5674                 GTK_WIDGET_VISIBLE (clist->column[i].button) &&
5675                 GTK_IS_CONTAINER (clist->column[i].button) &&
5676                 !GTK_WIDGET_HAS_FOCUS (clist->column[i].button))
5677               if (gtk_container_focus 
5678                   (GTK_CONTAINER (clist->column[i].button), dir))
5679                 {
5680                   return_val = TRUE;
5681                   i -= d;
5682                 }
5683             if (!return_val && dir == GTK_DIR_UP)
5684               return FALSE;
5685             i += d;
5686             break;
5687           }
5688         i++;
5689       }
5690
5691   j = i;
5692
5693   if (!return_val)
5694     while (j >= 0 && j < clist->columns)
5695       {
5696         if (clist->column[j].button &&
5697             GTK_WIDGET_VISIBLE (clist->column[j].button))
5698           {
5699             if (GTK_IS_CONTAINER (clist->column[j].button) &&
5700                 gtk_container_focus 
5701                 (GTK_CONTAINER (clist->column[j].button), dir))
5702               {
5703                 return_val = TRUE;
5704                 break;
5705               }
5706             else if (GTK_WIDGET_CAN_FOCUS (clist->column[j].button))
5707               {
5708                 gtk_widget_grab_focus (clist->column[j].button);
5709                 return_val = TRUE;
5710                 break;
5711               }
5712           }
5713         j += d;
5714       }
5715   
5716   if (return_val)
5717     {
5718       if (COLUMN_LEFT_XPIXEL (clist, j) < CELL_SPACING + COLUMN_INSET)
5719         gtk_clist_moveto (clist, -1, j, 0, 0);
5720       else if (COLUMN_LEFT_XPIXEL(clist, j) + clist->column[j].area.width >
5721                clist->clist_window_width)
5722         {
5723           if (j == clist->columns-1)
5724             gtk_clist_moveto (clist, -1, j, 0, 0);
5725           else
5726             gtk_clist_moveto (clist, -1, j, 0, 1);
5727         }
5728     }
5729   return return_val;
5730 }
5731
5732 static gint
5733 gtk_clist_focus (GtkContainer     * container,
5734                  GtkDirectionType   direction)
5735 {
5736   GtkCList *clist;
5737   GtkWidget *focus_child;
5738   gint old_row;
5739
5740   g_return_val_if_fail (container != NULL, FALSE);
5741   g_return_val_if_fail (GTK_IS_CLIST (container), FALSE);
5742
5743   if (!GTK_WIDGET_SENSITIVE (container))
5744     return FALSE;
5745   
5746   clist = GTK_CLIST (container);
5747   focus_child = container->focus_child;
5748   old_row = clist->focus_row;
5749
5750   switch (direction)
5751     {
5752     case GTK_DIR_LEFT:
5753     case GTK_DIR_RIGHT:
5754       if (GTK_CLIST_CHILD_HAS_FOCUS (clist) &&
5755           (!focus_child || (focus_child && focus_child != clist->vscrollbar && 
5756                             focus_child != clist->hscrollbar)))
5757         {
5758           if (title_focus (clist, direction))
5759             return TRUE;
5760           gtk_container_set_focus_child (container, NULL);
5761           return FALSE;
5762          }
5763       gtk_widget_grab_focus (GTK_WIDGET (container));
5764       return TRUE;
5765     case GTK_DIR_DOWN:
5766     case GTK_DIR_TAB_FORWARD:
5767       if (GTK_CLIST_CHILD_HAS_FOCUS (clist) && 
5768           (!focus_child || (focus_child != clist->vscrollbar &&
5769                             focus_child != clist->hscrollbar))) 
5770         {
5771           gboolean tf = FALSE;
5772
5773           if (((focus_child && direction == GTK_DIR_DOWN) ||
5774                !(tf = title_focus (clist, GTK_DIR_TAB_FORWARD)))
5775               && clist->rows)
5776             {
5777               if (clist->focus_row < 0)
5778                 {
5779                   clist->focus_row = 0;
5780
5781                   if ((clist->selection_mode == GTK_SELECTION_BROWSE ||
5782                        clist->selection_mode == GTK_SELECTION_EXTENDED) &&
5783                       !clist->selection)
5784                     select_row (clist, clist->focus_row, -1, NULL);
5785                 }
5786               gtk_widget_grab_focus (GTK_WIDGET (container));
5787               return TRUE;
5788             }
5789
5790           if (tf)
5791             return TRUE;
5792         }
5793       
5794       GTK_CLIST_SET_FLAG (clist, CLIST_CHILD_HAS_FOCUS);
5795
5796       if ((!GTK_CLIST_CHILD_HAS_FOCUS (clist) || !focus_child ||
5797            (focus_child != clist->vscrollbar &&
5798             focus_child != clist->hscrollbar)) &&
5799           GTK_WIDGET_VISIBLE (clist->vscrollbar) &&
5800           GTK_WIDGET_CAN_FOCUS (clist->vscrollbar))
5801         {
5802           gtk_widget_grab_focus (clist->vscrollbar);
5803           return TRUE;
5804         }
5805
5806       if ((!GTK_CLIST_CHILD_HAS_FOCUS (clist) || !focus_child || 
5807            focus_child != clist->hscrollbar) &&
5808           GTK_WIDGET_VISIBLE (clist->hscrollbar) &&
5809           GTK_WIDGET_CAN_FOCUS (clist->hscrollbar))
5810         {
5811           gtk_widget_grab_focus (clist->hscrollbar);
5812           return TRUE;
5813         }
5814       break;
5815     case GTK_DIR_UP:
5816     case GTK_DIR_TAB_BACKWARD:
5817       if (!focus_child && GTK_CLIST_CHILD_HAS_FOCUS (clist) &&
5818           GTK_WIDGET_VISIBLE (clist->hscrollbar) &&
5819           GTK_WIDGET_CAN_FOCUS (clist->hscrollbar))
5820         {
5821           gtk_widget_grab_focus (clist->hscrollbar);
5822           return TRUE;
5823         }
5824         
5825       if ((!focus_child || focus_child == clist->hscrollbar) &&
5826           GTK_CLIST_CHILD_HAS_FOCUS (clist) &&
5827           GTK_WIDGET_VISIBLE (clist->vscrollbar) &&
5828           GTK_WIDGET_CAN_FOCUS (clist->vscrollbar))
5829         {
5830           gtk_widget_grab_focus (clist->vscrollbar);
5831           return TRUE;
5832         }
5833
5834       if ((!focus_child || focus_child == clist->hscrollbar ||
5835            focus_child == clist->vscrollbar) &&
5836           GTK_CLIST_CHILD_HAS_FOCUS (clist) && clist->rows)
5837         {
5838           if (clist->focus_row < 0)
5839             {
5840               clist->focus_row = 0;
5841               if ((clist->selection_mode == GTK_SELECTION_BROWSE ||
5842                    clist->selection_mode == GTK_SELECTION_EXTENDED) &&
5843                   !clist->selection)
5844                 select_row (clist, clist->focus_row, -1, NULL);
5845             }
5846           gtk_widget_grab_focus (GTK_WIDGET (container));
5847           return TRUE;
5848         }
5849
5850       GTK_CLIST_SET_FLAG (clist, CLIST_CHILD_HAS_FOCUS);
5851
5852       if (title_focus (clist, direction))
5853         return TRUE;
5854
5855       break;
5856
5857     default:
5858       break;
5859     }
5860
5861   gtk_container_set_focus_child (container, NULL);
5862   return FALSE;
5863 }
5864
5865 void
5866 gtk_clist_unselect_all (GtkCList * clist)
5867 {
5868   GTK_CLIST_CLASS_FW (clist)->unselect_all (clist);
5869 }
5870
5871 static void
5872 real_unselect_all (GtkCList * clist)
5873 {
5874   GList *list;
5875   gint i;
5876  
5877   g_return_if_fail (clist != NULL);
5878   g_return_if_fail (GTK_IS_CLIST (clist));
5879
5880   if (gdk_pointer_is_grabbed ())
5881     return;
5882
5883   switch (clist->selection_mode)
5884     {
5885     case GTK_SELECTION_BROWSE:
5886       if (clist->focus_row >= 0)
5887         {
5888           select_row (clist, clist->focus_row, -1, NULL);
5889           return;
5890         }
5891       break;
5892
5893     case GTK_SELECTION_EXTENDED:
5894       g_list_free (clist->undo_selection);
5895       g_list_free (clist->undo_unselection);
5896       clist->undo_selection = NULL;
5897       clist->undo_unselection = NULL;
5898
5899       clist->anchor = -1;
5900       clist->drag_pos = -1;
5901       clist->undo_anchor = clist->focus_row;
5902       break;
5903
5904     default:
5905       break;
5906     }
5907
5908   list = clist->selection;
5909
5910   while (list)
5911     {
5912       i = GPOINTER_TO_INT (list->data);
5913       list = list->next;
5914       unselect_row (clist, i, -1, NULL);
5915     }
5916 }
5917
5918 void
5919 gtk_clist_select_all (GtkCList * clist)
5920 {
5921   GTK_CLIST_CLASS_FW (clist)->select_all (clist);
5922 }
5923
5924 static void
5925 real_select_all (GtkCList * clist)
5926 {
5927   GList *list;
5928   gint i;
5929  
5930   g_return_if_fail (clist != NULL);
5931   g_return_if_fail (GTK_IS_CLIST (clist));
5932
5933   if (gdk_pointer_is_grabbed ())
5934     return;
5935
5936   switch (clist->selection_mode)
5937     {
5938     case GTK_SELECTION_SINGLE:
5939     case GTK_SELECTION_BROWSE:
5940       return;
5941
5942     case GTK_SELECTION_EXTENDED:
5943       g_list_free (clist->undo_selection);
5944       g_list_free (clist->undo_unselection);
5945       clist->undo_selection = NULL;
5946       clist->undo_unselection = NULL;
5947           
5948       if (clist->rows &&
5949           ((GtkCListRow *) (clist->row_list->data))->state !=
5950           GTK_STATE_SELECTED)
5951         fake_toggle_row (clist, 0);
5952
5953       clist->anchor_state =  GTK_STATE_SELECTED;
5954       clist->anchor = 0;
5955       clist->drag_pos = 0;
5956       clist->undo_anchor = clist->focus_row;
5957       update_extended_selection (clist, clist->rows);
5958       GTK_CLIST_CLASS_FW (clist)->resync_selection (clist, NULL);
5959       return;
5960
5961     case GTK_SELECTION_MULTIPLE:
5962       for (i = 0, list = clist->row_list; list; i++, list = list->next)
5963         {
5964           if (((GtkCListRow *)(list->data))->state == GTK_STATE_NORMAL)
5965             gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
5966                              i, -1, NULL);
5967         }
5968       return;
5969     }
5970 }
5971
5972 static void
5973 fake_unselect_all (GtkCList * clist,
5974                    gint       row)
5975 {
5976   GList *list;
5977   GList *work;
5978   gint i;
5979
5980   if (row >= 0 && (work = g_list_nth (clist->row_list, row)))
5981     {
5982       if (GTK_CLIST_ROW (work)->state == GTK_STATE_NORMAL)
5983         {
5984           GTK_CLIST_ROW (work)->state = GTK_STATE_SELECTED;
5985           
5986           if (!GTK_CLIST_FROZEN (clist) &&
5987               gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
5988             GTK_CLIST_CLASS_FW (clist)->draw_row (clist, NULL, row,
5989                                                   GTK_CLIST_ROW (work));
5990         }  
5991     }
5992
5993   clist->undo_selection = clist->selection;
5994   clist->selection = NULL;
5995   clist->selection_end = NULL;
5996   
5997   for (list = clist->undo_selection; list; list = list->next)
5998     {
5999       if ((i = GPOINTER_TO_INT (list->data)) == row ||
6000           !(work = g_list_nth (clist->row_list, i)))
6001         continue;
6002
6003       GTK_CLIST_ROW (work)->state = GTK_STATE_NORMAL;
6004       if (!GTK_CLIST_FROZEN (clist) &&
6005           gtk_clist_row_is_visible (clist, i) != GTK_VISIBILITY_NONE)
6006         GTK_CLIST_CLASS_FW (clist)->draw_row (clist, NULL, i,
6007                                               GTK_CLIST_ROW (work));
6008     }
6009 }
6010
6011 static void
6012 fake_toggle_row (GtkCList *clist,
6013                  gint      row)
6014 {
6015   GList *work;
6016
6017   if (!(work = g_list_nth (clist->row_list, row)))
6018     return;
6019   
6020   if (GTK_CLIST_ROW (work)->state == GTK_STATE_NORMAL)
6021     clist->anchor_state = GTK_CLIST_ROW (work)->state = GTK_STATE_SELECTED;
6022   else
6023     clist->anchor_state = GTK_CLIST_ROW (work)->state = GTK_STATE_NORMAL;
6024   
6025   if (!GTK_CLIST_FROZEN (clist) &&
6026       gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
6027     GTK_CLIST_CLASS_FW (clist)->draw_row (clist, NULL, row,
6028                                           GTK_CLIST_ROW (work));
6029 }
6030
6031 static GList *
6032 selection_find (GtkCList *clist,
6033                 gint      row_number,
6034                 GList    *row_list_element)
6035 {
6036   return g_list_find (clist->selection, GINT_TO_POINTER (row_number));
6037 }