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