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