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