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