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