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