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