Removed function default_row_height
[pspp] / lib / gtksheet / gtksheet.c
1 /*
2  * Copyright (C) 2006, 2008 Free Software Foundation
3  *
4  * This version of GtkSheet has been *heavily* modified, for the specific
5  * requirements of PSPPIRE.  The changes are copyright by the
6  * Free Software Foundation.  The copyright notice for the original work is
7  * below.
8  */
9
10 /* GtkSheet widget for Gtk+.
11  * Copyright (C) 1999-2001 Adrian E. Feiguin <adrian@ifir.ifir.edu.ar>
12  *
13  * Based on GtkClist widget by Jay Painter, but major changes.
14  * Memory allocation routines inspired on SC (Spreadsheet Calculator)
15  *
16  * This library is free software; you can redistribute it and/or
17  * modify it under the terms of the GNU Lesser General Public
18  * License as published by the Free Software Foundation; either
19  * version 2.1 of the License, or (at your option) any later version.
20  *
21  * This library is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24  * Lesser General Public License for more details.
25  *
26  * You should have received a copy of the GNU Lesser General Public
27  * License along with this library; if not, write to the Free Software
28  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
29  */
30
31 /**
32  * SECTION:gtksheet
33  * @short_description: spreadsheet widget for gtk2
34  *
35  * GtkSheet is a matrix widget for GTK+. It consists of an scrollable grid of
36  * cells where you can allocate text. Cell contents can be edited interactively
37  * through a specially designed entry, GtkItemEntry.
38  *
39  */
40 #include <config.h>
41
42 #include <string.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <glib.h>
46 #include <gdk/gdk.h>
47 #include <gdk/gdkkeysyms.h>
48 #include <gtk/gtksignal.h>
49 #include <gtk/gtkbutton.h>
50 #include <gtk/gtkadjustment.h>
51 #include <gtk/gtktypeutils.h>
52 #include <gtk/gtkentry.h>
53 #include <gtk/gtkcontainer.h>
54 #include <pango/pango.h>
55 #include "gtksheet.h"
56 #include "gtkextra-marshal.h"
57 #include "gsheetmodel.h"
58 #include <libpspp/misc.h>
59 #include <math.h>
60
61 /* sheet flags */
62 enum
63   {
64     GTK_SHEET_IN_XDRAG = 1 << 1,
65     GTK_SHEET_IN_YDRAG = 1 << 2,
66     GTK_SHEET_IN_DRAG = 1 << 3,
67     GTK_SHEET_IN_SELECTION = 1 << 4,
68     GTK_SHEET_IN_RESIZE = 1 << 5
69   };
70
71 #define GTK_SHEET_FLAGS(sheet) (GTK_SHEET (sheet)->flags)
72 #define GTK_SHEET_SET_FLAGS(sheet,flag) (GTK_SHEET_FLAGS (sheet) |= (flag))
73 #define GTK_SHEET_UNSET_FLAGS(sheet,flag) (GTK_SHEET_FLAGS (sheet) &= ~ (flag))
74
75 #define GTK_SHEET_IN_XDRAG(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_XDRAG)
76 #define GTK_SHEET_IN_YDRAG(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_YDRAG)
77 #define GTK_SHEET_IN_DRAG(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_DRAG)
78 #define GTK_SHEET_IN_SELECTION(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_SELECTION)
79 #define GTK_SHEET_IN_RESIZE(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_RESIZE)
80
81 #define CELL_SPACING 1
82
83 #define TIMEOUT_HOVER 300
84 #define COLUMN_MIN_WIDTH 10
85 #define COLUMN_TITLES_HEIGHT 4
86 #define DEFAULT_COLUMN_WIDTH 80
87 #define DEFAULT_ROW_HEIGHT 25
88
89 static void gtk_sheet_update_primary_selection (GtkSheet *sheet);
90 static void gtk_sheet_column_title_button_draw (GtkSheet *sheet, gint column);
91
92 static void gtk_sheet_row_title_button_draw (GtkSheet *sheet, gint row);
93
94 static void gtk_sheet_set_row_height (GtkSheet *sheet,
95                                       gint row,
96                                       guint height);
97
98 static void destroy_hover_window (GtkSheetHoverTitle *);
99 static GtkSheetHoverTitle *create_hover_window (void);
100
101 static GtkStateType gtk_sheet_cell_get_state (GtkSheet *sheet, gint row, gint col);
102
103
104 static inline  void
105 dispose_string (const GtkSheet *sheet, gchar *text)
106 {
107   GSheetModel *model = gtk_sheet_get_model (sheet);
108
109   if ( ! model )
110     return;
111
112   if (g_sheet_model_free_strings (model))
113     g_free (text);
114 }
115
116 static
117 guint STRING_WIDTH (GtkWidget *widget,
118                     const PangoFontDescription *font, const gchar *text)
119 {
120   PangoRectangle rect;
121   PangoLayout *layout;
122
123   layout = gtk_widget_create_pango_layout (widget, text);
124   pango_layout_set_font_description (layout, font);
125
126   pango_layout_get_extents (layout, NULL, &rect);
127
128   g_object_unref (layout);
129   return PANGO_PIXELS (rect.width);
130 }
131
132 /* Return the row containing pixel Y */
133 static gint
134 yyy_row_ypixel_to_row (const GtkSheet *sheet, gint y)
135 {
136   GSheetRow *geo = sheet->row_geometry;
137
138   g_return_val_if_fail (y >= 0, -1);
139
140   return g_sheet_row_pixel_to_row (geo, y);
141 }
142
143 /* Return the lowest row number which is wholly or partially on
144    the visible range of the sheet */
145 static inline glong
146 min_visible_row (const GtkSheet *sheet)
147 {
148   return yyy_row_ypixel_to_row (sheet, sheet->vadjustment->value);
149 }
150
151 static inline glong
152 min_fully_visible_row (const GtkSheet *sheet)
153 {
154   glong row = yyy_row_ypixel_to_row (sheet, sheet->vadjustment->value);
155
156   if ( g_sheet_row_start_pixel (sheet->row_geometry, row) < sheet->vadjustment->value)
157     row++;
158
159   return  row;
160 }
161
162
163
164 static inline glong
165 max_visible_row (const GtkSheet *sheet)
166 {
167   return yyy_row_ypixel_to_row (sheet,
168                            sheet->vadjustment->value +
169                            sheet->vadjustment->page_size);
170 }
171
172
173 static inline glong
174 max_fully_visible_row (const GtkSheet *sheet)
175 {
176   glong row = max_visible_row (sheet);
177
178   if ( g_sheet_row_start_pixel (sheet->row_geometry, row)
179        + 
180        g_sheet_row_get_height (sheet->row_geometry, row)
181        > sheet->vadjustment->value)
182     row--;
183
184   return row;
185 }
186
187
188 /* returns the column index from a x pixel location */
189 static inline gint
190 column_from_xpixel (const GtkSheet *sheet, gint x)
191 {
192   gint i;
193   gint cx = 0;
194
195   if (x < 0) return -1;
196   for (i = 0;
197        i < g_sheet_column_get_column_count (sheet->column_geometry); i++)
198     {
199       if (x >= cx &&
200           x <= (cx + g_sheet_column_get_width (sheet->column_geometry, i)))
201         return i;
202
203       cx += g_sheet_column_get_width (sheet->column_geometry, i);
204     }
205
206   /* no match */
207   return g_sheet_column_get_column_count (sheet->column_geometry) - 1;
208 }
209
210
211 /* Returns the lowest column number which is wholly or partially
212    on the sheet */
213 static inline glong
214 min_visible_column (const GtkSheet *sheet)
215 {
216   return column_from_xpixel (sheet, sheet->hadjustment->value);
217 }
218
219 static inline glong
220 min_fully_visible_column (const GtkSheet *sheet)
221 {
222   glong col = min_visible_column (sheet);
223
224   if ( g_sheet_column_start_pixel (sheet->column_geometry, col) < sheet->hadjustment->value)
225     col++;
226
227   return col;
228 }
229
230
231 /* Returns the highest column number which is wholly or partially
232    on the sheet */
233 static inline glong
234 max_visible_column (const GtkSheet *sheet)
235 {
236   return column_from_xpixel (sheet,
237                              sheet->hadjustment->value +
238                              sheet->hadjustment->page_size);
239 }
240
241 static inline glong
242 max_fully_visible_column (const GtkSheet *sheet)
243 {
244   glong col = max_visible_column (sheet);
245
246   if ( g_sheet_column_start_pixel (sheet->column_geometry, col)
247        + 
248        g_sheet_column_get_width (sheet->column_geometry, col)
249        > sheet->hadjustment->value)
250     col--;
251
252   return col;
253 }
254
255
256
257 /* The size of the region (in pixels) around the row/column boundaries
258    where the height/width may be grabbed to change size */
259 #define DRAG_WIDTH 6
260
261 static gboolean
262 on_column_boundary (const GtkSheet *sheet, gint x, gint *column)
263 {
264   gint col;
265
266   x += sheet->hadjustment->value;
267
268   col = column_from_xpixel (sheet, x);
269
270   if ( column_from_xpixel (sheet, x - DRAG_WIDTH / 2) < col )
271     {
272       *column = col - 1;
273       return TRUE;
274     }
275
276   if  ( column_from_xpixel (sheet, x + DRAG_WIDTH / 2) > col )
277     {
278       *column = col;
279       return TRUE;
280     }
281
282   return FALSE;
283 }
284
285 static inline gboolean
286 POSSIBLE_YDRAG (const GtkSheet *sheet, gint y, gint *drag_row)
287 {
288   gint row, ydrag;
289
290   y += sheet->vadjustment->value;
291   row = yyy_row_ypixel_to_row (sheet, y);
292   *drag_row = row;
293
294   ydrag = g_sheet_row_start_pixel (sheet->row_geometry, row) + CELL_SPACING;
295   if (y <= ydrag + DRAG_WIDTH / 2 && row != 0)
296     {
297       *drag_row = row - 1;
298       return g_sheet_row_get_sensitivity (sheet->row_geometry, row - 1);
299     }
300
301   ydrag += g_sheet_row_get_height (sheet->row_geometry, row);
302
303   if (y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2)
304     return g_sheet_row_get_sensitivity (sheet->row_geometry, row);
305
306   return FALSE;
307 }
308
309 static inline gboolean
310 POSSIBLE_DRAG (const GtkSheet *sheet, gint x, gint y,
311                gint *drag_row, gint *drag_column)
312 {
313   gint ydrag, xdrag;
314
315   /* Can't drag if nothing is selected */
316   if ( sheet->range.row0 < 0 || sheet->range.rowi < 0 ||
317        sheet->range.col0 < 0 || sheet->range.coli < 0 )
318     return FALSE;
319
320   *drag_column = column_from_xpixel (sheet, x);
321   *drag_row = yyy_row_ypixel_to_row (sheet, y);
322
323   if (x >= g_sheet_column_start_pixel (sheet->column_geometry, sheet->range.col0) - DRAG_WIDTH / 2 &&
324       x <= g_sheet_column_start_pixel (sheet->column_geometry, sheet->range.coli) +
325       g_sheet_column_get_width (sheet->column_geometry, sheet->range.coli) + DRAG_WIDTH / 2)
326     {
327       ydrag = g_sheet_row_start_pixel (sheet->row_geometry, sheet->range.row0);
328       if (y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2)
329         {
330           *drag_row = sheet->range.row0;
331           return TRUE;
332         }
333       ydrag = g_sheet_row_start_pixel (sheet->row_geometry, sheet->range.rowi) +
334         g_sheet_row_get_height (sheet->row_geometry, sheet->range.rowi);
335       if (y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2)
336         {
337           *drag_row = sheet->range.rowi;
338           return TRUE;
339         }
340     }
341
342   if (y >= g_sheet_row_start_pixel (sheet->row_geometry, sheet->range.row0) - DRAG_WIDTH / 2 &&
343       y <= g_sheet_row_start_pixel (sheet->row_geometry, sheet->range.rowi) +
344       g_sheet_row_get_height (sheet->row_geometry, sheet->range.rowi) + DRAG_WIDTH / 2)
345     {
346       xdrag = g_sheet_column_start_pixel (sheet->column_geometry, sheet->range.col0);
347       if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2)
348         {
349           *drag_column = sheet->range.col0;
350           return TRUE;
351         }
352       xdrag = g_sheet_column_start_pixel (sheet->column_geometry, sheet->range.coli) +
353         g_sheet_column_get_width (sheet->column_geometry, sheet->range.coli);
354       if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2)
355         {
356           *drag_column = sheet->range.coli;
357           return TRUE;
358         }
359     }
360
361   return FALSE;
362 }
363
364 static inline gboolean
365 POSSIBLE_RESIZE (const GtkSheet *sheet, gint x, gint y,
366                  gint *drag_row, gint *drag_column)
367 {
368   gint xdrag, ydrag;
369
370   /* Can't drag if nothing is selected */
371   if ( sheet->range.row0 < 0 || sheet->range.rowi < 0 ||
372        sheet->range.col0 < 0 || sheet->range.coli < 0 )
373     return FALSE;
374
375   xdrag = g_sheet_column_start_pixel (sheet->column_geometry, sheet->range.coli)+
376     g_sheet_column_get_width (sheet->column_geometry, sheet->range.coli);
377
378   ydrag = g_sheet_row_start_pixel (sheet->row_geometry, sheet->range.rowi) +
379     g_sheet_row_get_height (sheet->row_geometry, sheet->range.rowi);
380
381   if (sheet->state == GTK_SHEET_COLUMN_SELECTED)
382     ydrag = g_sheet_row_start_pixel (sheet->row_geometry, min_visible_row (sheet));
383
384   if (sheet->state == GTK_SHEET_ROW_SELECTED)
385     xdrag = g_sheet_column_start_pixel (sheet->column_geometry, min_visible_column (sheet));
386
387   *drag_column = column_from_xpixel (sheet, x);
388   *drag_row = yyy_row_ypixel_to_row (sheet, y);
389
390   if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2 &&
391       y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2) return TRUE;
392
393   return FALSE;
394 }
395
396
397 static gboolean
398 rectangle_from_range (GtkSheet *sheet, const GtkSheetRange *range,
399                       GdkRectangle *r)
400 {
401   g_return_val_if_fail (range, FALSE);
402
403   r->x = g_sheet_column_start_pixel (sheet->column_geometry, range->col0);
404   r->x -= round (sheet->hadjustment->value);
405
406   if ( sheet->row_titles_visible)
407     r->x += sheet->row_title_area.width;
408
409
410   r->y = g_sheet_row_start_pixel (sheet->row_geometry, range->row0);
411   r->y -= round (sheet->vadjustment->value);
412
413   if ( sheet->column_titles_visible)
414     r->y += sheet->column_title_area.height;
415
416   r->width = g_sheet_column_start_pixel (sheet->column_geometry, range->coli) -
417     g_sheet_column_start_pixel (sheet->column_geometry, range->col0) +
418     g_sheet_column_get_width (sheet->column_geometry, range->coli);
419
420   r->height = g_sheet_row_start_pixel (sheet->row_geometry, range->rowi) -
421     g_sheet_row_start_pixel (sheet->row_geometry, range->row0) +
422     g_sheet_row_get_height (sheet->row_geometry, range->rowi);
423
424   return TRUE;
425 }
426
427 static gboolean
428 rectangle_from_cell (GtkSheet *sheet, gint row, gint col,
429                      GdkRectangle *r)
430 {
431   GtkSheetRange range;
432   g_return_val_if_fail (row >= 0, FALSE);
433   g_return_val_if_fail (col >= 0, FALSE);
434
435   range.row0 = range.rowi = row;
436   range.col0 = range.coli = col;
437
438   return rectangle_from_range (sheet, &range, r);
439 }
440
441
442 static void gtk_sheet_class_init                 (GtkSheetClass *klass);
443 static void gtk_sheet_init                       (GtkSheet *sheet);
444 static void gtk_sheet_dispose                    (GObject *object);
445 static void gtk_sheet_finalize                   (GObject *object);
446 static void gtk_sheet_style_set                  (GtkWidget *widget,
447                                                   GtkStyle *previous_style);
448 static void gtk_sheet_realize                    (GtkWidget *widget);
449 static void gtk_sheet_unrealize                  (GtkWidget *widget);
450 static void gtk_sheet_map                        (GtkWidget *widget);
451 static void gtk_sheet_unmap                      (GtkWidget *widget);
452 static gint gtk_sheet_expose                     (GtkWidget *widget,
453                                                   GdkEventExpose *event);
454 static void gtk_sheet_forall                     (GtkContainer *container,
455                                                   gboolean include_internals,
456                                                   GtkCallback callback,
457                                                   gpointer callback_data);
458
459 static void gtk_sheet_set_scroll_adjustments     (GtkSheet *sheet,
460                                                   GtkAdjustment *hadjustment,
461                                                   GtkAdjustment *vadjustment);
462
463 static gint gtk_sheet_button_press               (GtkWidget *widget,
464                                                   GdkEventButton *event);
465 static gint gtk_sheet_button_release             (GtkWidget *widget,
466                                                   GdkEventButton *event);
467 static gint gtk_sheet_motion                     (GtkWidget *widget,
468                                                   GdkEventMotion *event);
469 static gboolean gtk_sheet_crossing_notify           (GtkWidget *widget,
470                                                      GdkEventCrossing *event);
471 static gint gtk_sheet_entry_key_press            (GtkWidget *widget,
472                                                   GdkEventKey *key);
473 static gboolean gtk_sheet_key_press              (GtkWidget *widget,
474                                                   GdkEventKey *key);
475 static void gtk_sheet_size_request               (GtkWidget *widget,
476                                                   GtkRequisition *requisition);
477 static void gtk_sheet_size_allocate              (GtkWidget *widget,
478                                                   GtkAllocation *allocation);
479
480 /* Sheet queries */
481
482 static gboolean gtk_sheet_range_isvisible (const GtkSheet *sheet,
483                                            GtkSheetRange range);
484 static gboolean gtk_sheet_cell_isvisible  (GtkSheet *sheet,
485                                            gint row, gint column);
486 /* Drawing Routines */
487
488 /* draw cell */
489 static void gtk_sheet_cell_draw (GtkSheet *sheet, gint row, gint column);
490
491
492 /* draw visible part of range. If range == NULL then draw the whole screen */
493 static void gtk_sheet_range_draw (GtkSheet *sheet,
494                                   const GtkSheetRange *range);
495
496 /* highlight the visible part of the selected range */
497 static void gtk_sheet_range_draw_selection       (GtkSheet *sheet,
498                                                   GtkSheetRange range);
499
500 /* Selection */
501
502 static void gtk_sheet_real_select_range          (GtkSheet *sheet,
503                                                   const GtkSheetRange *range);
504 static void gtk_sheet_real_unselect_range        (GtkSheet *sheet,
505                                                   const GtkSheetRange *range);
506 static void gtk_sheet_extend_selection           (GtkSheet *sheet,
507                                                   gint row, gint column);
508 static void gtk_sheet_new_selection              (GtkSheet *sheet,
509                                                   GtkSheetRange *range);
510 static void gtk_sheet_draw_border                (GtkSheet *sheet,
511                                                   GtkSheetRange range);
512
513 /* Active Cell handling */
514
515 static void gtk_sheet_entry_changed              (GtkWidget *widget,
516                                                   gpointer data);
517 static void gtk_sheet_hide_active_cell           (GtkSheet *sheet);
518 static void change_active_cell           (GtkSheet *sheet,
519                                           gint row, gint col);
520 static void gtk_sheet_draw_active_cell           (GtkSheet *sheet);
521 static void gtk_sheet_show_active_cell           (GtkSheet *sheet);
522 static gboolean gtk_sheet_click_cell             (GtkSheet *sheet,
523                                                   gint row,
524                                                   gint column);
525
526
527 /* Scrollbars */
528
529 static void adjust_scrollbars                    (GtkSheet *sheet);
530 static void vadjustment_value_changed            (GtkAdjustment *adjustment,
531                                                   gpointer data);
532 static void hadjustment_value_changed            (GtkAdjustment *adjustment,
533                                                   gpointer data);
534
535
536 static void draw_xor_vline                       (GtkSheet *sheet);
537 static void draw_xor_hline                       (GtkSheet *sheet);
538 static void draw_xor_rectangle                   (GtkSheet *sheet,
539                                                   GtkSheetRange range);
540
541 static guint new_column_width                    (GtkSheet *sheet,
542                                                   gint column,
543                                                   gint *x);
544 static guint new_row_height                      (GtkSheet *sheet,
545                                                   gint row,
546                                                   gint *y);
547 /* Sheet Button */
548
549 static void create_global_button                 (GtkSheet *sheet);
550 static void global_button_clicked                (GtkWidget *widget,
551                                                   gpointer data);
552 /* Sheet Entry */
553
554 static void create_sheet_entry                   (GtkSheet *sheet);
555 static void gtk_sheet_size_allocate_entry        (GtkSheet *sheet);
556
557 /* Sheet button gadgets */
558
559 static void draw_column_title_buttons    (GtkSheet *sheet);
560 static void draw_row_title_buttons       (GtkSheet *sheet);
561
562
563 static void size_allocate_global_button          (GtkSheet *sheet);
564 static void gtk_sheet_button_size_request        (GtkSheet *sheet,
565                                                   const GtkSheetButton *button,
566                                                   GtkRequisition *requisition);
567
568 static void gtk_sheet_real_cell_clear            (GtkSheet *sheet,
569                                                   gint row,
570                                                   gint column);
571
572
573 static void gtk_sheet_column_size_request (GtkSheet *sheet,
574                                            gint col,
575                                            guint *requisition);
576 static void gtk_sheet_row_size_request (GtkSheet *sheet,
577                                         gint row,
578                                         guint *requisition);
579
580
581 /* Signals */
582 enum
583   {
584     SELECT_ROW,
585     SELECT_COLUMN,
586     DOUBLE_CLICK_ROW,
587     DOUBLE_CLICK_COLUMN,
588     BUTTON_EVENT_ROW,
589     BUTTON_EVENT_COLUMN,
590     SELECT_RANGE,
591     RESIZE_RANGE,
592     MOVE_RANGE,
593     TRAVERSE,
594     ACTIVATE,
595     CHANGED,
596     LAST_SIGNAL
597   };
598
599 static GtkContainerClass *parent_class = NULL;
600 static guint sheet_signals[LAST_SIGNAL] = { 0 };
601
602
603 GType
604 gtk_sheet_get_type ()
605 {
606   static GType sheet_type = 0;
607
608   if (!sheet_type)
609     {
610       static const GTypeInfo sheet_info =
611         {
612           sizeof (GtkSheetClass),
613           NULL,
614           NULL,
615           (GClassInitFunc) gtk_sheet_class_init,
616           NULL,
617           NULL,
618           sizeof (GtkSheet),
619           0,
620           (GInstanceInitFunc) gtk_sheet_init,
621           NULL,
622         };
623
624       sheet_type =
625         g_type_register_static (GTK_TYPE_BIN, "GtkSheet",
626                                 &sheet_info, 0);
627     }
628   return sheet_type;
629 }
630
631 static GtkSheetRange*
632 gtk_sheet_range_copy (const GtkSheetRange *range)
633 {
634   GtkSheetRange *new_range;
635
636   g_return_val_if_fail (range != NULL, NULL);
637
638   new_range = g_new (GtkSheetRange, 1);
639
640   *new_range = *range;
641
642   return new_range;
643 }
644
645 static void
646 gtk_sheet_range_free (GtkSheetRange *range)
647 {
648   g_return_if_fail (range != NULL);
649
650   g_free (range);
651 }
652
653 GType
654 gtk_sheet_range_get_type (void)
655 {
656   static GType sheet_range_type = 0;
657
658   if (!sheet_range_type)
659     {
660       sheet_range_type =
661         g_boxed_type_register_static ("GtkSheetRange",
662                                       (GBoxedCopyFunc) gtk_sheet_range_copy,
663                                       (GBoxedFreeFunc) gtk_sheet_range_free);
664     }
665
666   return sheet_range_type;
667 }
668
669
670 static void column_titles_changed (GtkWidget *w, gint first, gint n_columns,
671                                    gpointer data);
672
673 /* Properties */
674 enum
675   {
676     PROP_0,
677     PROP_ROW_GEO,
678     PROP_COL_GEO,
679     PROP_MODEL
680   };
681
682 static void
683 gtk_sheet_set_row_geometry (GtkSheet *sheet, GSheetRow *geo)
684 {
685   if ( sheet->row_geometry ) g_object_unref (sheet->row_geometry);
686
687   sheet->row_geometry = geo;
688
689   if ( sheet->row_geometry ) g_object_ref (sheet->row_geometry);
690 }
691
692 static void
693 gtk_sheet_set_column_geometry (GtkSheet *sheet, GSheetColumn *geo)
694 {
695   if ( sheet->column_geometry ) g_object_unref (sheet->column_geometry);
696
697   sheet->column_geometry = geo;
698
699   if ( sheet->column_geometry ) g_object_ref (sheet->column_geometry);
700 }
701
702
703 static void
704 gtk_sheet_set_property (GObject         *object,
705                         guint            prop_id,
706                         const GValue    *value,
707                         GParamSpec      *pspec)
708
709 {
710   GtkSheet *sheet = GTK_SHEET (object);
711
712   switch (prop_id)
713     {
714     case PROP_ROW_GEO:
715       gtk_sheet_set_row_geometry (sheet, g_value_get_pointer (value));
716       break;
717     case PROP_COL_GEO:
718       gtk_sheet_set_column_geometry (sheet, g_value_get_pointer (value));
719       if ( sheet->column_geometry)
720         g_signal_connect (sheet->column_geometry, "columns_changed",
721                           G_CALLBACK (column_titles_changed), sheet);
722       break;
723     case PROP_MODEL:
724       gtk_sheet_set_model (sheet, g_value_get_pointer (value));
725       break;
726     default:
727       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
728       break;
729     };
730 }
731
732 static void
733 gtk_sheet_get_property (GObject         *object,
734                         guint            prop_id,
735                         GValue          *value,
736                         GParamSpec      *pspec)
737 {
738   GtkSheet *sheet = GTK_SHEET (object);
739
740   switch (prop_id)
741     {
742     case PROP_ROW_GEO:
743       g_value_set_pointer (value, sheet->row_geometry);
744       break;
745     case PROP_COL_GEO:
746       g_value_set_pointer (value, sheet->column_geometry);
747       break;
748     case PROP_MODEL:
749       g_value_set_pointer (value, sheet->model);
750       break;
751     default:
752       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
753       break;
754     };
755 }
756
757
758 static void
759 gtk_sheet_class_init (GtkSheetClass *klass)
760 {
761   GObjectClass *object_class = G_OBJECT_CLASS (klass);
762
763   GParamSpec *row_geo_spec ;
764   GParamSpec *col_geo_spec ;
765   GParamSpec *model_spec ;
766
767   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
768   GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
769
770   parent_class = g_type_class_peek_parent (klass);
771
772   /**
773    * GtkSheet::select-row
774    * @sheet: the sheet widget that emitted the signal
775    * @row: the newly selected row index
776    *
777    * A row has been selected.
778    */
779   sheet_signals[SELECT_ROW] =
780     g_signal_new ("select-row",
781                   G_TYPE_FROM_CLASS (object_class),
782                   G_SIGNAL_RUN_LAST,
783                   offsetof (GtkSheetClass, select_row),
784                   NULL, NULL,
785                   g_cclosure_marshal_VOID__INT,
786                   G_TYPE_NONE,
787                   1,
788                   G_TYPE_INT);
789
790
791   /**
792    * GtkSheet::select - column
793    * @sheet: the sheet widget that emitted the signal
794    * @column: the newly selected column index
795    *
796    * A column has been selected.
797    */
798   sheet_signals[SELECT_COLUMN] =
799     g_signal_new ("select-column",
800                   G_TYPE_FROM_CLASS (object_class),
801                   G_SIGNAL_RUN_LAST,
802                   offsetof (GtkSheetClass, select_column),
803                   NULL, NULL,
804                   g_cclosure_marshal_VOID__INT,
805                   G_TYPE_NONE,
806                   1,
807                   G_TYPE_INT);
808
809
810   /**
811    * GtkSheet::double-click-row
812    * @sheet: the sheet widget that emitted the signal
813    * @row: the row that was double clicked.
814    *
815    * A row's title button has been double clicked
816    */
817   sheet_signals[DOUBLE_CLICK_ROW] =
818     g_signal_new ("double-click-row",
819                   G_TYPE_FROM_CLASS (object_class),
820                   G_SIGNAL_RUN_LAST,
821                   0,
822                   NULL, NULL,
823                   g_cclosure_marshal_VOID__INT,
824                   G_TYPE_NONE,
825                   1,
826                   G_TYPE_INT);
827
828
829   /**
830    * GtkSheet::double-click-column
831    * @sheet: the sheet widget that emitted the signal
832    * @column: the column that was double clicked.
833    *
834    * A column's title button has been double clicked
835    */
836   sheet_signals[DOUBLE_CLICK_COLUMN] =
837     g_signal_new ("double-click-column",
838                   G_TYPE_FROM_CLASS (object_class),
839                   G_SIGNAL_RUN_LAST,
840                   0,
841                   NULL, NULL,
842                   g_cclosure_marshal_VOID__INT,
843                   G_TYPE_NONE,
844                   1,
845                   G_TYPE_INT);
846
847
848   /**
849    * GtkSheet::button-event-column
850    * @sheet: the sheet widget that emitted the signal
851    * @column: the column on which the event occured.
852    *
853    * A button event occured on a column title button
854    */
855   sheet_signals[BUTTON_EVENT_COLUMN] =
856     g_signal_new ("button-event-column",
857                   G_TYPE_FROM_CLASS (object_class),
858                   G_SIGNAL_RUN_LAST,
859                   0,
860                   NULL, NULL,
861                   gtkextra_VOID__INT_POINTER,
862                   G_TYPE_NONE,
863                   2,
864                   G_TYPE_INT,
865                   G_TYPE_POINTER
866                   );
867
868
869   /**
870    * GtkSheet::button-event-row
871    * @sheet: the sheet widget that emitted the signal
872    * @column: the column on which the event occured.
873    *
874    * A button event occured on a row title button
875    */
876   sheet_signals[BUTTON_EVENT_ROW] =
877     g_signal_new ("button-event-row",
878                   G_TYPE_FROM_CLASS (object_class),
879                   G_SIGNAL_RUN_LAST,
880                   0,
881                   NULL, NULL,
882                   gtkextra_VOID__INT_POINTER,
883                   G_TYPE_NONE,
884                   2,
885                   G_TYPE_INT,
886                   G_TYPE_POINTER
887                   );
888
889
890   sheet_signals[SELECT_RANGE] =
891     g_signal_new ("select-range",
892                   G_TYPE_FROM_CLASS (object_class),
893                   G_SIGNAL_RUN_LAST,
894                   offsetof (GtkSheetClass, select_range),
895                   NULL, NULL,
896                   g_cclosure_marshal_VOID__BOXED,
897                   G_TYPE_NONE,
898                   1,
899                   GTK_TYPE_SHEET_RANGE);
900
901
902   sheet_signals[RESIZE_RANGE] =
903     g_signal_new ("resize-range",
904                   G_TYPE_FROM_CLASS (object_class),
905                   G_SIGNAL_RUN_LAST,
906                   offsetof (GtkSheetClass, resize_range),
907                   NULL, NULL,
908                   gtkextra_VOID__BOXED_BOXED,
909                   G_TYPE_NONE,
910                   2,
911                   GTK_TYPE_SHEET_RANGE, GTK_TYPE_SHEET_RANGE
912                   );
913
914   sheet_signals[MOVE_RANGE] =
915     g_signal_new ("move-range",
916                   G_TYPE_FROM_CLASS (object_class),
917                   G_SIGNAL_RUN_LAST,
918                   offsetof (GtkSheetClass, move_range),
919                   NULL, NULL,
920                   gtkextra_VOID__BOXED_BOXED,
921                   G_TYPE_NONE,
922                   2,
923                   GTK_TYPE_SHEET_RANGE, GTK_TYPE_SHEET_RANGE
924                   );
925
926   sheet_signals[TRAVERSE] =
927     g_signal_new ("traverse",
928                   G_TYPE_FROM_CLASS (object_class),
929                   G_SIGNAL_RUN_LAST,
930                   offsetof (GtkSheetClass, traverse),
931                   NULL, NULL,
932                   gtkextra_BOOLEAN__INT_INT_POINTER_POINTER,
933                   G_TYPE_BOOLEAN, 4, G_TYPE_INT, G_TYPE_INT,
934                   G_TYPE_POINTER, G_TYPE_POINTER);
935
936
937   sheet_signals[ACTIVATE] =
938     g_signal_new ("activate",
939                   G_TYPE_FROM_CLASS (object_class),
940                   G_SIGNAL_RUN_LAST,
941                   offsetof (GtkSheetClass, activate),
942                   NULL, NULL,
943                   gtkextra_VOID__INT_INT_INT_INT,
944                   G_TYPE_NONE, 4,
945                   G_TYPE_INT, G_TYPE_INT,
946                   G_TYPE_INT, G_TYPE_INT);
947
948   sheet_signals[CHANGED] =
949     g_signal_new ("changed",
950                   G_TYPE_FROM_CLASS (object_class),
951                   G_SIGNAL_RUN_LAST,
952                   offsetof (GtkSheetClass, changed),
953                   NULL, NULL,
954                   gtkextra_VOID__INT_INT,
955                   G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
956
957   widget_class->set_scroll_adjustments_signal =
958     g_signal_new ("set-scroll-adjustments",
959                   G_TYPE_FROM_CLASS (object_class),
960                   G_SIGNAL_RUN_LAST,
961                   offsetof (GtkSheetClass, set_scroll_adjustments),
962                   NULL, NULL,
963                   gtkextra_VOID__OBJECT_OBJECT,
964                   G_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
965
966
967   container_class->add = NULL;
968   container_class->remove = NULL;
969   container_class->forall = gtk_sheet_forall;
970
971   object_class->dispose = gtk_sheet_dispose;
972   object_class->finalize = gtk_sheet_finalize;
973
974
975   row_geo_spec =
976     g_param_spec_pointer ("row-geometry",
977                           "Row Geometry",
978                           "A pointer to the model of the row geometry",
979                           G_PARAM_READABLE | G_PARAM_WRITABLE );
980
981   col_geo_spec =
982     g_param_spec_pointer ("column-geometry",
983                           "Column Geometry",
984                           "A pointer to the model of the column geometry",
985                           G_PARAM_READABLE | G_PARAM_WRITABLE );
986
987   model_spec =
988     g_param_spec_pointer ("model",
989                           "Model",
990                           "A pointer to the data model",
991                           G_PARAM_READABLE | G_PARAM_WRITABLE );
992
993
994   object_class->set_property = gtk_sheet_set_property;
995   object_class->get_property = gtk_sheet_get_property;
996
997   g_object_class_install_property (object_class,
998                                    PROP_ROW_GEO,
999                                    row_geo_spec);
1000
1001   g_object_class_install_property (object_class,
1002                                    PROP_COL_GEO,
1003                                    col_geo_spec);
1004
1005   g_object_class_install_property (object_class,
1006                                    PROP_MODEL,
1007                                    model_spec);
1008
1009
1010   widget_class->realize = gtk_sheet_realize;
1011   widget_class->unrealize = gtk_sheet_unrealize;
1012   widget_class->map = gtk_sheet_map;
1013   widget_class->unmap = gtk_sheet_unmap;
1014   widget_class->style_set = gtk_sheet_style_set;
1015   widget_class->button_press_event = gtk_sheet_button_press;
1016   widget_class->button_release_event = gtk_sheet_button_release;
1017   widget_class->motion_notify_event = gtk_sheet_motion;
1018   widget_class->enter_notify_event = gtk_sheet_crossing_notify;
1019   widget_class->leave_notify_event = gtk_sheet_crossing_notify;
1020   widget_class->key_press_event = gtk_sheet_key_press;
1021   widget_class->expose_event = gtk_sheet_expose;
1022   widget_class->size_request = gtk_sheet_size_request;
1023   widget_class->size_allocate = gtk_sheet_size_allocate;
1024   widget_class->focus_in_event = NULL;
1025   widget_class->focus_out_event = NULL;
1026
1027   klass->set_scroll_adjustments = gtk_sheet_set_scroll_adjustments;
1028   klass->select_row = NULL;
1029   klass->select_column = NULL;
1030   klass->select_range = NULL;
1031   klass->resize_range = NULL;
1032   klass->move_range = NULL;
1033   klass->traverse = NULL;
1034   klass->activate = NULL;
1035   klass->changed = NULL;
1036 }
1037
1038 static void
1039 gtk_sheet_init (GtkSheet *sheet)
1040 {
1041   sheet->model = NULL;
1042   sheet->column_geometry = NULL;
1043   sheet->row_geometry = NULL;
1044
1045   sheet->flags = 0;
1046   sheet->selection_mode = GTK_SELECTION_NONE;
1047   sheet->state = GTK_SHEET_NORMAL;
1048
1049   GTK_WIDGET_UNSET_FLAGS (sheet, GTK_NO_WINDOW);
1050   GTK_WIDGET_SET_FLAGS (sheet, GTK_CAN_FOCUS);
1051
1052   sheet->column_title_window = NULL;
1053   sheet->column_title_area.x = 0;
1054   sheet->column_title_area.y = 0;
1055   sheet->column_title_area.width = 0;
1056   sheet->column_title_area.height = DEFAULT_ROW_HEIGHT;
1057
1058   sheet->row_title_window = NULL;
1059   sheet->row_title_area.x = 0;
1060   sheet->row_title_area.y = 0;
1061   sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
1062   sheet->row_title_area.height = 0;
1063
1064
1065   sheet->active_cell.row = 0;
1066   sheet->active_cell.col = 0;
1067   sheet->selection_cell.row = 0;
1068   sheet->selection_cell.col = 0;
1069
1070   sheet->range.row0 = 0;
1071   sheet->range.rowi = 0;
1072   sheet->range.col0 = 0;
1073   sheet->range.coli = 0;
1074
1075   sheet->state = GTK_SHEET_NORMAL;
1076
1077   sheet->sheet_window = NULL;
1078   sheet->entry_widget = NULL;
1079   sheet->entry_container = NULL;
1080   sheet->entry_handler_id = 0;
1081   sheet->button = NULL;
1082
1083   sheet->hadjustment = NULL;
1084   sheet->vadjustment = NULL;
1085
1086   sheet->cursor_drag = NULL;
1087
1088   sheet->xor_gc = NULL;
1089   sheet->fg_gc = NULL;
1090   sheet->bg_gc = NULL;
1091   sheet->x_drag = 0;
1092   sheet->y_drag = 0;
1093   sheet->show_grid = TRUE;
1094
1095   sheet->motion_timer = 0;
1096
1097   sheet->columns_resizable = TRUE;
1098   sheet->rows_resizable = TRUE;
1099
1100   sheet->row_titles_visible = TRUE;
1101   sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
1102
1103   sheet->column_titles_visible = TRUE;
1104
1105
1106   /* create sheet entry */
1107   sheet->entry_type = 0;
1108   create_sheet_entry (sheet);
1109
1110   /* create global selection button */
1111   create_global_button (sheet);
1112 }
1113
1114
1115 /* Callback which occurs whenever columns are inserted / deleted in the model */
1116 static void
1117 columns_inserted_deleted_callback (GSheetModel *model, gint first_column,
1118                                    gint n_columns,
1119                                    gpointer data)
1120 {
1121   gint i;
1122   GtkSheet *sheet = GTK_SHEET (data);
1123
1124   GtkSheetRange range;
1125   gint model_columns = g_sheet_model_get_column_count (model);
1126
1127
1128   /* Need to update all the columns starting from the first column and onwards.
1129    * Previous column are unchanged, so don't need to be updated.
1130    */
1131   range.col0 = first_column;
1132   range.row0 = 0;
1133   range.coli = g_sheet_column_get_column_count (sheet->column_geometry) - 1;
1134   range.rowi = g_sheet_row_get_row_count (sheet->row_geometry) - 1;
1135
1136   adjust_scrollbars (sheet);
1137
1138   if (sheet->active_cell.col >= model_columns)
1139     change_active_cell (sheet, sheet->active_cell.row, model_columns - 1);
1140
1141   for (i = first_column; i <= max_visible_column (sheet); i++)
1142     gtk_sheet_column_title_button_draw (sheet, i);
1143
1144   gtk_sheet_range_draw (sheet, &range);
1145 }
1146
1147
1148 /* Callback which occurs whenever rows are inserted / deleted in the model */
1149 static void
1150 rows_inserted_deleted_callback (GSheetModel *model, gint first_row,
1151                                 gint n_rows, gpointer data)
1152 {
1153   gint i;
1154   GtkSheet *sheet = GTK_SHEET (data);
1155
1156   GtkSheetRange range;
1157
1158   gint model_rows = g_sheet_model_get_row_count (model);
1159
1160   /* Need to update all the rows starting from the first row and onwards.
1161    * Previous rows are unchanged, so don't need to be updated.
1162    */
1163   range.row0 = first_row;
1164   range.col0 = 0;
1165   range.rowi = g_sheet_row_get_row_count (sheet->row_geometry) - 1;
1166   range.coli = g_sheet_column_get_column_count (sheet->column_geometry) - 1;
1167
1168   adjust_scrollbars (sheet);
1169
1170   if (sheet->active_cell.row >= model_rows)
1171     change_active_cell (sheet, model_rows - 1, sheet->active_cell.col);
1172
1173   for (i = first_row; i <= max_visible_row (sheet); i++)
1174     gtk_sheet_row_title_button_draw (sheet, i);
1175
1176   gtk_sheet_range_draw (sheet, &range);
1177 }
1178
1179 /*
1180   If row0 or rowi are negative, then all rows will be updated.
1181   If col0 or coli are negative, then all columns will be updated.
1182 */
1183 static void
1184 range_update_callback (GSheetModel *m, gint row0, gint col0,
1185                        gint rowi, gint coli, gpointer data)
1186 {
1187   GtkSheet *sheet = GTK_SHEET (data);
1188
1189   GtkSheetRange range;
1190
1191   range.row0 = row0;
1192   range.col0 = col0;
1193   range.rowi = rowi;
1194   range.coli = coli;
1195
1196   if ( !GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1197     return;
1198
1199   if ( max_visible_row (sheet) > g_sheet_model_get_row_count (sheet->model)
1200        ||
1201        max_visible_column (sheet) > g_sheet_model_get_column_count (sheet->model))
1202     {
1203       gtk_sheet_moveto (sheet, 0, 0, 0, 0);
1204     }
1205
1206   if ( ( row0 < 0 && col0 < 0 ) || ( rowi < 0 && coli < 0 ) )
1207     {
1208       gint i;
1209       gtk_sheet_range_draw (sheet, NULL);
1210       adjust_scrollbars (sheet);
1211
1212       for (i = min_visible_row (sheet); i <= max_visible_row (sheet); i++)
1213         gtk_sheet_row_title_button_draw (sheet, i);
1214
1215       for (i = min_visible_column (sheet);
1216            i <= max_visible_column (sheet); i++)
1217         gtk_sheet_column_title_button_draw (sheet, i);
1218
1219       return;
1220     }
1221   else if ( row0 < 0 || rowi < 0 )
1222     {
1223       range.row0 = min_visible_row (sheet);
1224       range.rowi = max_visible_row (sheet);
1225     }
1226   else if ( col0 < 0 || coli < 0 )
1227     {
1228       range.col0 = min_visible_column (sheet);
1229       range.coli = max_visible_column (sheet);
1230     }
1231
1232   gtk_sheet_range_draw (sheet, &range);
1233 }
1234
1235
1236 /**
1237  * gtk_sheet_new:
1238  * @rows: initial number of rows
1239  * @columns: initial number of columns
1240  * @title: sheet title
1241  * @model: the model to use for the sheet data
1242  *
1243  * Creates a new sheet widget with the given number of rows and columns.
1244  *
1245  * Returns: the new sheet widget
1246  */
1247 GtkWidget *
1248 gtk_sheet_new (GSheetRow *vgeo, GSheetColumn *hgeo, GSheetModel *model)
1249 {
1250   GtkWidget *widget = g_object_new (GTK_TYPE_SHEET,
1251                                     "row-geometry", vgeo,
1252                                     "column-geometry", hgeo,
1253                                     "model", model,
1254                                     NULL);
1255   return widget;
1256 }
1257
1258
1259 /**
1260  * gtk_sheet_set_model
1261  * @sheet: the sheet to set the model for
1262  * @model: the model to use for the sheet data
1263  *
1264  * Sets the model for a GtkSheet
1265  *
1266  */
1267 void
1268 gtk_sheet_set_model (GtkSheet *sheet, GSheetModel *model)
1269 {
1270   g_return_if_fail (GTK_IS_SHEET (sheet));
1271
1272   if (sheet->model ) g_object_unref (sheet->model);
1273
1274   sheet->model = model;
1275
1276   if ( model)
1277     {
1278       g_object_ref (model);
1279
1280       g_signal_connect (model, "range_changed",
1281                         G_CALLBACK (range_update_callback), sheet);
1282
1283       g_signal_connect (model, "rows_inserted",
1284                         G_CALLBACK (rows_inserted_deleted_callback), sheet);
1285
1286       g_signal_connect (model, "rows_deleted",
1287                         G_CALLBACK (rows_inserted_deleted_callback), sheet);
1288
1289       g_signal_connect (model, "columns_inserted",
1290                         G_CALLBACK (columns_inserted_deleted_callback), sheet);
1291
1292       g_signal_connect (model, "columns_deleted",
1293                         G_CALLBACK (columns_inserted_deleted_callback), sheet);
1294     }
1295 }
1296
1297
1298 /* Call back for when the column titles have changed.
1299    FIRST is the first column changed.
1300    N_COLUMNS is the number of columns which have changed, or -1, which
1301    indicates that the column has changed to its right-most extremity
1302 */
1303 static void
1304 column_titles_changed (GtkWidget *w, gint first, gint n_columns, gpointer data)
1305 {
1306   GtkSheet *sheet = GTK_SHEET (data);
1307   gboolean extremity = FALSE;
1308
1309   if ( n_columns == -1 )
1310     {
1311       extremity = TRUE;
1312       n_columns = g_sheet_column_get_column_count (sheet->column_geometry) - 1 ;
1313     }
1314
1315   {
1316     gint i;
1317     for ( i = first ; i <= first + n_columns ; ++i )
1318       {
1319         gtk_sheet_column_title_button_draw (sheet, i);
1320         g_signal_emit (sheet, sheet_signals[CHANGED], 0, -1, i);
1321       }
1322   }
1323
1324   if ( extremity)
1325     gtk_sheet_column_title_button_draw (sheet, -1);
1326
1327 }
1328
1329 void
1330 gtk_sheet_change_entry (GtkSheet *sheet, GtkType entry_type)
1331 {
1332   gint state;
1333
1334   g_return_if_fail (sheet != NULL);
1335   g_return_if_fail (GTK_IS_SHEET (sheet));
1336
1337   state = sheet->state;
1338
1339   if (sheet->state == GTK_SHEET_NORMAL)
1340     gtk_sheet_hide_active_cell (sheet);
1341
1342   sheet->entry_type = entry_type;
1343
1344   create_sheet_entry (sheet);
1345
1346   if (state == GTK_SHEET_NORMAL)
1347     {
1348       gtk_sheet_show_active_cell (sheet);
1349     }
1350
1351 }
1352
1353 void
1354 gtk_sheet_show_grid (GtkSheet *sheet, gboolean show)
1355 {
1356   g_return_if_fail (sheet != NULL);
1357   g_return_if_fail (GTK_IS_SHEET (sheet));
1358
1359   if (show == sheet->show_grid) return;
1360
1361   sheet->show_grid = show;
1362
1363   gtk_sheet_range_draw (sheet, NULL);
1364 }
1365
1366 gboolean
1367 gtk_sheet_grid_visible (GtkSheet *sheet)
1368 {
1369   g_return_val_if_fail (sheet != NULL, 0);
1370   g_return_val_if_fail (GTK_IS_SHEET (sheet), 0);
1371
1372   return sheet->show_grid;
1373 }
1374
1375 guint
1376 gtk_sheet_get_columns_count (GtkSheet *sheet)
1377 {
1378   g_return_val_if_fail (sheet != NULL, 0);
1379   g_return_val_if_fail (GTK_IS_SHEET (sheet), 0);
1380
1381   return g_sheet_column_get_column_count (sheet->column_geometry);
1382 }
1383
1384 static void
1385 gtk_sheet_set_column_width (GtkSheet *sheet,
1386                             gint column,
1387                             guint width);
1388
1389
1390 static void
1391 gtk_sheet_autoresize_column (GtkSheet *sheet, gint column)
1392 {
1393   gint text_width = 0;
1394   gint row;
1395
1396   g_return_if_fail (sheet != NULL);
1397   g_return_if_fail (GTK_IS_SHEET (sheet));
1398   if (column >= g_sheet_column_get_column_count (sheet->column_geometry) || column < 0) return;
1399
1400   for (row = 0; row < g_sheet_row_get_row_count (sheet->row_geometry); row++)
1401     {
1402       gchar *text = gtk_sheet_cell_get_text (sheet, row, column);
1403       if (text && strlen (text) > 0)
1404         {
1405           GtkSheetCellAttr attributes;
1406
1407           gtk_sheet_get_attributes (sheet, row, column, &attributes);
1408           if (attributes.is_visible)
1409             {
1410               gint width = STRING_WIDTH (GTK_WIDGET (sheet),
1411                                          attributes.font_desc,
1412                                          text)
1413                 + 2 * COLUMN_TITLES_HEIGHT + attributes.border.width;
1414               text_width = MAX (text_width, width);
1415             }
1416         }
1417       dispose_string (sheet, text);
1418     }
1419
1420   if (text_width > g_sheet_column_get_width (sheet->column_geometry, column) )
1421     {
1422       gtk_sheet_set_column_width (sheet, column, text_width);
1423     }
1424 }
1425
1426 void
1427 gtk_sheet_show_column_titles (GtkSheet *sheet)
1428 {
1429   if (sheet->column_titles_visible) return;
1430
1431   sheet->column_titles_visible = TRUE;
1432
1433   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1434     return;
1435
1436   gdk_window_show (sheet->column_title_window);
1437   gdk_window_move_resize (sheet->column_title_window,
1438                           sheet->column_title_area.x,
1439                           sheet->column_title_area.y,
1440                           sheet->column_title_area.width,
1441                           sheet->column_title_area.height);
1442
1443   adjust_scrollbars (sheet);
1444
1445   if (sheet->vadjustment)
1446     g_signal_emit_by_name (sheet->vadjustment,
1447                            "value_changed");
1448   size_allocate_global_button (sheet);
1449 }
1450
1451
1452 void
1453 gtk_sheet_show_row_titles (GtkSheet *sheet)
1454 {
1455   if (sheet->row_titles_visible) return;
1456
1457   sheet->row_titles_visible = TRUE;
1458
1459
1460   if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1461     {
1462       gdk_window_show (sheet->row_title_window);
1463       gdk_window_move_resize (sheet->row_title_window,
1464                               sheet->row_title_area.x,
1465                               sheet->row_title_area.y,
1466                               sheet->row_title_area.width,
1467                               sheet->row_title_area.height);
1468
1469       adjust_scrollbars (sheet);
1470     }
1471
1472   if (sheet->hadjustment)
1473     g_signal_emit_by_name (sheet->hadjustment,
1474                            "value_changed");
1475   size_allocate_global_button (sheet);
1476 }
1477
1478 void
1479 gtk_sheet_hide_column_titles (GtkSheet *sheet)
1480 {
1481   if (!sheet->column_titles_visible) return;
1482
1483   sheet->column_titles_visible = FALSE;
1484
1485   if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1486     {
1487       if (sheet->column_title_window)
1488         gdk_window_hide (sheet->column_title_window);
1489       if (GTK_WIDGET_VISIBLE (sheet->button))
1490         gtk_widget_hide (sheet->button);
1491
1492       adjust_scrollbars (sheet);
1493     }
1494
1495   if (sheet->vadjustment)
1496     g_signal_emit_by_name (sheet->vadjustment,
1497                            "value_changed");
1498 }
1499
1500 void
1501 gtk_sheet_hide_row_titles (GtkSheet *sheet)
1502 {
1503   if (!sheet->row_titles_visible) return;
1504
1505   sheet->row_titles_visible = FALSE;
1506
1507   if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1508     {
1509       if (sheet->row_title_window)
1510         gdk_window_hide (sheet->row_title_window);
1511
1512       if (GTK_WIDGET_VISIBLE (sheet->button))
1513         gtk_widget_hide (sheet->button);
1514
1515       adjust_scrollbars (sheet);
1516     }
1517
1518   if (sheet->hadjustment)
1519     g_signal_emit_by_name (sheet->hadjustment,
1520                            "value_changed");
1521 }
1522
1523
1524 /* Scroll the sheet so that the cell ROW, COLUMN is visible.
1525    If {ROW,COL}_ALIGN is zero, then the cell will be placed
1526    at the {top,left} of the sheet.  If it's 1, then it'll
1527    be placed at the {bottom,right}.
1528    ROW or COL may be -1, in which case scrolling in that dimension
1529    does not occur.
1530  */
1531 void
1532 gtk_sheet_moveto (GtkSheet *sheet,
1533                   gint row,
1534                   gint col,
1535                   gfloat row_align,
1536                   gfloat col_align)
1537 {
1538   gint width, height;
1539
1540   g_return_if_fail (row_align >= 0);
1541   g_return_if_fail (col_align >= 0);
1542
1543   g_return_if_fail (row_align <= 1);
1544   g_return_if_fail (col_align <= 1);
1545
1546   g_return_if_fail (col <
1547                     g_sheet_column_get_column_count (sheet->column_geometry));
1548   g_return_if_fail (row <
1549                     g_sheet_row_get_row_count (sheet->row_geometry));
1550
1551   gdk_drawable_get_size (sheet->sheet_window, &width, &height);
1552
1553
1554   if (row >= 0)
1555   {
1556     gint y =  g_sheet_row_start_pixel (sheet->row_geometry, row);
1557
1558     gtk_adjustment_set_value (sheet->vadjustment, y - height * row_align);
1559   }
1560
1561
1562   if (col >= 0)
1563   {
1564     gint x =  g_sheet_column_start_pixel (sheet->column_geometry, col);
1565
1566     gtk_adjustment_set_value (sheet->hadjustment, x - width * col_align);
1567   }
1568 }
1569
1570
1571 static gboolean
1572 gtk_sheet_columns_resizable (GtkSheet *sheet)
1573 {
1574   g_return_val_if_fail (sheet != NULL, FALSE);
1575   g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE);
1576
1577   return sheet->columns_resizable;
1578 }
1579
1580
1581 static gboolean
1582 gtk_sheet_rows_resizable (GtkSheet *sheet)
1583 {
1584   g_return_val_if_fail (sheet != NULL, FALSE);
1585   g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE);
1586
1587   return sheet->rows_resizable;
1588 }
1589
1590
1591 void
1592 gtk_sheet_select_row (GtkSheet *sheet, gint row)
1593 {
1594   g_return_if_fail (sheet != NULL);
1595   g_return_if_fail (GTK_IS_SHEET (sheet));
1596
1597   if (row < 0 || row >= g_sheet_row_get_row_count (sheet->row_geometry))
1598     return;
1599
1600   if (sheet->state != GTK_SHEET_NORMAL)
1601     gtk_sheet_real_unselect_range (sheet, NULL);
1602
1603   sheet->state = GTK_SHEET_ROW_SELECTED;
1604   sheet->range.row0 = row;
1605   sheet->range.col0 = 0;
1606   sheet->range.rowi = row;
1607   sheet->range.coli = g_sheet_column_get_column_count (sheet->column_geometry) - 1;
1608   sheet->active_cell.row = row;
1609   sheet->active_cell.col = 0;
1610
1611   g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, row);
1612   gtk_sheet_real_select_range (sheet, NULL);
1613 }
1614
1615
1616 void
1617 gtk_sheet_select_column (GtkSheet *sheet, gint column)
1618 {
1619   g_return_if_fail (sheet != NULL);
1620   g_return_if_fail (GTK_IS_SHEET (sheet));
1621
1622   if (column < 0 || column >= g_sheet_column_get_column_count (sheet->column_geometry))
1623     return;
1624
1625   if (sheet->state != GTK_SHEET_NORMAL)
1626     gtk_sheet_real_unselect_range (sheet, NULL);
1627
1628   sheet->state = GTK_SHEET_COLUMN_SELECTED;
1629   sheet->range.row0 = 0;
1630   sheet->range.col0 = column;
1631   sheet->range.rowi = g_sheet_row_get_row_count (sheet->row_geometry) - 1;
1632   sheet->range.coli = column;
1633   sheet->active_cell.row = 0;
1634   sheet->active_cell.col = column;
1635
1636   g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, column);
1637   gtk_sheet_real_select_range (sheet, NULL);
1638 }
1639
1640
1641
1642
1643 static gboolean
1644 gtk_sheet_range_isvisible (const GtkSheet *sheet,
1645                            GtkSheetRange range)
1646 {
1647   g_return_val_if_fail (sheet != NULL, FALSE);
1648
1649   if (range.row0 < 0 || range.row0 >= g_sheet_row_get_row_count (sheet->row_geometry))
1650     return FALSE;
1651
1652   if (range.rowi < 0 || range.rowi >= g_sheet_row_get_row_count (sheet->row_geometry))
1653     return FALSE;
1654
1655   if (range.col0 < 0 || range.col0 >= g_sheet_column_get_column_count (sheet->column_geometry))
1656     return FALSE;
1657
1658   if (range.coli < 0 || range.coli >= g_sheet_column_get_column_count (sheet->column_geometry))
1659     return FALSE;
1660
1661   if (range.rowi < min_visible_row (sheet))
1662     return FALSE;
1663
1664   if (range.row0 > max_visible_row (sheet))
1665     return FALSE;
1666
1667   if (range.coli < min_visible_column (sheet))
1668     return FALSE;
1669
1670   if (range.col0 > max_visible_column (sheet))
1671     return FALSE;
1672
1673   return TRUE;
1674 }
1675
1676 static gboolean
1677 gtk_sheet_cell_isvisible (GtkSheet *sheet,
1678                           gint row, gint column)
1679 {
1680   GtkSheetRange range;
1681
1682   range.row0 = row;
1683   range.col0 = column;
1684   range.rowi = row;
1685   range.coli = column;
1686
1687   return gtk_sheet_range_isvisible (sheet, range);
1688 }
1689
1690 void
1691 gtk_sheet_get_visible_range (GtkSheet *sheet, GtkSheetRange *range)
1692 {
1693   g_return_if_fail (sheet != NULL);
1694   g_return_if_fail (GTK_IS_SHEET (sheet)) ;
1695   g_return_if_fail (range != NULL);
1696
1697   range->row0 = min_visible_row (sheet);
1698   range->col0 = min_visible_column (sheet);
1699   range->rowi = max_visible_row (sheet);
1700   range->coli = max_visible_column (sheet);
1701 }
1702
1703
1704 static void
1705 gtk_sheet_set_scroll_adjustments (GtkSheet *sheet,
1706                                   GtkAdjustment *hadjustment,
1707                                   GtkAdjustment *vadjustment)
1708 {
1709   if ( sheet->vadjustment != vadjustment )
1710     {
1711       if (sheet->vadjustment)
1712         g_object_unref (sheet->vadjustment);
1713       sheet->vadjustment = vadjustment;
1714       g_object_ref (vadjustment);
1715
1716       g_signal_connect (sheet->vadjustment, "value_changed",
1717                         G_CALLBACK (vadjustment_value_changed),
1718                         sheet);
1719     }
1720
1721   if ( sheet->hadjustment != hadjustment )
1722     {
1723       if (sheet->hadjustment)
1724         g_object_unref (sheet->hadjustment);
1725       sheet->hadjustment = hadjustment;
1726       g_object_ref (hadjustment);
1727
1728       g_signal_connect (sheet->hadjustment, "value_changed",
1729                         G_CALLBACK (hadjustment_value_changed),
1730                         sheet);
1731     }
1732 }
1733
1734 static void
1735 gtk_sheet_finalize (GObject *object)
1736 {
1737   GtkSheet *sheet;
1738
1739   g_return_if_fail (object != NULL);
1740   g_return_if_fail (GTK_IS_SHEET (object));
1741
1742   sheet = GTK_SHEET (object);
1743
1744   if (G_OBJECT_CLASS (parent_class)->finalize)
1745     (*G_OBJECT_CLASS (parent_class)->finalize) (object);
1746 }
1747
1748 static void
1749 gtk_sheet_dispose  (GObject *object)
1750 {
1751   GtkSheet *sheet = GTK_SHEET (object);
1752
1753   g_return_if_fail (object != NULL);
1754   g_return_if_fail (GTK_IS_SHEET (object));
1755
1756   if ( sheet->dispose_has_run )
1757     return ;
1758
1759   sheet->dispose_has_run = TRUE;
1760
1761   if (sheet->model) g_object_unref (sheet->model);
1762   if (sheet->row_geometry) g_object_unref (sheet->row_geometry);
1763   if (sheet->column_geometry) g_object_unref (sheet->column_geometry);
1764
1765   g_object_unref (sheet->entry_container);
1766   sheet->entry_container = NULL;
1767
1768   g_object_unref (sheet->button);
1769   sheet->button = NULL;
1770
1771   /* unref adjustments */
1772   if (sheet->hadjustment)
1773     {
1774       g_signal_handlers_disconnect_matched (sheet->hadjustment,
1775                                             G_SIGNAL_MATCH_DATA,
1776                                             0, 0, 0, 0,
1777                                             sheet);
1778
1779       g_object_unref (sheet->hadjustment);
1780       sheet->hadjustment = NULL;
1781     }
1782
1783   if (sheet->vadjustment)
1784     {
1785       g_signal_handlers_disconnect_matched (sheet->vadjustment,
1786                                             G_SIGNAL_MATCH_DATA,
1787                                             0, 0, 0, 0,
1788                                             sheet);
1789
1790       g_object_unref (sheet->vadjustment);
1791
1792       sheet->vadjustment = NULL;
1793     }
1794
1795   if (G_OBJECT_CLASS (parent_class)->dispose)
1796     (*G_OBJECT_CLASS (parent_class)->dispose) (object);
1797 }
1798
1799 static void
1800 gtk_sheet_style_set (GtkWidget *widget,
1801                      GtkStyle *previous_style)
1802 {
1803   GtkSheet *sheet;
1804
1805   g_return_if_fail (widget != NULL);
1806   g_return_if_fail (GTK_IS_SHEET (widget));
1807
1808   if (GTK_WIDGET_CLASS (parent_class)->style_set)
1809     (*GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous_style);
1810
1811   sheet = GTK_SHEET (widget);
1812
1813   if (GTK_WIDGET_REALIZED (widget))
1814     {
1815       gtk_style_set_background (widget->style, widget->window, widget->state);
1816     }
1817
1818 }
1819
1820 static void
1821 gtk_sheet_realize (GtkWidget *widget)
1822 {
1823   GtkSheet *sheet;
1824   GdkWindowAttr attributes;
1825   const gint attributes_mask =
1826     GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_CURSOR;
1827
1828   GdkGCValues values, auxvalues;
1829   GdkColormap *colormap;
1830   GdkDisplay *display;
1831
1832   g_return_if_fail (widget != NULL);
1833   g_return_if_fail (GTK_IS_SHEET (widget));
1834
1835   sheet = GTK_SHEET (widget);
1836
1837   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1838
1839   colormap = gtk_widget_get_colormap (widget);
1840   display = gtk_widget_get_display (widget);
1841
1842   attributes.window_type = GDK_WINDOW_CHILD;
1843   attributes.x = widget->allocation.x;
1844   attributes.y = widget->allocation.y;
1845   attributes.width = widget->allocation.width;
1846   attributes.height = widget->allocation.height;
1847   attributes.wclass = GDK_INPUT_OUTPUT;
1848
1849   attributes.visual = gtk_widget_get_visual (widget);
1850   attributes.colormap = colormap;
1851
1852   attributes.event_mask = gtk_widget_get_events (widget);
1853   attributes.event_mask |= (GDK_EXPOSURE_MASK |
1854                             GDK_BUTTON_PRESS_MASK |
1855                             GDK_BUTTON_RELEASE_MASK |
1856                             GDK_KEY_PRESS_MASK |
1857                             GDK_ENTER_NOTIFY_MASK |
1858                             GDK_LEAVE_NOTIFY_MASK |
1859                             GDK_POINTER_MOTION_MASK |
1860                             GDK_POINTER_MOTION_HINT_MASK);
1861
1862   attributes.cursor = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
1863
1864   /* main window */
1865   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1866
1867   gdk_window_set_user_data (widget->window, sheet);
1868
1869   widget->style = gtk_style_attach (widget->style, widget->window);
1870
1871   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1872
1873   gdk_color_parse ("white", &sheet->color[BG_COLOR]);
1874   gdk_colormap_alloc_color (colormap, &sheet->color[BG_COLOR], FALSE,
1875                             TRUE);
1876   gdk_color_parse ("gray", &sheet->color[GRID_COLOR]);
1877   gdk_colormap_alloc_color (colormap, &sheet->color[GRID_COLOR], FALSE,
1878                             TRUE);
1879
1880   attributes.x = 0;
1881   attributes.y = 0;
1882   attributes.width = sheet->column_title_area.width;
1883   attributes.height = sheet->column_title_area.height;
1884
1885
1886   /* column - title window */
1887   sheet->column_title_window =
1888     gdk_window_new (widget->window, &attributes, attributes_mask);
1889   gdk_window_set_user_data (sheet->column_title_window, sheet);
1890   gtk_style_set_background (widget->style, sheet->column_title_window,
1891                             GTK_STATE_NORMAL);
1892
1893
1894   attributes.x = 0;
1895   attributes.y = 0;
1896   attributes.width = sheet->row_title_area.width;
1897   attributes.height = sheet->row_title_area.height;
1898
1899   /* row - title window */
1900   sheet->row_title_window = gdk_window_new (widget->window,
1901                                             &attributes, attributes_mask);
1902   gdk_window_set_user_data (sheet->row_title_window, sheet);
1903   gtk_style_set_background (widget->style, sheet->row_title_window,
1904                             GTK_STATE_NORMAL);
1905
1906   /* sheet - window */
1907   attributes.cursor = gdk_cursor_new_for_display (display, GDK_PLUS);
1908
1909   attributes.x = 0;
1910   attributes.y = 0;
1911
1912   sheet->sheet_window = gdk_window_new (widget->window,
1913                                         &attributes, attributes_mask);
1914   gdk_window_set_user_data (sheet->sheet_window, sheet);
1915
1916   gdk_cursor_unref (attributes.cursor);
1917
1918   gdk_window_set_background (sheet->sheet_window, &widget->style->white);
1919   gdk_window_show (sheet->sheet_window);
1920
1921   /* GCs */
1922   sheet->fg_gc = gdk_gc_new (widget->window);
1923   sheet->bg_gc = gdk_gc_new (widget->window);
1924
1925   gdk_gc_get_values (sheet->fg_gc, &auxvalues);
1926
1927   values.foreground = widget->style->white;
1928   values.function = GDK_INVERT;
1929   values.subwindow_mode = GDK_INCLUDE_INFERIORS;
1930   values.line_width = 3;
1931
1932   sheet->xor_gc = gdk_gc_new_with_values (widget->window,
1933                                           &values,
1934                                           GDK_GC_FOREGROUND |
1935                                           GDK_GC_FUNCTION |
1936                                           GDK_GC_SUBWINDOW |
1937                                           GDK_GC_LINE_WIDTH
1938                                           );
1939
1940
1941   gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
1942   gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
1943
1944   gtk_widget_set_parent_window (sheet->button, sheet->sheet_window);
1945   gtk_widget_set_parent (sheet->button, GTK_WIDGET (sheet));
1946
1947
1948   sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
1949
1950   if (sheet->column_titles_visible)
1951     gdk_window_show (sheet->column_title_window);
1952   if (sheet->row_titles_visible)
1953     gdk_window_show (sheet->row_title_window);
1954
1955   sheet->hover_window = create_hover_window ();
1956
1957   draw_row_title_buttons (sheet);
1958   draw_column_title_buttons (sheet);
1959
1960   gtk_sheet_update_primary_selection (sheet);
1961 }
1962
1963 static void
1964 create_global_button (GtkSheet *sheet)
1965 {
1966   sheet->button = gtk_button_new_with_label (" ");
1967
1968   g_object_ref_sink (sheet->button);
1969
1970   g_signal_connect (sheet->button,
1971                     "pressed",
1972                     G_CALLBACK (global_button_clicked),
1973                     sheet);
1974 }
1975
1976 static void
1977 size_allocate_global_button (GtkSheet *sheet)
1978 {
1979   GtkAllocation allocation;
1980
1981   if (!sheet->column_titles_visible) return;
1982   if (!sheet->row_titles_visible) return;
1983
1984   gtk_widget_size_request (sheet->button, NULL);
1985
1986   allocation.x = 0;
1987   allocation.y = 0;
1988   allocation.width = sheet->row_title_area.width;
1989   allocation.height = sheet->column_title_area.height;
1990
1991   gtk_widget_size_allocate (sheet->button, &allocation);
1992   gtk_widget_show (sheet->button);
1993 }
1994
1995 static void
1996 global_button_clicked (GtkWidget *widget, gpointer data)
1997 {
1998   gtk_sheet_click_cell (GTK_SHEET (data), -1, -1);
1999   gtk_widget_grab_focus (GTK_WIDGET (data));
2000 }
2001
2002
2003 static void
2004 gtk_sheet_unrealize (GtkWidget *widget)
2005 {
2006   GtkSheet *sheet;
2007
2008   g_return_if_fail (widget != NULL);
2009   g_return_if_fail (GTK_IS_SHEET (widget));
2010
2011   sheet = GTK_SHEET (widget);
2012
2013   gdk_cursor_unref (sheet->cursor_drag);
2014   sheet->cursor_drag = NULL;
2015
2016   gdk_colormap_free_colors (gtk_widget_get_colormap (widget),
2017                             sheet->color, n_COLORS);
2018
2019   g_object_unref (sheet->xor_gc);
2020   g_object_unref (sheet->fg_gc);
2021   g_object_unref (sheet->bg_gc);
2022
2023   destroy_hover_window (sheet->hover_window);
2024
2025   gdk_window_destroy (sheet->sheet_window);
2026   gdk_window_destroy (sheet->column_title_window);
2027   gdk_window_destroy (sheet->row_title_window);
2028
2029   gtk_widget_unparent (sheet->entry_widget);
2030   if (sheet->button != NULL)
2031     gtk_widget_unparent (sheet->button);
2032
2033   if (GTK_WIDGET_CLASS (parent_class)->unrealize)
2034     (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
2035 }
2036
2037 static void
2038 gtk_sheet_map (GtkWidget *widget)
2039 {
2040   GtkSheet *sheet = GTK_SHEET (widget);
2041
2042   g_return_if_fail (widget != NULL);
2043   g_return_if_fail (GTK_IS_SHEET (widget));
2044
2045   if (!GTK_WIDGET_MAPPED (widget))
2046     {
2047       GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
2048
2049       gdk_window_show (widget->window);
2050       gdk_window_show (sheet->sheet_window);
2051
2052       if (sheet->column_titles_visible)
2053         {
2054           draw_column_title_buttons (sheet);
2055           gdk_window_show (sheet->column_title_window);
2056         }
2057       if (sheet->row_titles_visible)
2058         {
2059           draw_row_title_buttons (sheet);
2060           gdk_window_show (sheet->row_title_window);
2061         }
2062
2063       if (!GTK_WIDGET_MAPPED (sheet->entry_widget)
2064           && sheet->active_cell.row >= 0
2065           && sheet->active_cell.col >= 0 )
2066         {
2067           gtk_widget_show (sheet->entry_widget);
2068           gtk_widget_map (sheet->entry_widget);
2069         }
2070
2071       if (GTK_WIDGET_VISIBLE (sheet->button) &&
2072           !GTK_WIDGET_MAPPED (sheet->button))
2073         {
2074           gtk_widget_show (sheet->button);
2075           gtk_widget_map (sheet->button);
2076         }
2077
2078       if (GTK_BIN (sheet->button)->child)
2079         if (GTK_WIDGET_VISIBLE (GTK_BIN (sheet->button)->child) &&
2080             !GTK_WIDGET_MAPPED (GTK_BIN (sheet->button)->child))
2081           gtk_widget_map (GTK_BIN (sheet->button)->child);
2082
2083       gtk_sheet_range_draw (sheet, NULL);
2084       change_active_cell (sheet,
2085                      sheet->active_cell.row,
2086                      sheet->active_cell.col);
2087     }
2088 }
2089
2090 static void
2091 gtk_sheet_unmap (GtkWidget *widget)
2092 {
2093   GtkSheet *sheet = GTK_SHEET (widget);
2094
2095   if (!GTK_WIDGET_MAPPED (widget))
2096     return;
2097
2098   GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
2099
2100   gdk_window_hide (sheet->sheet_window);
2101   if (sheet->column_titles_visible)
2102     gdk_window_hide (sheet->column_title_window);
2103   if (sheet->row_titles_visible)
2104     gdk_window_hide (sheet->row_title_window);
2105   gdk_window_hide (widget->window);
2106
2107   if (GTK_WIDGET_MAPPED (sheet->entry_widget))
2108     gtk_widget_unmap (sheet->entry_widget);
2109
2110   if (GTK_WIDGET_MAPPED (sheet->button))
2111     gtk_widget_unmap (sheet->button);
2112 }
2113
2114
2115 static void
2116 gtk_sheet_cell_draw (GtkSheet *sheet, gint row, gint col)
2117 {
2118   PangoLayout *layout;
2119   PangoRectangle text;
2120   gint font_height;
2121
2122   gchar *label;
2123
2124   GtkSheetCellAttr attributes;
2125   GdkRectangle area;
2126
2127   g_return_if_fail (sheet != NULL);
2128
2129   /* bail now if we aren't yet drawable */
2130   if (!GTK_WIDGET_DRAWABLE (sheet)) return;
2131
2132   if (row < 0 ||
2133       row >= g_sheet_row_get_row_count (sheet->row_geometry))
2134     return;
2135
2136   if (col < 0 ||
2137       col >= g_sheet_column_get_column_count (sheet->column_geometry))
2138     return;
2139
2140   gtk_sheet_get_attributes (sheet, row, col, &attributes);
2141
2142   /* select GC for background rectangle */
2143   gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground);
2144   gdk_gc_set_foreground (sheet->bg_gc, &attributes.background);
2145
2146   rectangle_from_cell (sheet, row, col, &area);
2147
2148   gdk_gc_set_line_attributes (sheet->fg_gc, 1, 0, 0, 0);
2149
2150   if (sheet->show_grid)
2151     {
2152       gdk_gc_set_foreground (sheet->bg_gc, &sheet->color[GRID_COLOR]);
2153
2154       gdk_draw_rectangle (sheet->sheet_window,
2155                           sheet->bg_gc,
2156                           FALSE,
2157                           area.x, area.y,
2158                           area.width, area.height);
2159     }
2160
2161   //  gtk_sheet_cell_draw_label (sheet, row, col);
2162
2163
2164   label = gtk_sheet_cell_get_text (sheet, row, col);
2165   if (NULL == label)
2166     return;
2167
2168
2169   layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), label);
2170   dispose_string (sheet, label);
2171   pango_layout_set_font_description (layout, attributes.font_desc);
2172
2173
2174   pango_layout_get_pixel_extents (layout, NULL, &text);
2175
2176   gdk_gc_set_clip_rectangle (sheet->fg_gc, &area);
2177
2178   font_height = pango_font_description_get_size (attributes.font_desc);
2179   if ( !pango_font_description_get_size_is_absolute (attributes.font_desc))
2180     font_height /= PANGO_SCALE;
2181
2182   /* Centre the text vertically */
2183   area.y += (area.height - font_height) / 2.0;
2184
2185   switch (attributes.justification)
2186     {
2187     case GTK_JUSTIFY_RIGHT:
2188       area.x += area.width - text.width;
2189       break;
2190     case GTK_JUSTIFY_CENTER:
2191       area.x += (area.width - text.width) / 2.0;
2192       break;
2193     case GTK_JUSTIFY_LEFT:
2194       /* Do nothing */
2195       break;
2196     default:
2197       g_critical ("Unhandled justification %d in column %d\n",
2198                  attributes.justification, col);
2199       break;
2200     }
2201
2202   gdk_draw_layout (sheet->sheet_window, sheet->fg_gc,
2203                    area.x,
2204                    area.y,
2205                    layout);
2206
2207   gdk_gc_set_clip_rectangle (sheet->fg_gc, NULL);
2208   g_object_unref (layout);
2209 }
2210
2211
2212
2213 static void
2214 gtk_sheet_range_draw (GtkSheet *sheet, const GtkSheetRange *range)
2215 {
2216   gint i, j;
2217
2218   GdkRectangle area;
2219   GtkSheetRange drawing_range;
2220
2221   g_return_if_fail (sheet != NULL);
2222   g_return_if_fail (GTK_SHEET (sheet));
2223
2224   if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (sheet))) return;
2225   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2226   if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
2227
2228   if (range == NULL)
2229     {
2230       drawing_range.row0 = min_visible_row (sheet);
2231       drawing_range.col0 = min_visible_column (sheet);
2232       drawing_range.rowi = MIN (max_visible_row (sheet),
2233                                 g_sheet_row_get_row_count (sheet->row_geometry) - 1);
2234       drawing_range.coli = max_visible_column (sheet);
2235       gdk_drawable_get_size (sheet->sheet_window, &area.width, &area.height);
2236       area.x = area.y = 0;
2237     }
2238   else
2239     {
2240       drawing_range.row0 = MAX (range->row0, min_visible_row (sheet));
2241       drawing_range.col0 = MAX (range->col0, min_visible_column (sheet));
2242       drawing_range.rowi = MIN (range->rowi, max_visible_row (sheet));
2243       drawing_range.coli = MIN (range->coli, max_visible_column (sheet));
2244
2245
2246       rectangle_from_range (sheet, &drawing_range, &area);
2247     }
2248
2249
2250   gdk_draw_rectangle (sheet->sheet_window,
2251                       GTK_WIDGET (sheet)->style->white_gc,
2252                       TRUE,
2253                       area.x, area.y,
2254                       area.width, area.height);
2255
2256   for (i = drawing_range.row0; i <= drawing_range.rowi; i++)
2257     for (j = drawing_range.col0; j <= drawing_range.coli; j++)
2258       {
2259         gtk_sheet_cell_draw (sheet, i, j);
2260       }
2261
2262   if (sheet->state != GTK_SHEET_NORMAL &&
2263       gtk_sheet_range_isvisible (sheet, sheet->range))
2264     gtk_sheet_range_draw_selection (sheet, drawing_range);
2265
2266   if (sheet->state == GTK_STATE_NORMAL &&
2267       sheet->active_cell.row >= drawing_range.row0 &&
2268       sheet->active_cell.row <= drawing_range.rowi &&
2269       sheet->active_cell.col >= drawing_range.col0 &&
2270       sheet->active_cell.col <= drawing_range.coli)
2271     gtk_sheet_show_active_cell (sheet);
2272 }
2273
2274 static void
2275 gtk_sheet_range_draw_selection (GtkSheet *sheet, GtkSheetRange range)
2276 {
2277   GdkRectangle area;
2278   gint i, j;
2279   GtkSheetRange aux;
2280
2281   if (range.col0 > sheet->range.coli || range.coli < sheet->range.col0 ||
2282       range.row0 > sheet->range.rowi || range.rowi < sheet->range.row0)
2283     return;
2284
2285   if (!gtk_sheet_range_isvisible (sheet, range)) return;
2286   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2287
2288   aux = range;
2289
2290   range.col0 = MAX (sheet->range.col0, range.col0);
2291   range.coli = MIN (sheet->range.coli, range.coli);
2292   range.row0 = MAX (sheet->range.row0, range.row0);
2293   range.rowi = MIN (sheet->range.rowi, range.rowi);
2294
2295   range.col0 = MAX (range.col0, min_visible_column (sheet));
2296   range.coli = MIN (range.coli, max_visible_column (sheet));
2297   range.row0 = MAX (range.row0, min_visible_row (sheet));
2298   range.rowi = MIN (range.rowi, max_visible_row (sheet));
2299
2300   for (i = range.row0; i <= range.rowi; i++)
2301     {
2302       for (j = range.col0; j <= range.coli; j++)
2303         {
2304           if (gtk_sheet_cell_get_state (sheet, i, j) == GTK_STATE_SELECTED)
2305             {
2306               rectangle_from_cell (sheet, i, j, &area);
2307
2308               if (i == sheet->range.row0)
2309                 {
2310                   area.y = area.y + 2;
2311                   area.height = area.height - 2;
2312                 }
2313               if (i == sheet->range.rowi) area.height = area.height - 3;
2314               if (j == sheet->range.col0)
2315                 {
2316                   area.x = area.x + 2;
2317                   area.width = area.width - 2;
2318                 }
2319               if (j == sheet->range.coli) area.width = area.width - 3;
2320
2321               if (i != sheet->active_cell.row || j != sheet->active_cell.col)
2322                 {
2323                   gdk_draw_rectangle (sheet->sheet_window,
2324                                       sheet->xor_gc,
2325                                       TRUE,
2326                                       area.x + 1, area.y + 1,
2327                                       area.width, area.height);
2328                 }
2329             }
2330
2331         }
2332     }
2333
2334   gtk_sheet_draw_border (sheet, sheet->range);
2335 }
2336
2337 static void gtk_sheet_set_cell (GtkSheet *sheet, gint row, gint col,
2338                                 GtkJustification justification,
2339                                 const gchar *text);
2340
2341
2342 static inline gint
2343 safe_strcmp (const gchar *s1, const gchar *s2)
2344 {
2345   if ( !s1 && !s2) return 0;
2346   if ( !s1) return - 1;
2347   if ( !s2) return +1;
2348   return strcmp (s1, s2);
2349 }
2350
2351 static void
2352 gtk_sheet_set_cell (GtkSheet *sheet, gint row, gint col,
2353                     GtkJustification justification,
2354                     const gchar *text)
2355 {
2356   GSheetModel *model ;
2357   gboolean changed = FALSE;
2358   gchar *old_text ;
2359
2360   g_return_if_fail (sheet != NULL);
2361   g_return_if_fail (GTK_IS_SHEET (sheet));
2362
2363   if (col >= g_sheet_column_get_column_count (sheet->column_geometry)
2364       || row >= g_sheet_row_get_row_count (sheet->row_geometry))
2365     return;
2366
2367   if (col < 0 || row < 0) return;
2368
2369   model = gtk_sheet_get_model (sheet);
2370
2371   old_text = g_sheet_model_get_string (model, row, col);
2372
2373   if (0 != safe_strcmp (old_text, text))
2374     {
2375       changed = g_sheet_model_set_string (model, text, row, col);
2376     }
2377
2378   if ( g_sheet_model_free_strings (model))
2379     g_free (old_text);
2380
2381   if ( changed )
2382     g_signal_emit (sheet, sheet_signals[CHANGED], 0, row, col);
2383 }
2384
2385
2386 void
2387 gtk_sheet_cell_clear (GtkSheet *sheet, gint row, gint column)
2388 {
2389   GtkSheetRange range;
2390
2391   g_return_if_fail (sheet != NULL);
2392   g_return_if_fail (GTK_IS_SHEET (sheet));
2393   if (column >= g_sheet_column_get_column_count (sheet->column_geometry) ||
2394       row >= g_sheet_row_get_row_count (sheet->row_geometry)) return;
2395
2396   if (column < 0 || row < 0) return;
2397
2398   range.row0 = row;
2399   range.rowi = row;
2400   range.col0 = min_visible_column (sheet);
2401   range.coli = max_visible_column (sheet);
2402
2403   gtk_sheet_real_cell_clear (sheet, row, column);
2404
2405   gtk_sheet_range_draw (sheet, &range);
2406 }
2407
2408 static void
2409 gtk_sheet_real_cell_clear (GtkSheet *sheet, gint row, gint column)
2410 {
2411   GSheetModel *model = gtk_sheet_get_model (sheet);
2412
2413   gchar *old_text = gtk_sheet_cell_get_text (sheet, row, column);
2414
2415   if (old_text && strlen (old_text) > 0 )
2416     {
2417       g_sheet_model_datum_clear (model, row, column);
2418     }
2419
2420   dispose_string (sheet, old_text);
2421 }
2422
2423 gchar *
2424 gtk_sheet_cell_get_text (const GtkSheet *sheet, gint row, gint col)
2425 {
2426   GSheetModel *model;
2427   g_return_val_if_fail (sheet != NULL, NULL);
2428   g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);
2429
2430   if (col >= g_sheet_column_get_column_count (sheet->column_geometry) || row >= g_sheet_row_get_row_count (sheet->row_geometry))
2431     return NULL;
2432   if (col < 0 || row < 0) return NULL;
2433
2434   model = gtk_sheet_get_model (sheet);
2435
2436   if ( !model )
2437     return NULL;
2438
2439   return g_sheet_model_get_string (model, row, col);
2440 }
2441
2442
2443 static GtkStateType
2444 gtk_sheet_cell_get_state (GtkSheet *sheet, gint row, gint col)
2445 {
2446   gint state;
2447   GtkSheetRange *range;
2448
2449   g_return_val_if_fail (sheet != NULL, 0);
2450   g_return_val_if_fail (GTK_IS_SHEET (sheet), 0);
2451   if (col >= g_sheet_column_get_column_count (sheet->column_geometry) || row >= g_sheet_row_get_row_count (sheet->row_geometry)) return 0;
2452   if (col < 0 || row < 0) return 0;
2453
2454   state = sheet->state;
2455   range = &sheet->range;
2456
2457   switch (state)
2458     {
2459     case GTK_SHEET_NORMAL:
2460       return GTK_STATE_NORMAL;
2461       break;
2462     case GTK_SHEET_ROW_SELECTED:
2463       if (row >= range->row0 && row <= range->rowi)
2464         return GTK_STATE_SELECTED;
2465       break;
2466     case GTK_SHEET_COLUMN_SELECTED:
2467       if (col >= range->col0 && col <= range->coli)
2468         return GTK_STATE_SELECTED;
2469       break;
2470     case GTK_SHEET_RANGE_SELECTED:
2471       if (row >= range->row0 && row <= range->rowi && \
2472           col >= range->col0 && col <= range->coli)
2473         return GTK_STATE_SELECTED;
2474       break;
2475     }
2476   return GTK_STATE_NORMAL;
2477 }
2478
2479 /* Convert X, Y (in pixels) to *ROW, *COLUMN
2480    If the function returns FALSE, then the results will be unreliable.
2481 */
2482 static gboolean
2483 gtk_sheet_get_pixel_info (GtkSheet *sheet,
2484                           gint x,
2485                           gint y,
2486                           gint *row,
2487                           gint *column)
2488 {
2489   gint trow, tcol;
2490   *row = -G_MAXINT;
2491   *column = -G_MAXINT;
2492
2493   g_return_val_if_fail (sheet != NULL, 0);
2494   g_return_val_if_fail (GTK_IS_SHEET (sheet), 0);
2495
2496   /* bounds checking, return false if the user clicked
2497      on a blank area */
2498   if (y < 0)
2499     return FALSE;
2500
2501   if (x < 0)
2502     return FALSE;
2503
2504   if ( sheet->column_titles_visible)
2505     y -= sheet->column_title_area.height;
2506
2507   y += sheet->vadjustment->value;
2508
2509   if ( y < 0 && sheet->column_titles_visible)
2510     {
2511       trow = -1;
2512     }
2513   else
2514     {
2515       trow = yyy_row_ypixel_to_row (sheet, y);
2516       if (trow > g_sheet_row_get_row_count (sheet->row_geometry))
2517         return FALSE;
2518     }
2519
2520   *row = trow;
2521
2522   if ( sheet->row_titles_visible)
2523     x -= sheet->row_title_area.width;
2524
2525   x += sheet->hadjustment->value;
2526
2527   if ( x < 0 && sheet->row_titles_visible)
2528     {
2529       tcol = -1;
2530     }
2531   else
2532     {
2533       tcol = column_from_xpixel (sheet, x);
2534       if (tcol > g_sheet_column_get_column_count (sheet->column_geometry))
2535         return FALSE;
2536     }
2537
2538   *column = tcol;
2539
2540   return TRUE;
2541 }
2542
2543 gboolean
2544 gtk_sheet_get_cell_area (GtkSheet *sheet,
2545                          gint row,
2546                          gint column,
2547                          GdkRectangle *area)
2548 {
2549   g_return_val_if_fail (sheet != NULL, 0);
2550   g_return_val_if_fail (GTK_IS_SHEET (sheet), 0);
2551
2552   if (row >= g_sheet_row_get_row_count (sheet->row_geometry) || column >= g_sheet_column_get_column_count (sheet->column_geometry))
2553     return FALSE;
2554
2555   area->x = (column == -1) ? 0 : g_sheet_column_start_pixel (sheet->column_geometry, column);
2556   area->y = (row == -1)    ? 0 : g_sheet_row_start_pixel (sheet->row_geometry, row);
2557
2558   area->width= (column == -1) ? sheet->row_title_area.width
2559     : g_sheet_column_get_width (sheet->column_geometry, column);
2560
2561   area->height= (row == -1) ? sheet->column_title_area.height
2562     : g_sheet_row_get_height (sheet->row_geometry, row);
2563
2564   return TRUE;
2565 }
2566
2567 void
2568 gtk_sheet_set_active_cell (GtkSheet *sheet, gint row, gint col)
2569 {
2570   g_return_if_fail (sheet != NULL);
2571   g_return_if_fail (GTK_IS_SHEET (sheet));
2572
2573   if (row < -1 || col < -1)
2574     return;
2575
2576   if (row >= g_sheet_row_get_row_count (sheet->row_geometry)
2577       ||
2578       col >= g_sheet_column_get_column_count (sheet->column_geometry))
2579     return;
2580
2581   sheet->active_cell.row = row;
2582   sheet->active_cell.col = col;
2583
2584   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2585     return;
2586
2587   if ( row == -1 || col == -1)
2588     {
2589       gtk_sheet_hide_active_cell (sheet);
2590       return;
2591     }
2592
2593   change_active_cell (sheet, row, col);
2594 }
2595
2596 void
2597 gtk_sheet_get_active_cell (GtkSheet *sheet, gint *row, gint *column)
2598 {
2599   g_return_if_fail (sheet != NULL);
2600   g_return_if_fail (GTK_IS_SHEET (sheet));
2601
2602   if ( row ) *row = sheet->active_cell.row;
2603   if (column) *column = sheet->active_cell.col;
2604 }
2605
2606 static void
2607 gtk_sheet_entry_changed (GtkWidget *widget, gpointer data)
2608 {
2609   GtkSheet *sheet;
2610   gint row, col;
2611   const char *text;
2612   GtkJustification justification;
2613   GtkSheetCellAttr attributes;
2614
2615   g_return_if_fail (data != NULL);
2616   g_return_if_fail (GTK_IS_SHEET (data));
2617
2618   sheet = GTK_SHEET (data);
2619
2620   if (!GTK_WIDGET_VISIBLE (widget)) return;
2621   if (sheet->state != GTK_STATE_NORMAL) return;
2622
2623   row = sheet->active_cell.row;
2624   col = sheet->active_cell.col;
2625
2626   if (row < 0 || col < 0) return;
2627
2628   sheet->active_cell.row = -1;
2629   sheet->active_cell.col = -1;
2630
2631   text = gtk_entry_get_text (GTK_ENTRY (gtk_sheet_get_entry (sheet)));
2632
2633   if (text && strlen (text) > 0)
2634     {
2635       gtk_sheet_get_attributes (sheet, row, col, &attributes);
2636       justification = attributes.justification;
2637       gtk_sheet_set_cell (sheet, row, col, justification, text);
2638     }
2639
2640   sheet->active_cell.row = row;;
2641   sheet->active_cell.col = col;
2642 }
2643
2644
2645 static void
2646 gtk_sheet_hide_active_cell (GtkSheet *sheet)
2647 {
2648   GdkRectangle area;
2649
2650   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2651     return;
2652
2653   if (sheet->active_cell.row < 0 ||
2654       sheet->active_cell.col < 0) return;
2655
2656   gtk_widget_hide (sheet->entry_widget);
2657   gtk_widget_unmap (sheet->entry_widget);
2658
2659   rectangle_from_cell (sheet,
2660                        sheet->active_cell.row, sheet->active_cell.col,
2661                        &area);
2662
2663   gdk_draw_rectangle (sheet->sheet_window,
2664                       GTK_WIDGET (sheet)->style->white_gc,
2665                       TRUE,
2666                       area.x, area.y,
2667                       area.width, area.height);
2668
2669   gtk_sheet_cell_draw (sheet, sheet->active_cell.row,
2670                                sheet->active_cell.col);
2671
2672   GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2673 }
2674
2675 static void
2676 change_active_cell (GtkSheet *sheet, gint row, gint col)
2677 {
2678   gint old_row, old_col;
2679   glong old_handler_id = sheet->entry_handler_id;
2680
2681   g_return_if_fail (GTK_IS_SHEET (sheet));
2682
2683   if (row < 0 || col < 0)
2684     return;
2685
2686   if ( row > g_sheet_row_get_row_count (sheet->row_geometry)
2687        || col > g_sheet_column_get_column_count (sheet->column_geometry))
2688     return;
2689
2690   if (sheet->state != GTK_SHEET_NORMAL)
2691     {
2692       sheet->state = GTK_SHEET_NORMAL;
2693       gtk_sheet_real_unselect_range (sheet, NULL);
2694     }
2695
2696   g_signal_handler_block   (sheet->entry_widget, sheet->entry_handler_id);
2697
2698   old_row = sheet->active_cell.row;
2699   old_col = sheet->active_cell.col;
2700
2701   sheet->range.row0 = row;
2702   sheet->range.col0 = col;
2703   sheet->range.rowi = row;
2704   sheet->range.coli = col;
2705   sheet->active_cell.row = row;
2706   sheet->active_cell.col = col;
2707   sheet->selection_cell.row = row;
2708   sheet->selection_cell.col = col;
2709
2710   GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
2711
2712   gtk_sheet_show_active_cell (sheet);
2713
2714
2715   g_signal_emit (sheet, sheet_signals [ACTIVATE], 0,
2716                  row, col, old_row, old_col);
2717
2718   if ( old_handler_id == sheet->entry_handler_id)
2719     g_signal_handler_unblock   (sheet->entry_widget, sheet->entry_handler_id);
2720 }
2721
2722 static void
2723 gtk_sheet_show_active_cell (GtkSheet *sheet)
2724 {
2725   GtkEntry *sheet_entry;
2726   GtkSheetCellAttr attributes;
2727   gchar *text = NULL;
2728   gint row, col;
2729
2730   g_return_if_fail (sheet != NULL);
2731   g_return_if_fail (GTK_IS_SHEET (sheet));
2732
2733   row = sheet->active_cell.row;
2734   col = sheet->active_cell.col;
2735
2736   /* Don't show the active cell, if there is no active cell: */
2737   if (! (row >= 0 && col >= 0)) /* e.g row or coll == -1. */
2738     return;
2739
2740   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2741   if (sheet->state != GTK_SHEET_NORMAL) return;
2742   if (GTK_SHEET_IN_SELECTION (sheet)) return;
2743
2744   GTK_WIDGET_SET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2745
2746   sheet_entry = GTK_ENTRY (gtk_sheet_get_entry (sheet));
2747
2748   gtk_sheet_get_attributes (sheet, row, col, &attributes);
2749
2750
2751   text = gtk_sheet_cell_get_text (sheet, row, col);
2752   if ( ! text )
2753     text = g_strdup ("");
2754
2755   gtk_entry_set_visibility (GTK_ENTRY (sheet_entry), attributes.is_visible);
2756
2757
2758   if ( GTK_IS_ENTRY (sheet_entry))
2759     {
2760       const gchar *old_text = gtk_entry_get_text (GTK_ENTRY (sheet_entry));
2761       if (strcmp (old_text, text) != 0)
2762         gtk_entry_set_text (GTK_ENTRY (sheet_entry), text);
2763
2764       switch (attributes.justification)
2765         {
2766         case GTK_JUSTIFY_RIGHT:
2767           gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 1.0);
2768           break;
2769         case GTK_JUSTIFY_CENTER:
2770           gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.5);
2771           break;
2772         case GTK_JUSTIFY_LEFT:
2773         default:
2774           gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.0);
2775           break;
2776         }
2777     }
2778
2779   gtk_sheet_size_allocate_entry (sheet);
2780
2781   gtk_widget_map (sheet->entry_widget);
2782
2783   gtk_widget_grab_focus (GTK_WIDGET (sheet_entry));
2784
2785   dispose_string (sheet, text);
2786
2787 }
2788
2789 static void
2790 gtk_sheet_draw_active_cell (GtkSheet *sheet)
2791 {
2792   gint row, col;
2793   GtkSheetRange range;
2794
2795   if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (sheet))) return;
2796   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2797
2798   row = sheet->active_cell.row;
2799   col = sheet->active_cell.col;
2800
2801   if (row < 0 || col < 0) return;
2802
2803   if (!gtk_sheet_cell_isvisible (sheet, row, col))
2804     return;
2805
2806   range.col0 = range.coli = col;
2807   range.row0 = range.rowi = row;
2808
2809   gtk_sheet_draw_border (sheet, range);
2810 }
2811
2812
2813
2814 static void
2815 gtk_sheet_new_selection (GtkSheet *sheet, GtkSheetRange *range)
2816 {
2817   gint i, j, mask1, mask2;
2818   gint state, selected;
2819   gint x, y, width, height;
2820   GtkSheetRange new_range, aux_range;
2821
2822   g_return_if_fail (sheet != NULL);
2823
2824   if (range == NULL) range=&sheet->range;
2825
2826   new_range=*range;
2827
2828   range->row0 = MIN (range->row0, sheet->range.row0);
2829   range->rowi = MAX (range->rowi, sheet->range.rowi);
2830   range->col0 = MIN (range->col0, sheet->range.col0);
2831   range->coli = MAX (range->coli, sheet->range.coli);
2832
2833   range->row0 = MAX (range->row0, min_visible_row (sheet));
2834   range->rowi = MIN (range->rowi, max_visible_row (sheet));
2835   range->col0 = MAX (range->col0, min_visible_column (sheet));
2836   range->coli = MIN (range->coli, max_visible_column (sheet));
2837
2838   aux_range.row0 = MAX (new_range.row0, min_visible_row (sheet));
2839   aux_range.rowi = MIN (new_range.rowi, max_visible_row (sheet));
2840   aux_range.col0 = MAX (new_range.col0, min_visible_column (sheet));
2841   aux_range.coli = MIN (new_range.coli, max_visible_column (sheet));
2842
2843   for (i = range->row0; i <= range->rowi; i++)
2844     {
2845       for (j = range->col0; j <= range->coli; j++)
2846         {
2847
2848           state = gtk_sheet_cell_get_state (sheet, i, j);
2849           selected= (i <= new_range.rowi && i >= new_range.row0 &&
2850                      j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
2851
2852           if (state == GTK_STATE_SELECTED && selected &&
2853               (i == sheet->range.row0 || i == sheet->range.rowi ||
2854                j == sheet->range.col0 || j == sheet->range.coli ||
2855                i == new_range.row0 || i == new_range.rowi ||
2856                j == new_range.col0 || j == new_range.coli))
2857             {
2858
2859               mask1 = i == sheet->range.row0 ? 1 : 0;
2860               mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1;
2861               mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1;
2862               mask1 = j == sheet->range.coli ? mask1 + 8 : mask1;
2863
2864               mask2 = i == new_range.row0 ? 1 : 0;
2865               mask2 = i == new_range.rowi ? mask2 + 2 : mask2;
2866               mask2 = j == new_range.col0 ? mask2 + 4 : mask2;
2867               mask2 = j == new_range.coli ? mask2 + 8 : mask2;
2868
2869               if (mask1 != mask2)
2870                 {
2871                   x = g_sheet_column_start_pixel (sheet->column_geometry, j);
2872                   y = g_sheet_row_start_pixel (sheet->row_geometry, i);
2873                   width = g_sheet_column_start_pixel (sheet->column_geometry, j)- x+
2874                     g_sheet_column_get_width (sheet->column_geometry, j);
2875                   height = g_sheet_row_start_pixel (sheet->row_geometry, i) - y + g_sheet_row_get_height (sheet->row_geometry, i);
2876
2877                   if (i == sheet->range.row0)
2878                     {
2879                       y = y - 3;
2880                       height = height + 3;
2881                     }
2882                   if (i == sheet->range.rowi) height = height + 3;
2883                   if (j == sheet->range.col0)
2884                     {
2885                       x = x - 3;
2886                       width = width + 3;
2887                     }
2888                   if (j == sheet->range.coli) width = width + 3;
2889
2890                   if (i != sheet->active_cell.row || j != sheet->active_cell.col)
2891                     {
2892                       x = g_sheet_column_start_pixel (sheet->column_geometry, j);
2893                       y = g_sheet_row_start_pixel (sheet->row_geometry, i);
2894                       width = g_sheet_column_start_pixel (sheet->column_geometry, j)- x+
2895                         g_sheet_column_get_width (sheet->column_geometry, j);
2896
2897                       height = g_sheet_row_start_pixel (sheet->row_geometry, i) - y + g_sheet_row_get_height (sheet->row_geometry, i);
2898
2899                       if (i == new_range.row0)
2900                         {
2901                           y = y+2;
2902                           height = height - 2;
2903                         }
2904                       if (i == new_range.rowi) height = height - 3;
2905                       if (j == new_range.col0)
2906                         {
2907                           x = x+2;
2908                           width = width - 2;
2909                         }
2910                       if (j == new_range.coli) width = width - 3;
2911
2912                       gdk_draw_rectangle (sheet->sheet_window,
2913                                           sheet->xor_gc,
2914                                           TRUE,
2915                                           x + 1, y + 1,
2916                                           width, height);
2917                     }
2918                 }
2919             }
2920         }
2921     }
2922
2923   for (i = range->row0; i <= range->rowi; i++)
2924     {
2925       for (j = range->col0; j <= range->coli; j++)
2926         {
2927
2928           state = gtk_sheet_cell_get_state (sheet, i, j);
2929           selected= (i <= new_range.rowi && i >= new_range.row0 &&
2930                      j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
2931
2932           if (state == GTK_STATE_SELECTED && !selected)
2933             {
2934
2935               x = g_sheet_column_start_pixel (sheet->column_geometry, j);
2936               y = g_sheet_row_start_pixel (sheet->row_geometry, i);
2937               width = g_sheet_column_start_pixel (sheet->column_geometry, j) - x + g_sheet_column_get_width (sheet->column_geometry, j);
2938               height = g_sheet_row_start_pixel (sheet->row_geometry, i) - y + g_sheet_row_get_height (sheet->row_geometry, i);
2939
2940               if (i == sheet->range.row0)
2941                 {
2942                   y = y - 3;
2943                   height = height + 3;
2944                 }
2945               if (i == sheet->range.rowi) height = height + 3;
2946               if (j == sheet->range.col0)
2947                 {
2948                   x = x - 3;
2949                   width = width + 3;
2950                 }
2951               if (j == sheet->range.coli) width = width + 3;
2952
2953             }
2954         }
2955     }
2956
2957   for (i = range->row0; i <= range->rowi; i++)
2958     {
2959       for (j = range->col0; j <= range->coli; j++)
2960         {
2961
2962           state = gtk_sheet_cell_get_state (sheet, i, j);
2963           selected= (i <= new_range.rowi && i >= new_range.row0 &&
2964                      j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
2965
2966           if (state != GTK_STATE_SELECTED && selected &&
2967               (i != sheet->active_cell.row || j != sheet->active_cell.col))
2968             {
2969
2970               x = g_sheet_column_start_pixel (sheet->column_geometry, j);
2971               y = g_sheet_row_start_pixel (sheet->row_geometry, i);
2972               width = g_sheet_column_start_pixel (sheet->column_geometry, j) - x + g_sheet_column_get_width (sheet->column_geometry, j);
2973               height = g_sheet_row_start_pixel (sheet->row_geometry, i) - y + g_sheet_row_get_height (sheet->row_geometry, i);
2974
2975               if (i == new_range.row0)
2976                 {
2977                   y = y+2;
2978                   height = height - 2;
2979                 }
2980               if (i == new_range.rowi) height = height - 3;
2981               if (j == new_range.col0)
2982                 {
2983                   x = x+2;
2984                   width = width - 2;
2985                 }
2986               if (j == new_range.coli) width = width - 3;
2987
2988               gdk_draw_rectangle (sheet->sheet_window,
2989                                   sheet->xor_gc,
2990                                   TRUE,
2991                                   x + 1, y + 1,
2992                                   width, height);
2993
2994             }
2995
2996         }
2997     }
2998
2999   for (i = aux_range.row0; i <= aux_range.rowi; i++)
3000     {
3001       for (j = aux_range.col0; j <= aux_range.coli; j++)
3002         {
3003           state = gtk_sheet_cell_get_state (sheet, i, j);
3004
3005           mask1 = i == sheet->range.row0 ? 1 : 0;
3006           mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1;
3007           mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1;
3008           mask1 = j == sheet->range.coli ? mask1 + 8 : mask1;
3009
3010           mask2 = i == new_range.row0 ? 1 : 0;
3011           mask2 = i == new_range.rowi ? mask2 + 2 : mask2;
3012           mask2 = j == new_range.col0 ? mask2 + 4 : mask2;
3013           mask2 = j == new_range.coli ? mask2 + 8 : mask2;
3014           if (mask2 != mask1 || (mask2 == mask1 && state != GTK_STATE_SELECTED))
3015             {
3016               x = g_sheet_column_start_pixel (sheet->column_geometry, j);
3017               y = g_sheet_row_start_pixel (sheet->row_geometry, i);
3018               width = g_sheet_column_get_width (sheet->column_geometry, j);
3019               height = g_sheet_row_get_height (sheet->row_geometry, i);
3020               if (mask2 & 1)
3021                 gdk_draw_rectangle (sheet->sheet_window,
3022                                     sheet->xor_gc,
3023                                     TRUE,
3024                                     x + 1, y - 1,
3025                                     width, 3);
3026
3027
3028               if (mask2 & 2)
3029                 gdk_draw_rectangle (sheet->sheet_window,
3030                                     sheet->xor_gc,
3031                                     TRUE,
3032                                     x + 1, y + height - 1,
3033                                     width, 3);
3034
3035               if (mask2 & 4)
3036                 gdk_draw_rectangle (sheet->sheet_window,
3037                                     sheet->xor_gc,
3038                                     TRUE,
3039                                     x - 1, y + 1,
3040                                     3, height);
3041
3042
3043               if (mask2 & 8)
3044                 gdk_draw_rectangle (sheet->sheet_window,
3045                                     sheet->xor_gc,
3046                                     TRUE,
3047                                     x + width - 1, y + 1,
3048                                     3, height);
3049             }
3050         }
3051     }
3052
3053   *range = new_range;
3054 }
3055
3056 static void
3057 gtk_sheet_draw_border (GtkSheet *sheet, GtkSheetRange new_range)
3058 {
3059   GdkRectangle area;
3060
3061   rectangle_from_range (sheet, &new_range, &area);
3062
3063   gdk_draw_rectangle (sheet->sheet_window,
3064                       sheet->xor_gc,
3065                       FALSE,
3066                       area.x + 1,
3067                       area.y + 1,
3068                       area.width - 2,
3069                       area.height - 2);
3070 }
3071
3072
3073 static void
3074 gtk_sheet_real_select_range (GtkSheet *sheet,
3075                              const GtkSheetRange *range)
3076 {
3077   gint state;
3078
3079   g_return_if_fail (sheet != NULL);
3080
3081   if (range == NULL) range = &sheet->range;
3082
3083   memcpy (&sheet->range, range, sizeof (*range));
3084
3085   if (range->row0 < 0 || range->rowi < 0) return;
3086   if (range->col0 < 0 || range->coli < 0) return;
3087
3088   state = sheet->state;
3089
3090 #if 0
3091   if (range->coli != sheet->range.coli || range->col0 != sheet->range.col0 ||
3092       range->rowi != sheet->range.rowi || range->row0 != sheet->range.row0)
3093     {
3094       gtk_sheet_new_selection (sheet, &sheet->range);
3095     }
3096   else
3097     {
3098       gtk_sheet_range_draw_selection (sheet, sheet->range);
3099     }
3100 #endif
3101
3102   gtk_sheet_update_primary_selection (sheet);
3103
3104   g_signal_emit (sheet, sheet_signals[SELECT_RANGE], 0, &sheet->range);
3105 }
3106
3107
3108 void
3109 gtk_sheet_get_selected_range (GtkSheet *sheet, GtkSheetRange *range)
3110 {
3111   g_return_if_fail (sheet != NULL);
3112   *range = sheet->range;
3113 }
3114
3115
3116 void
3117 gtk_sheet_select_range (GtkSheet *sheet, const GtkSheetRange *range)
3118 {
3119   g_return_if_fail (sheet != NULL);
3120
3121   if (range == NULL) range=&sheet->range;
3122
3123   if (range->row0 < 0 || range->rowi < 0) return;
3124   if (range->col0 < 0 || range->coli < 0) return;
3125
3126
3127   if (sheet->state != GTK_SHEET_NORMAL)
3128     gtk_sheet_real_unselect_range (sheet, NULL);
3129
3130   sheet->range.row0 = range->row0;
3131   sheet->range.rowi = range->rowi;
3132   sheet->range.col0 = range->col0;
3133   sheet->range.coli = range->coli;
3134   sheet->active_cell.row = range->row0;
3135   sheet->active_cell.col = range->col0;
3136   sheet->selection_cell.row = range->rowi;
3137   sheet->selection_cell.col = range->coli;
3138
3139   sheet->state = GTK_SHEET_RANGE_SELECTED;
3140   gtk_sheet_real_select_range (sheet, NULL);
3141 }
3142
3143 void
3144 gtk_sheet_unselect_range (GtkSheet *sheet)
3145 {
3146   if (! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
3147     return;
3148
3149   gtk_sheet_real_unselect_range (sheet, NULL);
3150   sheet->state = GTK_STATE_NORMAL;
3151
3152   change_active_cell (sheet,
3153                  sheet->active_cell.row, sheet->active_cell.col);
3154 }
3155
3156
3157 static void
3158 gtk_sheet_real_unselect_range (GtkSheet *sheet,
3159                                const GtkSheetRange *range)
3160 {
3161   g_return_if_fail (sheet != NULL);
3162   g_return_if_fail (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)));
3163
3164   if ( range == NULL)
3165     range = &sheet->range;
3166
3167   if (range->row0 < 0 || range->rowi < 0) return;
3168   if (range->col0 < 0 || range->coli < 0) return;
3169
3170   g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, -1);
3171   g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, -1);
3172
3173 #if 0
3174   if (gtk_sheet_range_isvisible (sheet, *range))
3175     gtk_sheet_draw_backing_pixmap (sheet, *range);
3176 #endif
3177
3178   sheet->range.row0 = -1;
3179   sheet->range.rowi = -1;
3180   sheet->range.col0 = -1;
3181   sheet->range.coli = -1;
3182 }
3183
3184
3185 static gint
3186 gtk_sheet_expose (GtkWidget *widget,
3187                   GdkEventExpose *event)
3188 {
3189   GtkSheet *sheet;
3190   GtkSheetRange range;
3191
3192   g_return_val_if_fail (widget != NULL, FALSE);
3193   g_return_val_if_fail (GTK_IS_SHEET (widget), FALSE);
3194   g_return_val_if_fail (event != NULL, FALSE);
3195
3196   sheet = GTK_SHEET (widget);
3197
3198   if (!GTK_WIDGET_DRAWABLE (widget))
3199     return FALSE;
3200
3201   /* exposure events on the sheet */
3202   if (event->window == sheet->row_title_window &&
3203       sheet->row_titles_visible)
3204     {
3205       gint i;
3206       for (i = min_visible_row (sheet); i <= max_visible_row (sheet); i++)
3207         gtk_sheet_row_title_button_draw (sheet, i);
3208     }
3209
3210   if (event->window == sheet->column_title_window &&
3211       sheet->column_titles_visible)
3212     {
3213       gint i;
3214       for (i = min_visible_column (sheet);
3215            i <= max_visible_column (sheet);
3216            ++i)
3217         gtk_sheet_column_title_button_draw (sheet, i);
3218     }
3219
3220
3221   range.row0 =
3222     yyy_row_ypixel_to_row (sheet,
3223                            event->area.y + sheet->vadjustment->value);
3224   range.row0--;
3225
3226   range.rowi =
3227     yyy_row_ypixel_to_row (sheet,
3228                            event->area.y +
3229                            event->area.height + sheet->vadjustment->value);
3230   range.rowi++;
3231
3232   range.col0 =
3233     column_from_xpixel (sheet,
3234                         event->area.x + sheet->hadjustment->value);
3235   range.col0--;
3236
3237   range.coli =
3238     column_from_xpixel (sheet,
3239                         event->area.x + event->area.width +
3240                         sheet->hadjustment->value);
3241   range.coli++;
3242
3243   if (event->window == sheet->sheet_window)
3244     {
3245       gtk_sheet_range_draw (sheet, &range);
3246
3247       if (sheet->state != GTK_SHEET_NORMAL)
3248         {
3249           if (gtk_sheet_range_isvisible (sheet, sheet->range))
3250             gtk_sheet_range_draw (sheet, &sheet->range);
3251
3252           if (GTK_SHEET_IN_RESIZE (sheet) || GTK_SHEET_IN_DRAG (sheet))
3253             gtk_sheet_range_draw (sheet, &sheet->drag_range);
3254
3255           if (gtk_sheet_range_isvisible (sheet, sheet->range))
3256             gtk_sheet_range_draw_selection (sheet, sheet->range);
3257           if (GTK_SHEET_IN_RESIZE (sheet) || GTK_SHEET_IN_DRAG (sheet))
3258             draw_xor_rectangle (sheet, sheet->drag_range);
3259         }
3260
3261       if ((!GTK_SHEET_IN_XDRAG (sheet)) && (!GTK_SHEET_IN_YDRAG (sheet)))
3262         {
3263           if (sheet->state == GTK_SHEET_NORMAL)
3264             gtk_sheet_draw_active_cell (sheet);
3265         }
3266     }
3267
3268   if (sheet->state != GTK_SHEET_NORMAL && GTK_SHEET_IN_SELECTION (sheet))
3269     gtk_widget_grab_focus (GTK_WIDGET (sheet));
3270
3271   (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
3272
3273   return FALSE;
3274 }
3275
3276
3277 static gboolean
3278 gtk_sheet_button_press (GtkWidget *widget,
3279                         GdkEventButton *event)
3280 {
3281   GtkSheet *sheet;
3282   GdkModifierType mods;
3283   gint x, y;
3284   gint  row, column;
3285   gboolean veto;
3286
3287   g_return_val_if_fail (widget != NULL, FALSE);
3288   g_return_val_if_fail (GTK_IS_SHEET (widget), FALSE);
3289   g_return_val_if_fail (event != NULL, FALSE);
3290
3291   sheet = GTK_SHEET (widget);
3292
3293   /* Cancel any pending tooltips */
3294   if (sheet->motion_timer)
3295     {
3296       g_source_remove (sheet->motion_timer);
3297       sheet->motion_timer = 0;
3298     }
3299
3300   gtk_widget_get_pointer (widget, &x, &y);
3301   gtk_sheet_get_pixel_info (sheet, x, y, &row, &column);
3302
3303
3304   if (event->window == sheet->column_title_window)
3305     {
3306       g_signal_emit (sheet,
3307                      sheet_signals[BUTTON_EVENT_COLUMN], 0,
3308                      column, event);
3309
3310       if (g_sheet_column_get_sensitivity (sheet->column_geometry, column))
3311         {
3312           if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
3313             g_signal_emit (sheet,
3314                            sheet_signals[DOUBLE_CLICK_COLUMN], 0, column);
3315         }
3316     }
3317   else if (event->window == sheet->row_title_window)
3318     {
3319       g_signal_emit (sheet,
3320                      sheet_signals[BUTTON_EVENT_ROW], 0,
3321                      row, event);
3322
3323
3324       if (g_sheet_row_get_sensitivity (sheet->row_geometry, row))
3325         {
3326           if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
3327             g_signal_emit (sheet,
3328                            sheet_signals[DOUBLE_CLICK_ROW], 0, row);
3329         }
3330     }
3331
3332   gdk_window_get_pointer (widget->window, NULL, NULL, &mods);
3333
3334   if (! (mods & GDK_BUTTON1_MASK)) return TRUE;
3335
3336
3337   /* press on resize windows */
3338   if (event->window == sheet->column_title_window &&
3339       gtk_sheet_columns_resizable (sheet))
3340     {
3341 #if 0
3342       gtk_widget_get_pointer (widget, &sheet->x_drag, NULL);
3343       if ( sheet->row_titles_visible)
3344         sheet->x_drag -= sheet->row_title_area.width;
3345 #endif
3346
3347       sheet->x_drag = event->x;
3348
3349       if (on_column_boundary (sheet, sheet->x_drag, &sheet->drag_cell.col))
3350         {
3351           guint req;
3352           if (event->type == GDK_2BUTTON_PRESS)
3353             {
3354               gtk_sheet_autoresize_column (sheet, sheet->drag_cell.col);
3355               GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_XDRAG);
3356               return TRUE;
3357             }
3358           gtk_sheet_column_size_request (sheet, sheet->drag_cell.col, &req);
3359           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_XDRAG);
3360           gdk_pointer_grab (sheet->column_title_window, FALSE,
3361                             GDK_POINTER_MOTION_HINT_MASK |
3362                             GDK_BUTTON1_MOTION_MASK |
3363                             GDK_BUTTON_RELEASE_MASK,
3364                             NULL, NULL, event->time);
3365
3366           draw_xor_vline (sheet);
3367           return TRUE;
3368         }
3369     }
3370
3371   if (event->window == sheet->row_title_window && gtk_sheet_rows_resizable (sheet))
3372     {
3373       gtk_widget_get_pointer (widget, NULL, &sheet->y_drag);
3374
3375       if (POSSIBLE_YDRAG (sheet, sheet->y_drag, &sheet->drag_cell.row))
3376         {
3377           guint req;
3378           gtk_sheet_row_size_request (sheet, sheet->drag_cell.row, &req);
3379           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_YDRAG);
3380           gdk_pointer_grab (sheet->row_title_window, FALSE,
3381                             GDK_POINTER_MOTION_HINT_MASK |
3382                             GDK_BUTTON1_MOTION_MASK |
3383                             GDK_BUTTON_RELEASE_MASK,
3384                             NULL, NULL, event->time);
3385
3386           draw_xor_hline (sheet);
3387           return TRUE;
3388         }
3389     }
3390
3391   /* the sheet itself does not handle other than single click events */
3392   if (event->type != GDK_BUTTON_PRESS) return FALSE;
3393
3394   /* selections on the sheet */
3395   if (event->window == sheet->sheet_window)
3396     {
3397       gtk_widget_get_pointer (widget, &x, &y);
3398       gtk_sheet_get_pixel_info (sheet, x, y, &row, &column);
3399       gdk_pointer_grab (sheet->sheet_window, FALSE,
3400                         GDK_POINTER_MOTION_HINT_MASK |
3401                         GDK_BUTTON1_MOTION_MASK |
3402                         GDK_BUTTON_RELEASE_MASK,
3403                         NULL, NULL, event->time);
3404       gtk_grab_add (GTK_WIDGET (sheet));
3405
3406       /* This seems to be a kludge to work around a problem where the sheet
3407          scrolls to another position.  The timeout scrolls it back to its
3408          original posn.          JMD 3 July 2007
3409       */
3410       gtk_widget_grab_focus (GTK_WIDGET (sheet));
3411
3412       if (sheet->selection_mode != GTK_SELECTION_SINGLE &&
3413           sheet->selection_mode != GTK_SELECTION_NONE &&
3414           sheet->cursor_drag->type == GDK_SIZING &&
3415           !GTK_SHEET_IN_SELECTION (sheet) && !GTK_SHEET_IN_RESIZE (sheet))
3416         {
3417           if (sheet->state == GTK_STATE_NORMAL)
3418             {
3419               row = sheet->active_cell.row;
3420               column = sheet->active_cell.col;
3421               sheet->active_cell.row = row;
3422               sheet->active_cell.col = column;
3423               sheet->drag_range = sheet->range;
3424               sheet->state = GTK_SHEET_RANGE_SELECTED;
3425               gtk_sheet_select_range (sheet, &sheet->drag_range);
3426             }
3427           sheet->x_drag = x;
3428           sheet->y_drag = y;
3429           if (row > sheet->range.rowi) row--;
3430           if (column > sheet->range.coli) column--;
3431           sheet->drag_cell.row = row;
3432           sheet->drag_cell.col = column;
3433           sheet->drag_range = sheet->range;
3434           draw_xor_rectangle (sheet, sheet->drag_range);
3435           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_RESIZE);
3436         }
3437       else if (sheet->cursor_drag->type == GDK_TOP_LEFT_ARROW &&
3438                !GTK_SHEET_IN_SELECTION (sheet)
3439                && ! GTK_SHEET_IN_DRAG (sheet)
3440                && sheet->active_cell.row >= 0
3441                && sheet->active_cell.col >= 0
3442                )
3443         {
3444           if (sheet->state == GTK_STATE_NORMAL)
3445             {
3446               row = sheet->active_cell.row;
3447               column = sheet->active_cell.col;
3448               sheet->active_cell.row = row;
3449               sheet->active_cell.col = column;
3450               sheet->drag_range = sheet->range;
3451               sheet->state = GTK_SHEET_RANGE_SELECTED;
3452               gtk_sheet_select_range (sheet, &sheet->drag_range);
3453             }
3454           sheet->x_drag = x;
3455           sheet->y_drag = y;
3456           if (row < sheet->range.row0) row++;
3457           if (row > sheet->range.rowi) row--;
3458           if (column < sheet->range.col0) column++;
3459           if (column > sheet->range.coli) column--;
3460           sheet->drag_cell.row = row;
3461           sheet->drag_cell.col = column;
3462           sheet->drag_range = sheet->range;
3463           draw_xor_rectangle (sheet, sheet->drag_range);
3464           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_DRAG);
3465         }
3466       else
3467         {
3468           veto = gtk_sheet_click_cell (sheet, row, column);
3469           if (veto) GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
3470         }
3471     }
3472
3473   if (event->window == sheet->column_title_window)
3474     {
3475       gtk_widget_get_pointer (widget, &x, &y);
3476       if ( sheet->row_titles_visible)
3477         x -= sheet->row_title_area.width;
3478
3479       x += sheet->hadjustment->value;
3480
3481       column = column_from_xpixel (sheet, x);
3482
3483       if (g_sheet_column_get_sensitivity (sheet->column_geometry, column))
3484         {
3485           veto = gtk_sheet_click_cell (sheet, -1, column);
3486           gtk_grab_add (GTK_WIDGET (sheet));
3487           gtk_widget_grab_focus (GTK_WIDGET (sheet));
3488           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
3489         }
3490     }
3491
3492   if (event->window == sheet->row_title_window)
3493     {
3494       gtk_widget_get_pointer (widget, &x, &y);
3495       if ( sheet->column_titles_visible)
3496         y -= sheet->column_title_area.height;
3497
3498       y += sheet->vadjustment->value;
3499
3500       row = yyy_row_ypixel_to_row (sheet, y);
3501       if (g_sheet_row_get_sensitivity (sheet->row_geometry, row))
3502         {
3503           veto = gtk_sheet_click_cell (sheet, row, -1);
3504           gtk_grab_add (GTK_WIDGET (sheet));
3505           gtk_widget_grab_focus (GTK_WIDGET (sheet));
3506           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
3507         }
3508     }
3509
3510   return TRUE;
3511 }
3512
3513 static gboolean
3514 gtk_sheet_click_cell (GtkSheet *sheet, gint row, gint column)
3515 {
3516   gboolean forbid_move;
3517
3518   if (row >= g_sheet_row_get_row_count (sheet->row_geometry)
3519       || column >= g_sheet_column_get_column_count (sheet->column_geometry))
3520     {
3521       return FALSE;
3522     }
3523
3524   g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
3525                  sheet->active_cell.row, sheet->active_cell.col,
3526                  &row, &column, &forbid_move);
3527
3528   if (forbid_move)
3529     {
3530       if (sheet->state == GTK_STATE_NORMAL)
3531         return FALSE;
3532
3533       row = sheet->active_cell.row;
3534       column = sheet->active_cell.col;
3535
3536       change_active_cell (sheet, row, column);
3537       return FALSE;
3538     }
3539
3540   if (row == -1 && column >= 0)
3541     {
3542       gtk_sheet_select_column (sheet, column);
3543       return TRUE;
3544     }
3545
3546   if (column == -1 && row >= 0)
3547     {
3548       gtk_sheet_select_row (sheet, row);
3549       return TRUE;
3550     }
3551
3552   if (row == -1 && column == -1)
3553     {
3554       sheet->range.row0 = 0;
3555       sheet->range.col0 = 0;
3556       sheet->range.rowi = g_sheet_row_get_row_count (sheet->row_geometry) - 1;
3557       sheet->range.coli =
3558         g_sheet_column_get_column_count (sheet->column_geometry) - 1;
3559       sheet->active_cell.row = 0;
3560       sheet->active_cell.col = 0;
3561       gtk_sheet_select_range (sheet, NULL);
3562       return TRUE;
3563     }
3564
3565   if (sheet->state != GTK_SHEET_NORMAL)
3566     {
3567       sheet->state = GTK_SHEET_NORMAL;
3568       gtk_sheet_real_unselect_range (sheet, NULL);
3569     }
3570   else
3571     {
3572       change_active_cell (sheet, row, column);
3573     }
3574
3575   sheet->active_cell.row = row;
3576   sheet->active_cell.col = column;
3577   sheet->selection_cell.row = row;
3578   sheet->selection_cell.col = column;
3579   sheet->range.row0 = row;
3580   sheet->range.col0 = column;
3581   sheet->range.rowi = row;
3582   sheet->range.coli = column;
3583   sheet->state = GTK_SHEET_NORMAL;
3584   GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
3585   gtk_sheet_draw_active_cell (sheet);
3586   return TRUE;
3587 }
3588
3589 static gint
3590 gtk_sheet_button_release (GtkWidget *widget,
3591                           GdkEventButton *event)
3592 {
3593   gint y;
3594   GdkDisplay *display = gtk_widget_get_display (widget);
3595
3596   GtkSheet *sheet = GTK_SHEET (widget);
3597
3598   /* release on resize windows */
3599   if (GTK_SHEET_IN_XDRAG (sheet))
3600     {
3601       gint xpos = event->x;
3602       gint width;
3603       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_XDRAG);
3604       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
3605
3606       gdk_display_pointer_ungrab (display, event->time);
3607       draw_xor_vline (sheet);
3608
3609       width = new_column_width (sheet, sheet->drag_cell.col, &xpos);
3610
3611       gtk_sheet_set_column_width (sheet, sheet->drag_cell.col, width);
3612       return TRUE;
3613     }
3614
3615   if (GTK_SHEET_IN_YDRAG (sheet))
3616     {
3617       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_YDRAG);
3618       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
3619       gtk_widget_get_pointer (widget, NULL, &y);
3620       gdk_display_pointer_ungrab (display, event->time);
3621       draw_xor_hline (sheet);
3622
3623       gtk_sheet_set_row_height (sheet, sheet->drag_cell.row,
3624                                 new_row_height (sheet, sheet->drag_cell.row, &y));
3625       g_signal_emit_by_name (sheet->vadjustment, "value_changed");
3626       return TRUE;
3627     }
3628
3629
3630   if (GTK_SHEET_IN_DRAG (sheet))
3631     {
3632       GtkSheetRange old_range;
3633       draw_xor_rectangle (sheet, sheet->drag_range);
3634       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_DRAG);
3635       gdk_display_pointer_ungrab (display, event->time);
3636
3637       gtk_sheet_real_unselect_range (sheet, NULL);
3638
3639       sheet->active_cell.row = sheet->active_cell.row +
3640         (sheet->drag_range.row0 - sheet->range.row0);
3641       sheet->active_cell.col = sheet->active_cell.col +
3642         (sheet->drag_range.col0 - sheet->range.col0);
3643       sheet->selection_cell.row = sheet->selection_cell.row +
3644         (sheet->drag_range.row0 - sheet->range.row0);
3645       sheet->selection_cell.col = sheet->selection_cell.col +
3646         (sheet->drag_range.col0 - sheet->range.col0);
3647       old_range = sheet->range;
3648       sheet->range = sheet->drag_range;
3649       sheet->drag_range = old_range;
3650       g_signal_emit (sheet, sheet_signals[MOVE_RANGE], 0,
3651                      &sheet->drag_range, &sheet->range);
3652       gtk_sheet_select_range (sheet, &sheet->range);
3653     }
3654
3655   if (GTK_SHEET_IN_RESIZE (sheet))
3656     {
3657       GtkSheetRange old_range;
3658       draw_xor_rectangle (sheet, sheet->drag_range);
3659       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_RESIZE);
3660       gdk_display_pointer_ungrab (display, event->time);
3661
3662       gtk_sheet_real_unselect_range (sheet, NULL);
3663
3664       sheet->active_cell.row = sheet->active_cell.row +
3665         (sheet->drag_range.row0 - sheet->range.row0);
3666       sheet->active_cell.col = sheet->active_cell.col +
3667         (sheet->drag_range.col0 - sheet->range.col0);
3668       if (sheet->drag_range.row0 < sheet->range.row0)
3669         sheet->selection_cell.row = sheet->drag_range.row0;
3670       if (sheet->drag_range.rowi >= sheet->range.rowi)
3671         sheet->selection_cell.row = sheet->drag_range.rowi;
3672       if (sheet->drag_range.col0 < sheet->range.col0)
3673         sheet->selection_cell.col = sheet->drag_range.col0;
3674       if (sheet->drag_range.coli >= sheet->range.coli)
3675         sheet->selection_cell.col = sheet->drag_range.coli;
3676       old_range = sheet->range;
3677       sheet->range = sheet->drag_range;
3678       sheet->drag_range = old_range;
3679
3680       if (sheet->state == GTK_STATE_NORMAL) sheet->state = GTK_SHEET_RANGE_SELECTED;
3681       g_signal_emit (sheet, sheet_signals[RESIZE_RANGE], 0,
3682                      &sheet->drag_range, &sheet->range);
3683       gtk_sheet_select_range (sheet, &sheet->range);
3684     }
3685
3686   if (sheet->state == GTK_SHEET_NORMAL && GTK_SHEET_IN_SELECTION (sheet))
3687     {
3688       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
3689       gdk_display_pointer_ungrab (display, event->time);
3690       change_active_cell (sheet, sheet->active_cell.row,
3691                                sheet->active_cell.col);
3692     }
3693
3694   if (GTK_SHEET_IN_SELECTION)
3695     gdk_display_pointer_ungrab (display, event->time);
3696   gtk_grab_remove (GTK_WIDGET (sheet));
3697
3698   GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
3699
3700   return TRUE;
3701 }
3702
3703 \f
3704
3705
3706
3707 /* Shamelessly lifted from gtktooltips */
3708 static gboolean
3709 gtk_sheet_subtitle_paint_window (GtkWidget *tip_window)
3710 {
3711   GtkRequisition req;
3712
3713   gtk_widget_size_request (tip_window, &req);
3714   gtk_paint_flat_box (tip_window->style, tip_window->window,
3715                       GTK_STATE_NORMAL, GTK_SHADOW_OUT,
3716                       NULL, GTK_WIDGET(tip_window), "tooltip",
3717                       0, 0, req.width, req.height);
3718
3719   return FALSE;
3720 }
3721
3722 static void
3723 destroy_hover_window (GtkSheetHoverTitle *h)
3724 {
3725   gtk_widget_destroy (h->window);
3726   g_free (h);
3727 }
3728
3729 static GtkSheetHoverTitle *
3730 create_hover_window (void)
3731 {
3732   GtkSheetHoverTitle *hw = g_malloc (sizeof (*hw));
3733
3734   hw->window = gtk_window_new (GTK_WINDOW_POPUP);
3735
3736 #if GTK_CHECK_VERSION (2, 9, 0)
3737   gtk_window_set_type_hint (GTK_WINDOW (hw->window),
3738                             GDK_WINDOW_TYPE_HINT_TOOLTIP);
3739 #endif
3740
3741   gtk_widget_set_app_paintable (hw->window, TRUE);
3742   gtk_window_set_resizable (GTK_WINDOW (hw->window), FALSE);
3743   gtk_widget_set_name (hw->window, "gtk-tooltips");
3744   gtk_container_set_border_width (GTK_CONTAINER (hw->window), 4);
3745
3746   g_signal_connect (hw->window,
3747                     "expose_event",
3748                     G_CALLBACK (gtk_sheet_subtitle_paint_window),
3749                     NULL);
3750
3751   hw->label = gtk_label_new (NULL);
3752
3753
3754   gtk_label_set_line_wrap (GTK_LABEL (hw->label), TRUE);
3755   gtk_misc_set_alignment (GTK_MISC (hw->label), 0.5, 0.5);
3756
3757   gtk_container_add (GTK_CONTAINER (hw->window), hw->label);
3758
3759   gtk_widget_show (hw->label);
3760
3761   g_signal_connect (hw->window,
3762                     "destroy",
3763                     G_CALLBACK (gtk_widget_destroyed),
3764                     &hw->window);
3765
3766   return hw;
3767 }
3768
3769 #define HOVER_WINDOW_Y_OFFSET 2
3770
3771 static void
3772 show_subtitle (GtkSheet *sheet, gint row, gint column, const gchar *subtitle)
3773 {
3774   gint x, y;
3775   gint px, py;
3776   gint width;
3777
3778   if ( ! subtitle )
3779     return;
3780
3781   gtk_label_set_text (GTK_LABEL (sheet->hover_window->label),
3782                       subtitle);
3783
3784
3785   sheet->hover_window->row = row;
3786   sheet->hover_window->column = column;
3787
3788   gdk_window_get_origin (GTK_WIDGET (sheet)->window, &x, &y);
3789
3790   gtk_widget_get_pointer (GTK_WIDGET (sheet), &px, &py);
3791
3792   gtk_widget_show (sheet->hover_window->window);
3793
3794   width = GTK_WIDGET (sheet->hover_window->label)->allocation.width;
3795
3796   if (row == -1 )
3797     {
3798       x += px;
3799       x -= width / 2;
3800       y += sheet->column_title_area.y;
3801       y += sheet->column_title_area.height;
3802       y += HOVER_WINDOW_Y_OFFSET;
3803     }
3804
3805   if ( column == -1 )
3806     {
3807       y += py;
3808       x += sheet->row_title_area.x;
3809       x += sheet->row_title_area.width * 2 / 3.0;
3810     }
3811
3812   gtk_window_move (GTK_WINDOW (sheet->hover_window->window),
3813                    x, y);
3814 }
3815
3816 static gboolean
3817 motion_timeout_callback (gpointer data)
3818 {
3819   GtkSheet *sheet = GTK_SHEET (data);
3820   gint x, y;
3821   gint row, column;
3822   gtk_widget_get_pointer (GTK_WIDGET (sheet), &x, &y);
3823
3824   if ( gtk_sheet_get_pixel_info (sheet, x, y, &row, &column) )
3825     {
3826       if (sheet->row_title_under)
3827         {
3828           GSheetRow *row_geo = sheet->row_geometry;
3829           gchar *text;
3830
3831           text = g_sheet_row_get_subtitle (row_geo, row);
3832
3833           show_subtitle (sheet, row, -1, text);
3834           g_free (text);
3835         }
3836
3837       if (sheet->column_title_under)
3838         {
3839           GSheetColumn *col_geo = sheet->column_geometry;
3840           gchar *text;
3841
3842           text = g_sheet_column_get_subtitle (col_geo, column);
3843
3844           show_subtitle (sheet, -1, column, text);
3845
3846           g_free (text);
3847         }
3848     }
3849
3850   return FALSE;
3851 }
3852
3853 static gboolean
3854 gtk_sheet_motion (GtkWidget *widget,  GdkEventMotion *event)
3855 {
3856   GtkSheet *sheet;
3857   GdkModifierType mods;
3858   GdkCursorType new_cursor;
3859   gint x, y;
3860   gint row, column;
3861   GdkDisplay *display;
3862
3863   g_return_val_if_fail (widget != NULL, FALSE);
3864   g_return_val_if_fail (GTK_IS_SHEET (widget), FALSE);
3865   g_return_val_if_fail (event != NULL, FALSE);
3866
3867   sheet = GTK_SHEET (widget);
3868
3869   display = gtk_widget_get_display (widget);
3870
3871   /* selections on the sheet */
3872   x = event->x;
3873   y = event->y;
3874
3875   if (!GTK_WIDGET_VISIBLE (sheet->hover_window->window))
3876     {
3877       if ( sheet->motion_timer > 0 )
3878         g_source_remove (sheet->motion_timer);
3879       sheet->motion_timer =
3880         g_timeout_add (TIMEOUT_HOVER, motion_timeout_callback, sheet);
3881     }
3882   else
3883     {
3884       gint row, column;
3885       gint wx, wy;
3886       gtk_widget_get_pointer (widget, &wx, &wy);
3887
3888       if ( gtk_sheet_get_pixel_info (sheet, wx, wy, &row, &column) )
3889         {
3890           if ( row != sheet->hover_window->row ||
3891                column != sheet->hover_window->column)
3892             {
3893               gtk_widget_hide (sheet->hover_window->window);
3894             }
3895         }
3896     }
3897
3898   if (event->window == sheet->column_title_window &&
3899       gtk_sheet_columns_resizable (sheet))
3900     {
3901       if (!GTK_SHEET_IN_SELECTION (sheet) &&
3902           on_column_boundary (sheet, x, &column))
3903         {
3904           new_cursor = GDK_SB_H_DOUBLE_ARROW;
3905           if (new_cursor != sheet->cursor_drag->type)
3906             {
3907               gdk_cursor_unref (sheet->cursor_drag);
3908               sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_SB_H_DOUBLE_ARROW);
3909               gdk_window_set_cursor (sheet->column_title_window,
3910                                      sheet->cursor_drag);
3911             }
3912         }
3913       else
3914         {
3915           new_cursor = GDK_TOP_LEFT_ARROW;
3916           if (!GTK_SHEET_IN_XDRAG (sheet) &&
3917               new_cursor != sheet->cursor_drag->type)
3918             {
3919               gdk_cursor_unref (sheet->cursor_drag);
3920               sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
3921               gdk_window_set_cursor (sheet->column_title_window,
3922                                      sheet->cursor_drag);
3923             }
3924         }
3925     }
3926
3927   if (event->window == sheet->row_title_window &&
3928       gtk_sheet_rows_resizable (sheet))
3929     {
3930       if (!GTK_SHEET_IN_SELECTION (sheet) && POSSIBLE_YDRAG (sheet, y, &column))
3931         {
3932           new_cursor = GDK_SB_V_DOUBLE_ARROW;
3933           if (new_cursor != sheet->cursor_drag->type)
3934             {
3935               gdk_cursor_unref (sheet->cursor_drag);
3936               sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_SB_V_DOUBLE_ARROW);
3937               gdk_window_set_cursor (sheet->row_title_window, sheet->cursor_drag);
3938             }
3939         }
3940       else
3941         {
3942           new_cursor = GDK_TOP_LEFT_ARROW;
3943           if (!GTK_SHEET_IN_YDRAG (sheet) &&
3944               new_cursor != sheet->cursor_drag->type)
3945             {
3946               gdk_cursor_unref (sheet->cursor_drag);
3947               sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
3948               gdk_window_set_cursor (sheet->row_title_window, sheet->cursor_drag);
3949             }
3950         }
3951     }
3952
3953   new_cursor = GDK_PLUS;
3954   if ( event->window == sheet->sheet_window &&
3955        !POSSIBLE_DRAG (sheet, x, y, &row, &column) &&
3956        !GTK_SHEET_IN_DRAG (sheet) &&
3957        !POSSIBLE_RESIZE (sheet, x, y, &row, &column) &&
3958        !GTK_SHEET_IN_RESIZE (sheet) &&
3959        new_cursor != sheet->cursor_drag->type)
3960     {
3961       gdk_cursor_unref (sheet->cursor_drag);
3962       sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
3963       gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3964     }
3965
3966   new_cursor = GDK_TOP_LEFT_ARROW;
3967   if ( event->window == sheet->sheet_window &&
3968        ! (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
3969           GTK_SHEET_IN_RESIZE (sheet)) &&
3970        (POSSIBLE_DRAG (sheet, x, y, &row, &column) ||
3971         GTK_SHEET_IN_DRAG (sheet)) &&
3972        new_cursor != sheet->cursor_drag->type)
3973     {
3974       gdk_cursor_unref (sheet->cursor_drag);
3975       sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
3976       gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3977     }
3978
3979   new_cursor = GDK_SIZING;
3980   if ( event->window == sheet->sheet_window &&
3981        sheet->selection_mode != GTK_SELECTION_NONE &&
3982        !GTK_SHEET_IN_DRAG (sheet) &&
3983        (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
3984         GTK_SHEET_IN_RESIZE (sheet)) &&
3985        new_cursor != sheet->cursor_drag->type)
3986     {
3987       gdk_cursor_unref (sheet->cursor_drag);
3988       sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_SIZING);
3989       gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3990     }
3991
3992
3993   gdk_window_get_pointer (widget->window, &x, &y, &mods);
3994   if (! (mods & GDK_BUTTON1_MASK)) return FALSE;
3995
3996   if (GTK_SHEET_IN_XDRAG (sheet))
3997     {
3998       x = event->x;
3999
4000       new_column_width (sheet, sheet->drag_cell.col, &x);
4001 #if 0
4002       if (x != sheet->x_drag)
4003         {
4004           draw_xor_vline (sheet);
4005           sheet->x_drag = x;
4006           draw_xor_vline (sheet);
4007         }
4008 #endif
4009       return TRUE;
4010     }
4011
4012   if (GTK_SHEET_IN_YDRAG (sheet))
4013     {
4014       if (event->is_hint || event->window != widget->window)
4015         gtk_widget_get_pointer (widget, NULL, &y);
4016       else
4017         y = event->y;
4018
4019       new_row_height (sheet, sheet->drag_cell.row, &y);
4020       if (y != sheet->y_drag)
4021         {
4022           draw_xor_hline (sheet);
4023           sheet->y_drag = y;
4024           draw_xor_hline (sheet);
4025         }
4026       return TRUE;
4027     }
4028
4029   if (GTK_SHEET_IN_DRAG (sheet))
4030     {
4031       GtkSheetRange aux;
4032       column = column_from_xpixel (sheet, x)- sheet->drag_cell.col;
4033       row = yyy_row_ypixel_to_row (sheet, y) - sheet->drag_cell.row;
4034       if (sheet->state == GTK_SHEET_COLUMN_SELECTED) row = 0;
4035       if (sheet->state == GTK_SHEET_ROW_SELECTED) column = 0;
4036       sheet->x_drag = x;
4037       sheet->y_drag = y;
4038       aux = sheet->range;
4039       if (aux.row0 + row >= 0 && aux.rowi + row < g_sheet_row_get_row_count (sheet->row_geometry) &&
4040           aux.col0 + column >= 0 && aux.coli + column < g_sheet_column_get_column_count (sheet->column_geometry))
4041         {
4042           aux = sheet->drag_range;
4043           sheet->drag_range.row0 = sheet->range.row0 + row;
4044           sheet->drag_range.col0 = sheet->range.col0 + column;
4045           sheet->drag_range.rowi = sheet->range.rowi + row;
4046           sheet->drag_range.coli = sheet->range.coli + column;
4047           if (aux.row0 != sheet->drag_range.row0 ||
4048               aux.col0 != sheet->drag_range.col0)
4049             {
4050               draw_xor_rectangle (sheet, aux);
4051               draw_xor_rectangle (sheet, sheet->drag_range);
4052             }
4053         }
4054       return TRUE;
4055     }
4056
4057   if (GTK_SHEET_IN_RESIZE (sheet))
4058     {
4059       GtkSheetRange aux;
4060       gint v_h, current_col, current_row, col_threshold, row_threshold;
4061       v_h = 1;
4062       if (abs (x - g_sheet_column_start_pixel (sheet->column_geometry, sheet->drag_cell.col)) >
4063           abs (y - g_sheet_row_start_pixel (sheet->row_geometry, sheet->drag_cell.row))) v_h = 2;
4064
4065       current_col = column_from_xpixel (sheet, x);
4066       current_row = yyy_row_ypixel_to_row (sheet, y);
4067       column = current_col - sheet->drag_cell.col;
4068       row = current_row - sheet->drag_cell.row;
4069
4070       /*use half of column width resp. row height as threshold to
4071         expand selection*/
4072       col_threshold = g_sheet_column_start_pixel (sheet->column_geometry, current_col) +
4073         g_sheet_column_get_width (sheet->column_geometry, current_col) / 2;
4074       if (column > 0)
4075         {
4076           if (x < col_threshold)
4077             column -= 1;
4078         }
4079       else if (column < 0)
4080         {
4081           if (x > col_threshold)
4082             column +=1;
4083         }
4084       row_threshold = g_sheet_row_start_pixel (sheet->row_geometry, current_row) +
4085         g_sheet_row_get_height (sheet->row_geometry, current_row)/2;
4086       if (row > 0)
4087         {
4088           if (y < row_threshold)
4089             row -= 1;
4090         }
4091       else if (row < 0)
4092         {
4093           if (y > row_threshold)
4094             row +=1;
4095         }
4096
4097       if (sheet->state == GTK_SHEET_COLUMN_SELECTED) row = 0;
4098       if (sheet->state == GTK_SHEET_ROW_SELECTED) column = 0;
4099       sheet->x_drag = x;
4100       sheet->y_drag = y;
4101       aux = sheet->range;
4102
4103       if (v_h == 1)
4104         column = 0;
4105       else
4106         row = 0;
4107
4108       if (aux.row0 + row >= 0 && aux.rowi + row < g_sheet_row_get_row_count (sheet->row_geometry) &&
4109           aux.col0 + column >= 0 && aux.coli + column < g_sheet_column_get_column_count (sheet->column_geometry))
4110         {
4111           aux = sheet->drag_range;
4112           sheet->drag_range = sheet->range;
4113
4114           if (row < 0) sheet->drag_range.row0 = sheet->range.row0 + row;
4115           if (row > 0) sheet->drag_range.rowi = sheet->range.rowi + row;
4116           if (column < 0) sheet->drag_range.col0 = sheet->range.col0 + column;
4117           if (column > 0) sheet->drag_range.coli = sheet->range.coli + column;
4118
4119           if (aux.row0 != sheet->drag_range.row0 ||
4120               aux.rowi != sheet->drag_range.rowi ||
4121               aux.col0 != sheet->drag_range.col0 ||
4122               aux.coli != sheet->drag_range.coli)
4123             {
4124               draw_xor_rectangle (sheet, aux);
4125               draw_xor_rectangle (sheet, sheet->drag_range);
4126             }
4127         }
4128       return TRUE;
4129     }
4130
4131   gtk_sheet_get_pixel_info (sheet, x, y, &row, &column);
4132
4133   if (sheet->state == GTK_SHEET_NORMAL && row == sheet->active_cell.row &&
4134       column == sheet->active_cell.col) return TRUE;
4135
4136   if (GTK_SHEET_IN_SELECTION (sheet) && mods&GDK_BUTTON1_MASK)
4137     gtk_sheet_extend_selection (sheet, row, column);
4138
4139   return TRUE;
4140 }
4141
4142 static gboolean
4143 gtk_sheet_crossing_notify (GtkWidget *widget,
4144                            GdkEventCrossing *event)
4145 {
4146   GtkSheet *sheet = GTK_SHEET (widget);
4147
4148   if (event->window == sheet->column_title_window)
4149     sheet->column_title_under = event->type == GDK_ENTER_NOTIFY;
4150   else if (event->window == sheet->row_title_window)
4151     sheet->row_title_under = event->type == GDK_ENTER_NOTIFY;
4152
4153   return TRUE;
4154 }
4155
4156 static void
4157 gtk_sheet_extend_selection (GtkSheet *sheet, gint row, gint column)
4158 {
4159   GtkSheetRange range;
4160   gint state;
4161   gint r, c;
4162
4163   if (row == sheet->selection_cell.row && column == sheet->selection_cell.col)
4164     return;
4165
4166   if (sheet->selection_mode == GTK_SELECTION_SINGLE) return;
4167
4168   gtk_widget_grab_focus (GTK_WIDGET (sheet));
4169
4170   if (GTK_SHEET_IN_DRAG (sheet)) return;
4171
4172   state = sheet->state;
4173
4174   switch (sheet->state)
4175     {
4176     case GTK_SHEET_ROW_SELECTED:
4177       column = g_sheet_column_get_column_count (sheet->column_geometry) - 1;
4178       break;
4179     case GTK_SHEET_COLUMN_SELECTED:
4180       row = g_sheet_row_get_row_count (sheet->row_geometry) - 1;
4181       break;
4182     case GTK_SHEET_NORMAL:
4183       sheet->state = GTK_SHEET_RANGE_SELECTED;
4184       r = sheet->active_cell.row;
4185       c = sheet->active_cell.col;
4186       sheet->range.col0 = c;
4187       sheet->range.row0 = r;
4188       sheet->range.coli = c;
4189       sheet->range.rowi = r;
4190       gtk_sheet_range_draw_selection (sheet, sheet->range);
4191     case GTK_SHEET_RANGE_SELECTED:
4192       sheet->state = GTK_SHEET_RANGE_SELECTED;
4193     }
4194
4195   sheet->selection_cell.row = row;
4196   sheet->selection_cell.col = column;
4197
4198   range.col0 = MIN (column, sheet->active_cell.col);
4199   range.coli = MAX (column, sheet->active_cell.col);
4200   range.row0 = MIN (row, sheet->active_cell.row);
4201   range.rowi = MAX (row, sheet->active_cell.row);
4202
4203   if (range.row0 != sheet->range.row0 || range.rowi != sheet->range.rowi ||
4204       range.col0 != sheet->range.col0 || range.coli != sheet->range.coli ||
4205       state == GTK_SHEET_NORMAL)
4206     gtk_sheet_real_select_range (sheet, &range);
4207
4208 }
4209
4210 static gint
4211 gtk_sheet_entry_key_press (GtkWidget *widget,
4212                            GdkEventKey *key)
4213 {
4214   gboolean focus;
4215   g_signal_emit_by_name (widget, "key_press_event", key, &focus);
4216   return focus;
4217 }
4218
4219
4220 /* Number of rows in a step-increment */
4221 #define ROWS_PER_STEP 1
4222
4223
4224 static void
4225 page_vertical (GtkSheet *sheet, GtkScrollType dir)
4226 {
4227   gint old_row = sheet->active_cell.row ;
4228   glong vpixel = g_sheet_row_start_pixel (sheet->row_geometry, old_row);
4229
4230   gint new_row;
4231
4232   vpixel -= g_sheet_row_start_pixel (sheet->row_geometry,
4233                                      min_visible_row (sheet));
4234
4235   switch ( dir)
4236     {
4237     case GTK_SCROLL_PAGE_DOWN:
4238       gtk_adjustment_set_value (sheet->vadjustment,
4239                                 sheet->vadjustment->value +
4240                                 sheet->vadjustment->page_increment);
4241       break;
4242     case GTK_SCROLL_PAGE_UP:
4243       gtk_adjustment_set_value (sheet->vadjustment,
4244                                 sheet->vadjustment->value -
4245                                 sheet->vadjustment->page_increment);
4246
4247       break;
4248     default:
4249       g_assert_not_reached ();
4250       break;
4251     }
4252
4253
4254   vpixel += g_sheet_row_start_pixel (sheet->row_geometry,
4255                                      min_visible_row (sheet));
4256
4257   new_row =  yyy_row_ypixel_to_row (sheet, vpixel);
4258
4259   change_active_cell (sheet, new_row,
4260                            sheet->active_cell.col);
4261 }
4262
4263
4264 static void
4265 step_sheet (GtkSheet *sheet, GtkScrollType dir)
4266 {
4267   gint current_row = sheet->active_cell.row;
4268   gint current_col = sheet->active_cell.col;
4269   gint new_row = current_row;
4270   gint new_col = current_col;
4271   gboolean forbidden = FALSE;
4272
4273   switch ( dir)
4274     {
4275     case GTK_SCROLL_STEP_DOWN:
4276       new_row++;
4277       break;
4278     case GTK_SCROLL_STEP_UP:
4279       new_row--;
4280       break;
4281     case GTK_SCROLL_STEP_RIGHT:
4282       new_col++;
4283       break;
4284     case GTK_SCROLL_STEP_LEFT:
4285       new_col--;
4286       break;
4287     default:
4288       g_assert_not_reached ();
4289       break;
4290     }
4291
4292
4293   maximize_int (&new_row, 0);
4294   maximize_int (&new_col, 0);
4295
4296   minimize_int (&new_row,
4297                 g_sheet_row_get_row_count (sheet->row_geometry) - 1);
4298
4299   minimize_int (&new_col,
4300                 g_sheet_column_get_column_count (sheet->column_geometry) - 1);
4301
4302   g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
4303                  current_row, current_col,
4304                  &new_row, &new_col, &forbidden);
4305
4306   if (forbidden)
4307     return;
4308
4309   change_active_cell (sheet, new_row, new_col);
4310
4311   if ( new_col > max_fully_visible_column (sheet))
4312     {
4313       glong hpos  =
4314         g_sheet_column_start_pixel (sheet->column_geometry,
4315                                     new_col + 1);
4316       hpos -= sheet->hadjustment->page_size;
4317
4318       gtk_adjustment_set_value (sheet->hadjustment,
4319                                 hpos);
4320     }
4321   else if ( new_col < min_fully_visible_column (sheet))
4322     {
4323       glong hpos  =
4324         g_sheet_column_start_pixel (sheet->column_geometry,
4325                                     new_col);
4326
4327       gtk_adjustment_set_value (sheet->hadjustment,
4328                                 hpos);
4329     }
4330
4331
4332   if ( new_row > max_fully_visible_row (sheet))
4333     {
4334       glong vpos  =
4335         g_sheet_row_start_pixel (sheet->row_geometry,
4336                                     new_row + 1);
4337       vpos -= sheet->vadjustment->page_size;
4338
4339       gtk_adjustment_set_value (sheet->vadjustment,
4340                                 vpos);
4341     }
4342   else if ( new_row < min_fully_visible_row (sheet))
4343     {
4344       glong vpos  =
4345         g_sheet_row_start_pixel (sheet->row_geometry,
4346                                     new_row);
4347
4348       gtk_adjustment_set_value (sheet->vadjustment,
4349                                 vpos);
4350     }
4351 }
4352
4353
4354 static gboolean
4355 gtk_sheet_key_press (GtkWidget *widget,
4356                      GdkEventKey *key)
4357 {
4358   GtkSheet *sheet = GTK_SHEET (widget);
4359
4360   GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
4361
4362   switch (key->keyval)
4363     {
4364     case GDK_Tab:
4365     case GDK_Right:
4366       step_sheet (sheet, GTK_SCROLL_STEP_RIGHT);
4367       break;
4368     case GDK_ISO_Left_Tab:
4369     case GDK_Left:
4370       step_sheet (sheet, GTK_SCROLL_STEP_LEFT);
4371       break;
4372     case GDK_Return:
4373     case GDK_Down:
4374       step_sheet (sheet, GTK_SCROLL_STEP_DOWN);
4375       break;
4376     case GDK_Up:
4377       step_sheet (sheet, GTK_SCROLL_STEP_UP);
4378       break;
4379
4380     case GDK_Page_Down:
4381       page_vertical (sheet, GTK_SCROLL_PAGE_DOWN);
4382       break;
4383     case GDK_Page_Up:
4384       page_vertical (sheet, GTK_SCROLL_PAGE_UP);
4385       break;
4386
4387     case GDK_Home:
4388       gtk_adjustment_set_value (sheet->vadjustment,
4389                                 sheet->vadjustment->lower);
4390
4391       change_active_cell (sheet,  0,
4392                                sheet->active_cell.col);
4393
4394       break;
4395
4396     case GDK_End:
4397       gtk_adjustment_set_value (sheet->vadjustment,
4398                                 sheet->vadjustment->upper -
4399                                 sheet->vadjustment->page_size -
4400                                 sheet->vadjustment->page_increment);
4401
4402       /*
4403         change_active_cellx (sheet,
4404         g_sheet_row_get_row_count (sheet->row_geometry) - 1,
4405         sheet->active_cell.col);
4406       */
4407       break;
4408     case GDK_Delete:
4409       gtk_sheet_real_cell_clear (sheet, sheet->active_cell.row, sheet->active_cell.col);
4410       break;
4411     default:
4412       return FALSE;
4413       break;
4414     }
4415
4416   return TRUE;
4417 }
4418
4419 static void
4420 gtk_sheet_size_request (GtkWidget *widget,
4421                         GtkRequisition *requisition)
4422 {
4423   GtkSheet *sheet;
4424
4425   g_return_if_fail (widget != NULL);
4426   g_return_if_fail (GTK_IS_SHEET (widget));
4427   g_return_if_fail (requisition != NULL);
4428
4429   sheet = GTK_SHEET (widget);
4430
4431   requisition->width = 3 * DEFAULT_COLUMN_WIDTH;
4432   requisition->height = 3 * DEFAULT_ROW_HEIGHT;
4433
4434   /* compute the size of the column title area */
4435   if (sheet->column_titles_visible)
4436     requisition->height += sheet->column_title_area.height;
4437
4438   /* compute the size of the row title area */
4439   if (sheet->row_titles_visible)
4440     requisition->width += sheet->row_title_area.width;
4441 }
4442
4443
4444 static void
4445 gtk_sheet_size_allocate (GtkWidget *widget,
4446                          GtkAllocation *allocation)
4447 {
4448   GtkSheet *sheet;
4449   GtkAllocation sheet_allocation;
4450   gint border_width;
4451
4452   g_return_if_fail (widget != NULL);
4453   g_return_if_fail (GTK_IS_SHEET (widget));
4454   g_return_if_fail (allocation != NULL);
4455
4456   sheet = GTK_SHEET (widget);
4457   widget->allocation = *allocation;
4458   border_width = GTK_CONTAINER (widget)->border_width;
4459
4460   if (GTK_WIDGET_REALIZED (widget))
4461     gdk_window_move_resize (widget->window,
4462                             allocation->x + border_width,
4463                             allocation->y + border_width,
4464                             allocation->width - 2 * border_width,
4465                             allocation->height - 2 * border_width);
4466
4467   /* use internal allocation structure for all the math
4468    * because it's easier than always subtracting the container
4469    * border width */
4470   sheet->internal_allocation.x = 0;
4471   sheet->internal_allocation.y = 0;
4472   sheet->internal_allocation.width = allocation->width - 2 * border_width;
4473   sheet->internal_allocation.height = allocation->height - 2 * border_width;
4474
4475   sheet_allocation.x = 0;
4476   sheet_allocation.y = 0;
4477   sheet_allocation.width = allocation->width - 2 * border_width;
4478   sheet_allocation.height = allocation->height - 2 * border_width;
4479
4480   if (GTK_WIDGET_REALIZED (widget))
4481     gdk_window_move_resize (sheet->sheet_window,
4482                             sheet_allocation.x,
4483                             sheet_allocation.y,
4484                             sheet_allocation.width,
4485                             sheet_allocation.height);
4486
4487   /* position the window which holds the column title buttons */
4488   sheet->column_title_area.x = 0;
4489   sheet->column_title_area.y = 0;
4490
4491   if (sheet->row_titles_visible)
4492     {
4493       sheet->column_title_area.x = sheet->row_title_area.width;
4494     }
4495
4496   sheet->column_title_area.width = sheet_allocation.width ;
4497
4498
4499   if (GTK_WIDGET_REALIZED (widget) && sheet->column_titles_visible)
4500     gdk_window_move_resize (sheet->column_title_window,
4501                             sheet->column_title_area.x,
4502                             sheet->column_title_area.y,
4503                             sheet->column_title_area.width,
4504                             sheet->column_title_area.height);
4505
4506
4507   /* column button allocation */
4508   draw_column_title_buttons (sheet);
4509
4510   /* position the window which holds the row title buttons */
4511   sheet->row_title_area.x = 0;
4512   sheet->row_title_area.y = 0;
4513   if (sheet->column_titles_visible)
4514     {
4515       sheet->row_title_area.y = sheet->column_title_area.height;
4516     }
4517
4518   sheet->row_title_area.height = sheet_allocation.height -
4519     sheet->row_title_area.y;
4520
4521   if (GTK_WIDGET_REALIZED (widget) && sheet->row_titles_visible)
4522     gdk_window_move_resize (sheet->row_title_window,
4523                             sheet->row_title_area.x,
4524                             sheet->row_title_area.y,
4525                             sheet->row_title_area.width,
4526                             sheet->row_title_area.height);
4527
4528
4529   /* row button allocation */
4530   draw_row_title_buttons (sheet);
4531   draw_column_title_buttons (sheet);
4532
4533   /* set the scrollbars adjustments */
4534   adjust_scrollbars (sheet);
4535 }
4536
4537 static void
4538 draw_column_title_buttons (GtkSheet *sheet)
4539 {
4540   gint i;
4541   gint x, width;
4542
4543   if (!sheet->column_titles_visible) return;
4544   if (!GTK_WIDGET_REALIZED (sheet))
4545     return;
4546
4547   gdk_drawable_get_size (sheet->sheet_window, &width, NULL);
4548   x = 0;
4549
4550   if (sheet->row_titles_visible)
4551     {
4552       x = sheet->row_title_area.width;
4553     }
4554
4555   if (sheet->column_title_area.width != width || sheet->column_title_area.x != x)
4556     {
4557       sheet->column_title_area.width = width;
4558       sheet->column_title_area.x = x;
4559       gdk_window_move_resize (sheet->column_title_window,
4560                               sheet->column_title_area.x,
4561                               sheet->column_title_area.y,
4562                               sheet->column_title_area.width,
4563                               sheet->column_title_area.height);
4564     }
4565
4566   if (max_visible_column (sheet) ==
4567       g_sheet_column_get_column_count (sheet->column_geometry) - 1)
4568     gdk_window_clear_area (sheet->column_title_window,
4569                            0, 0,
4570                            sheet->column_title_area.width,
4571                            sheet->column_title_area.height);
4572
4573   if (!GTK_WIDGET_DRAWABLE (sheet)) return;
4574
4575   size_allocate_global_button (sheet);
4576
4577   for (i = min_visible_column (sheet); i <= max_visible_column (sheet); i++)
4578     gtk_sheet_column_title_button_draw (sheet, i);
4579 }
4580
4581 static void
4582 draw_row_title_buttons (GtkSheet *sheet)
4583 {
4584   gint i;
4585   gint y = 0;
4586   gint height;
4587
4588   if (!sheet->row_titles_visible) return;
4589   if (!GTK_WIDGET_REALIZED (sheet))
4590     return;
4591
4592   gdk_drawable_get_size (sheet->sheet_window, NULL, &height);
4593
4594   if (sheet->column_titles_visible)
4595     {
4596       y = sheet->column_title_area.height;
4597     }
4598
4599   if (sheet->row_title_area.height != height || sheet->row_title_area.y != y)
4600     {
4601       sheet->row_title_area.y = y;
4602       sheet->row_title_area.height = height;
4603       gdk_window_move_resize (sheet->row_title_window,
4604                               sheet->row_title_area.x,
4605                               sheet->row_title_area.y,
4606                               sheet->row_title_area.width,
4607                               sheet->row_title_area.height);
4608     }
4609
4610   if (max_visible_row (sheet) == g_sheet_row_get_row_count (sheet->row_geometry) - 1)
4611     gdk_window_clear_area (sheet->row_title_window,
4612                            0, 0,
4613                            sheet->row_title_area.width,
4614                            sheet->row_title_area.height);
4615
4616   if (!GTK_WIDGET_DRAWABLE (sheet)) return;
4617
4618   size_allocate_global_button (sheet);
4619
4620   for (i = min_visible_row (sheet); i <= max_visible_row (sheet); i++)
4621     {
4622       if ( i >= g_sheet_row_get_row_count (sheet->row_geometry))
4623         break;
4624       gtk_sheet_row_title_button_draw (sheet, i);
4625     }
4626 }
4627
4628
4629 static void
4630 gtk_sheet_size_allocate_entry (GtkSheet *sheet)
4631 {
4632   GtkAllocation entry_alloc;
4633   GtkSheetCellAttr attributes = { 0 };
4634   GtkEntry *sheet_entry;
4635
4636
4637   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4638   if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
4639
4640   sheet_entry = GTK_ENTRY (gtk_sheet_get_entry (sheet));
4641
4642   if ( ! gtk_sheet_get_attributes (sheet, sheet->active_cell.row,
4643                                    sheet->active_cell.col,
4644                                    &attributes) )
4645     return ;
4646
4647   gtk_widget_ensure_style (GTK_WIDGET (sheet_entry));
4648
4649   if ( GTK_WIDGET_REALIZED (sheet->entry_widget) )
4650     {
4651       GtkStyle *style = GTK_WIDGET (sheet_entry)->style;
4652
4653       style->bg[GTK_STATE_NORMAL] = attributes.background;
4654       style->fg[GTK_STATE_NORMAL] = attributes.foreground;
4655       style->text[GTK_STATE_NORMAL] = attributes.foreground;
4656       style->bg[GTK_STATE_ACTIVE] = attributes.background;
4657       style->fg[GTK_STATE_ACTIVE] = attributes.foreground;
4658       style->text[GTK_STATE_ACTIVE] = attributes.foreground;
4659
4660       pango_font_description_free (style->font_desc);
4661       g_assert (attributes.font_desc);
4662       style->font_desc = pango_font_description_copy (attributes.font_desc);
4663     }
4664
4665   rectangle_from_cell (sheet, sheet->active_cell.row,
4666                        sheet->active_cell.col, &entry_alloc);
4667
4668   gtk_widget_set_size_request (sheet->entry_widget, entry_alloc.width,
4669                                entry_alloc.height);
4670   gtk_widget_size_allocate (sheet->entry_widget, &entry_alloc);
4671 }
4672
4673
4674 static void
4675 create_sheet_entry (GtkSheet *sheet)
4676 {
4677   if (sheet->entry_widget)
4678     {
4679       gtk_widget_unparent (sheet->entry_widget);
4680     }
4681
4682   if (sheet->entry_type)
4683     {
4684       sheet->entry_container = g_object_new (sheet->entry_type, NULL);
4685       g_object_ref_sink (sheet->entry_container);
4686       sheet->entry_widget = gtk_sheet_get_entry (sheet);
4687
4688       if  ( NULL == sheet->entry_widget)
4689         {
4690           g_warning ("Entry type is %s. It must be GtkEntry subclass, or a widget containing one. "
4691                      "Using default", g_type_name (sheet->entry_type));
4692           g_object_unref (sheet->entry_container);
4693           sheet->entry_widget = sheet->entry_container = gtk_entry_new ();
4694         }
4695       else
4696         {
4697           sheet->entry_widget = sheet->entry_container ;
4698         }
4699     }
4700   else
4701     {
4702       sheet->entry_widget = sheet->entry_container = gtk_entry_new ();
4703       g_object_ref_sink (sheet->entry_container);
4704     }
4705
4706   gtk_widget_size_request (sheet->entry_widget, NULL);
4707
4708   if (GTK_WIDGET_REALIZED (sheet))
4709     {
4710       gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
4711       gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
4712       gtk_widget_realize (sheet->entry_widget);
4713     }
4714
4715   g_signal_connect_swapped (sheet->entry_widget, "key_press_event",
4716                             G_CALLBACK (gtk_sheet_entry_key_press),
4717                             sheet);
4718
4719   sheet->entry_handler_id =
4720     g_signal_connect (sheet->entry_widget,
4721                       "changed",
4722                       G_CALLBACK (gtk_sheet_entry_changed),
4723                       sheet);
4724
4725   gtk_widget_show (sheet->entry_widget);
4726 }
4727
4728
4729 /* Finds the last child widget that happens to be of type GtkEntry */
4730 static void
4731 find_entry (GtkWidget *w, gpointer user_data)
4732 {
4733   GtkWidget **entry = user_data;
4734   if ( GTK_IS_ENTRY (w))
4735     {
4736       *entry = w;
4737     }
4738 }
4739
4740 GtkWidget *
4741 gtk_sheet_get_entry (GtkSheet *sheet)
4742 {
4743   GtkWidget *parent;
4744   GtkWidget *entry = NULL;
4745
4746   g_return_val_if_fail (sheet != NULL, NULL);
4747   g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);
4748   g_return_val_if_fail (sheet->entry_widget != NULL, NULL);
4749
4750   if (GTK_IS_ENTRY (sheet->entry_container))
4751     return (sheet->entry_container);
4752
4753   parent = sheet->entry_container;
4754
4755   if (GTK_IS_CONTAINER (parent))
4756     {
4757       gtk_container_forall (GTK_CONTAINER (parent), find_entry, &entry);
4758
4759       if (GTK_IS_ENTRY (entry))
4760         return entry;
4761     }
4762
4763   if (!GTK_IS_ENTRY (entry)) return NULL;
4764
4765   return (entry);
4766
4767 }
4768
4769 GtkWidget *
4770 gtk_sheet_get_entry_widget (GtkSheet *sheet)
4771 {
4772   g_return_val_if_fail (sheet != NULL, NULL);
4773   g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);
4774   g_return_val_if_fail (sheet->entry_widget != NULL, NULL);
4775
4776   return (sheet->entry_widget);
4777 }
4778
4779
4780 static void
4781 gtk_sheet_button_draw (GtkSheet *sheet, GdkWindow *window,
4782                        GtkSheetButton *button, gboolean is_sensitive,
4783                        GdkRectangle allocation)
4784 {
4785   GtkShadowType shadow_type;
4786   gint text_width = 0, text_height = 0;
4787   PangoAlignment align = PANGO_ALIGN_LEFT;
4788
4789   gboolean rtl ;
4790
4791   gint state = 0;
4792   gint len = 0;
4793   gchar *line = 0;
4794
4795   g_return_if_fail (sheet != NULL);
4796   g_return_if_fail (button != NULL);
4797
4798
4799   rtl = gtk_widget_get_direction (GTK_WIDGET (sheet)) == GTK_TEXT_DIR_RTL;
4800
4801   gdk_window_clear_area (window,
4802                          allocation.x, allocation.y,
4803                          allocation.width, allocation.height);
4804
4805   gtk_paint_box (sheet->button->style, window,
4806                  GTK_STATE_NORMAL, GTK_SHADOW_OUT,
4807                  &allocation, GTK_WIDGET (sheet->button),
4808                  "buttondefault",
4809                  allocation.x, allocation.y,
4810                  allocation.width, allocation.height);
4811
4812   state = button->state;
4813   if (!is_sensitive) state = GTK_STATE_INSENSITIVE;
4814
4815   if (state == GTK_STATE_ACTIVE)
4816     shadow_type = GTK_SHADOW_IN;
4817   else
4818     shadow_type = GTK_SHADOW_OUT;
4819
4820   if (state != GTK_STATE_NORMAL && state != GTK_STATE_INSENSITIVE)
4821     gtk_paint_box (sheet->button->style, window,
4822                    button->state, shadow_type,
4823                    &allocation, GTK_WIDGET (sheet->button),
4824                    "button",
4825                    allocation.x, allocation.y,
4826                    allocation.width, allocation.height);
4827
4828   if (button->label_visible)
4829     {
4830
4831       text_height = DEFAULT_ROW_HEIGHT - 
4832         2 * COLUMN_TITLES_HEIGHT;
4833
4834       gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4835                                  &allocation);
4836       gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc,
4837                                  &allocation);
4838
4839       allocation.y += 2 * sheet->button->style->ythickness;
4840
4841
4842       if (button->label && strlen (button->label)>0)
4843         {
4844           gchar *words = 0;
4845           PangoLayout *layout = NULL;
4846           gint real_x = allocation.x, real_y = allocation.y;
4847
4848           words = button->label;
4849           line = g_new (gchar, 1);
4850           line[0]='\0';
4851
4852           while (words && *words != '\0')
4853             {
4854               if (*words != '\n')
4855                 {
4856                   len = strlen (line);
4857                   line = g_realloc (line, len + 2);
4858                   line[len]=*words;
4859                   line[len + 1]='\0';
4860                 }
4861               if (*words == '\n' || * (words + 1) == '\0')
4862                 {
4863                   text_width = STRING_WIDTH (GTK_WIDGET (sheet), GTK_WIDGET (sheet)->style->font_desc, line);
4864
4865                   layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), line);
4866                   switch (button->justification)
4867                     {
4868                     case GTK_JUSTIFY_LEFT:
4869                       real_x = allocation.x + COLUMN_TITLES_HEIGHT;
4870                       align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4871                       break;
4872                     case GTK_JUSTIFY_RIGHT:
4873                       real_x = allocation.x + allocation.width - text_width - COLUMN_TITLES_HEIGHT;
4874                       align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
4875                       break;
4876                     case GTK_JUSTIFY_CENTER:
4877                     default:
4878                       real_x = allocation.x + (allocation.width - text_width)/2;
4879                       align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4880                       pango_layout_set_justify (layout, TRUE);
4881                     }
4882                   pango_layout_set_alignment (layout, align);
4883                   gtk_paint_layout (GTK_WIDGET (sheet)->style,
4884                                     window,
4885                                     state,
4886                                     FALSE,
4887                                     &allocation,
4888                                     GTK_WIDGET (sheet),
4889                                     "label",
4890                                     real_x, real_y,
4891                                     layout);
4892                   g_object_unref (layout);
4893
4894                   real_y += text_height + 2;
4895
4896                   g_free (line);
4897                   line = g_new (gchar, 1);
4898                   line[0]='\0';
4899                 }
4900               words++;
4901             }
4902           g_free (line);
4903         }
4904
4905       gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4906                                  NULL);
4907       gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc, NULL);
4908
4909     }
4910
4911   gtk_sheet_button_free (button);
4912 }
4913
4914 static void
4915 gtk_sheet_column_title_button_draw (GtkSheet *sheet, gint column)
4916 {
4917   GdkRectangle allocation;
4918   GtkSheetButton *button = NULL;
4919   gboolean is_sensitive = FALSE;
4920
4921   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4922
4923   if (!sheet->column_titles_visible) return;
4924
4925   if (column < min_visible_column (sheet)) return;
4926   if (column > max_visible_column (sheet)) return;
4927
4928   button = g_sheet_column_get_button (sheet->column_geometry, column);
4929   allocation.y = 0;
4930   allocation.x = g_sheet_column_start_pixel (sheet->column_geometry, column) + CELL_SPACING;
4931   allocation.x -= sheet->hadjustment->value;
4932
4933   allocation.height = sheet->column_title_area.height;
4934   allocation.width = g_sheet_column_get_width (sheet->column_geometry, column);
4935   is_sensitive = g_sheet_column_get_sensitivity (sheet->column_geometry, column);
4936
4937   gtk_sheet_button_draw (sheet, sheet->column_title_window,
4938                          button, is_sensitive, allocation);
4939 }
4940
4941
4942 static void
4943 gtk_sheet_row_title_button_draw (GtkSheet *sheet, gint row)
4944 {
4945   GdkRectangle allocation;
4946   GtkSheetButton *button = NULL;
4947   gboolean is_sensitive = FALSE;
4948
4949
4950   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4951
4952   if (!sheet->row_titles_visible) return;
4953
4954   if (row < min_visible_row (sheet)) return;
4955   if (row > max_visible_row (sheet)) return;
4956
4957   button = g_sheet_row_get_button (sheet->row_geometry, row);
4958   allocation.x = 0;
4959   allocation.y = g_sheet_row_start_pixel (sheet->row_geometry, row) + CELL_SPACING;
4960   allocation.y -= sheet->vadjustment->value;
4961
4962   allocation.width = sheet->row_title_area.width;
4963   allocation.height = g_sheet_row_get_height (sheet->row_geometry, row);
4964   is_sensitive = g_sheet_row_get_sensitivity (sheet->row_geometry, row);
4965
4966   gtk_sheet_button_draw (sheet, sheet->row_title_window,
4967                          button, is_sensitive, allocation);
4968 }
4969
4970 /* SCROLLBARS
4971  *
4972  * functions:
4973  * adjust_scrollbars
4974  * vadjustment_value_changed
4975  * hadjustment_value_changed */
4976
4977 static void
4978 adjust_scrollbars (GtkSheet *sheet)
4979 {
4980   gint width, height;
4981
4982   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4983     return;
4984
4985   gdk_drawable_get_size (sheet->sheet_window, &width, &height);
4986
4987   if ( sheet->row_titles_visible)
4988     width -= sheet->row_title_area.width;
4989
4990   if (sheet->column_titles_visible)
4991     height -= sheet->column_title_area.height;
4992
4993   if (sheet->vadjustment)
4994     {
4995       glong last_row = g_sheet_row_get_row_count (sheet->row_geometry) - 1;
4996
4997       sheet->vadjustment->step_increment =
4998         ROWS_PER_STEP *
4999         g_sheet_row_get_height (sheet->row_geometry, last_row);
5000
5001       sheet->vadjustment->page_increment =
5002         height -
5003         sheet->column_title_area.height -
5004         g_sheet_row_get_height (sheet->row_geometry, last_row);
5005
5006
5007
5008       sheet->vadjustment->upper =
5009         g_sheet_row_start_pixel (sheet->row_geometry, last_row)
5010         +
5011         g_sheet_row_get_height (sheet->row_geometry, last_row)
5012         ;
5013
5014       sheet->vadjustment->lower = 0;
5015       sheet->vadjustment->page_size = height;
5016
5017       g_signal_emit_by_name (sheet->vadjustment, "changed");
5018     }
5019
5020   if (sheet->hadjustment)
5021     {
5022       gint last_col;
5023       sheet->hadjustment->step_increment = 1;
5024
5025       sheet->hadjustment->page_increment = width;
5026
5027       last_col = g_sheet_column_get_column_count (sheet->column_geometry) - 1;
5028
5029       sheet->hadjustment->upper =
5030         g_sheet_column_start_pixel (sheet->column_geometry, last_col)
5031         +
5032         g_sheet_column_get_width (sheet->column_geometry, last_col)
5033         ;
5034
5035       sheet->hadjustment->lower = 0;
5036       sheet->hadjustment->page_size = width;
5037
5038       g_signal_emit_by_name (sheet->hadjustment, "changed");
5039     }
5040 }
5041
5042 static void
5043 vadjustment_value_changed (GtkAdjustment *adjustment,
5044                            gpointer data)
5045 {
5046   GdkRegion *region;
5047   GtkSheet *sheet = GTK_SHEET (data);
5048
5049   g_return_if_fail (adjustment != NULL);
5050
5051   if ( ! GTK_WIDGET_REALIZED (sheet)) return;
5052
5053
5054   gtk_widget_hide (sheet->entry_widget);
5055
5056   region =
5057     gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
5058
5059   gdk_window_begin_paint_region (sheet->sheet_window, region);
5060
5061
5062   gtk_sheet_range_draw (sheet, NULL);
5063   draw_row_title_buttons (sheet);
5064   //  size_allocate_global_button (sheet);
5065
5066   gdk_window_end_paint (sheet->sheet_window);
5067 }
5068
5069
5070 static void
5071 hadjustment_value_changed (GtkAdjustment *adjustment,
5072                            gpointer data)
5073 {
5074   GdkRegion *region;
5075   GtkSheet *sheet = GTK_SHEET (data);
5076
5077   g_return_if_fail (adjustment != NULL);
5078
5079   if ( ! GTK_WIDGET_REALIZED (sheet)) return;
5080
5081   gtk_widget_hide (sheet->entry_widget);
5082
5083
5084   region =
5085     gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
5086
5087   gdk_window_begin_paint_region (sheet->sheet_window, region);
5088
5089   gtk_sheet_range_draw (sheet, NULL);
5090   draw_column_title_buttons (sheet);
5091   //  size_allocate_global_button (sheet);
5092
5093   gdk_window_end_paint (sheet->sheet_window);
5094 }
5095
5096
5097 /* COLUMN RESIZING */
5098 static void
5099 draw_xor_vline (GtkSheet *sheet)
5100 {
5101   gint height;
5102   gint xpos = sheet->x_drag;
5103   gdk_drawable_get_size (sheet->sheet_window,
5104                          NULL, &height);
5105
5106
5107   if (sheet->row_titles_visible)
5108     xpos += sheet->row_title_area.width;
5109
5110   gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
5111                  xpos,
5112                  sheet->column_title_area.height,
5113                  xpos,
5114                  height + CELL_SPACING);
5115 }
5116
5117 /* ROW RESIZING */
5118 static void
5119 draw_xor_hline (GtkSheet *sheet)
5120
5121 {
5122   gint width;
5123   gint ypos = sheet->y_drag;
5124
5125   gdk_drawable_get_size (sheet->sheet_window,
5126                          &width, NULL);
5127
5128
5129   if (sheet->column_titles_visible)
5130     ypos += sheet->column_title_area.height;
5131
5132   gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
5133                  sheet->row_title_area.width,
5134                  ypos,
5135                  width + CELL_SPACING,
5136                  ypos);
5137 }
5138
5139 /* SELECTED RANGE */
5140 static void
5141 draw_xor_rectangle (GtkSheet *sheet, GtkSheetRange range)
5142 {
5143   gint i = 0;
5144   GdkRectangle clip_area, area;
5145   GdkGCValues values;
5146
5147   area.x = g_sheet_column_start_pixel (sheet->column_geometry, range.col0);
5148   area.y = g_sheet_row_start_pixel (sheet->row_geometry, range.row0);
5149   area.width = g_sheet_column_start_pixel (sheet->column_geometry, range.coli)- area.x+
5150     g_sheet_column_get_width (sheet->column_geometry, range.coli);
5151   area.height = g_sheet_row_start_pixel (sheet->row_geometry, range.rowi)- area.y +
5152     g_sheet_row_get_height (sheet->row_geometry, range.rowi);
5153
5154   clip_area.x = sheet->row_title_area.width;
5155   clip_area.y = sheet->column_title_area.height;
5156
5157   gdk_drawable_get_size (sheet->sheet_window,
5158                          &clip_area.width, &clip_area.height);
5159
5160   if (!sheet->row_titles_visible) clip_area.x = 0;
5161   if (!sheet->column_titles_visible) clip_area.y = 0;
5162
5163   if (area.x < 0)
5164     {
5165       area.width = area.width + area.x;
5166       area.x = 0;
5167     }
5168   if (area.width > clip_area.width) area.width = clip_area.width + 10;
5169   if (area.y < 0)
5170     {
5171       area.height = area.height + area.y;
5172       area.y = 0;
5173     }
5174   if (area.height > clip_area.height) area.height = clip_area.height + 10;
5175
5176   clip_area.x--;
5177   clip_area.y--;
5178   clip_area.width += 3;
5179   clip_area.height += 3;
5180
5181   gdk_gc_get_values (sheet->xor_gc, &values);
5182
5183   gdk_gc_set_clip_rectangle (sheet->xor_gc, &clip_area);
5184
5185   gdk_draw_rectangle (sheet->sheet_window,
5186                       sheet->xor_gc,
5187                       FALSE,
5188                       area.x + i, area.y + i,
5189                       area.width - 2 * i, area.height - 2 * i);
5190
5191
5192   gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
5193
5194   gdk_gc_set_foreground (sheet->xor_gc, &values.foreground);
5195 }
5196
5197
5198 /* this function returns the new width of the column being resized given
5199  * the COLUMN and X position of the cursor; the x cursor position is passed
5200  * in as a pointer and automaticaly corrected if it's outside the acceptable
5201  * range */
5202 static guint
5203 new_column_width (GtkSheet *sheet, gint column, gint *x)
5204 {
5205   gint left_pos = g_sheet_column_start_pixel (sheet->column_geometry, column)
5206     - sheet->hadjustment->value;
5207
5208   gint width = *x - left_pos;
5209
5210   if ( width < sheet->column_requisition)
5211     {
5212       width = sheet->column_requisition;
5213       *x = left_pos + width;
5214     }
5215
5216   g_sheet_column_set_width (sheet->column_geometry, column, width);
5217
5218   draw_column_title_buttons (sheet);
5219
5220   return width;
5221 }
5222
5223 /* this function returns the new height of the row being resized given
5224  * the row and y position of the cursor; the y cursor position is passed
5225  * in as a pointer and automaticaly corrected if it's beyond min / max limits */
5226 static guint
5227 new_row_height (GtkSheet *sheet, gint row, gint *y)
5228 {
5229   gint height;
5230   guint min_height;
5231
5232   gint cy = *y;
5233   min_height = sheet->row_requisition;
5234
5235   /* you can't shrink a row to less than its minimum height */
5236   if (cy < g_sheet_row_start_pixel (sheet->row_geometry, row) + min_height)
5237
5238     {
5239       *y = cy = g_sheet_row_start_pixel (sheet->row_geometry, row) + min_height;
5240     }
5241
5242   /* calculate new row height making sure it doesn't end up
5243    * less than the minimum height */
5244   height = (cy - g_sheet_row_start_pixel (sheet->row_geometry, row));
5245   if (height < min_height)
5246     height = min_height;
5247
5248   g_sheet_row_set_height (sheet->row_geometry, row, height);
5249   draw_row_title_buttons (sheet);
5250
5251   return height;
5252 }
5253
5254 static void
5255 gtk_sheet_set_column_width (GtkSheet *sheet,
5256                             gint column,
5257                             guint width)
5258 {
5259   guint min_width;
5260
5261   g_return_if_fail (sheet != NULL);
5262   g_return_if_fail (GTK_IS_SHEET (sheet));
5263
5264   if (column < 0 || column >= g_sheet_column_get_column_count (sheet->column_geometry))
5265     return;
5266
5267   gtk_sheet_column_size_request (sheet, column, &min_width);
5268   if (width < min_width) return;
5269
5270   g_sheet_column_set_width (sheet->column_geometry, column, width);
5271
5272   if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
5273     {
5274       draw_column_title_buttons (sheet);
5275       adjust_scrollbars (sheet);
5276       gtk_sheet_size_allocate_entry (sheet);
5277       gtk_sheet_range_draw (sheet, NULL);
5278     }
5279
5280   g_signal_emit (sheet, sheet_signals[CHANGED], 0, -1, column);
5281 }
5282
5283
5284
5285 static void
5286 gtk_sheet_set_row_height (GtkSheet *sheet,
5287                           gint row,
5288                           guint height)
5289 {
5290   guint min_height;
5291
5292   g_return_if_fail (sheet != NULL);
5293   g_return_if_fail (GTK_IS_SHEET (sheet));
5294
5295   if (row < 0 || row >= g_sheet_row_get_row_count (sheet->row_geometry))
5296     return;
5297
5298   gtk_sheet_row_size_request (sheet, row, &min_height);
5299   if (height < min_height) return;
5300
5301   g_sheet_row_set_height (sheet->row_geometry, row, height);
5302
5303   if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) )
5304     {
5305       draw_row_title_buttons (sheet);
5306       adjust_scrollbars (sheet);
5307       gtk_sheet_size_allocate_entry (sheet);
5308       gtk_sheet_range_draw (sheet, NULL);
5309     }
5310
5311   g_signal_emit (sheet, sheet_signals[CHANGED], 0, row, - 1);
5312 }
5313
5314 gboolean
5315 gtk_sheet_get_attributes (const GtkSheet *sheet, gint row, gint col,
5316                           GtkSheetCellAttr *attr)
5317 {
5318   GdkColor *fg, *bg;
5319   const GtkJustification *j ;
5320   const PangoFontDescription *font_desc ;
5321   const GtkSheetCellBorder *border ;
5322   GdkColormap *colormap;
5323
5324   g_return_val_if_fail (sheet != NULL, FALSE);
5325   g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE);
5326
5327   if (row < 0 || col < 0) return FALSE;
5328
5329
5330   attr->foreground = GTK_WIDGET (sheet)->style->black;
5331   attr->background = sheet->color[BG_COLOR];
5332
5333   attr->border.width = 0;
5334   attr->border.line_style = GDK_LINE_SOLID;
5335   attr->border.cap_style = GDK_CAP_NOT_LAST;
5336   attr->border.join_style = GDK_JOIN_MITER;
5337   attr->border.mask = 0;
5338   attr->border.color = GTK_WIDGET (sheet)->style->black;
5339   attr->font_desc = GTK_WIDGET (sheet)->style->font_desc;
5340
5341   attr->is_editable = g_sheet_model_is_editable (sheet->model, row, col);
5342   attr->is_visible = g_sheet_model_is_visible (sheet->model, row, col);
5343
5344
5345   colormap = gtk_widget_get_colormap (GTK_WIDGET (sheet));
5346   fg = g_sheet_model_get_foreground (sheet->model, row, col);
5347   if ( fg )
5348     {
5349       gdk_colormap_alloc_color (colormap, fg, TRUE, TRUE);
5350       attr->foreground = *fg;
5351     }
5352
5353   bg = g_sheet_model_get_background (sheet->model, row, col);
5354   if ( bg )
5355     {
5356       gdk_colormap_alloc_color (colormap, bg, TRUE, TRUE);
5357       attr->background = *bg;
5358     }
5359
5360   attr->justification =
5361     g_sheet_column_get_justification (sheet->column_geometry, col);
5362
5363   j = g_sheet_model_get_justification (sheet->model, row, col);
5364   if (j)
5365     attr->justification = *j;
5366
5367   font_desc = g_sheet_model_get_font_desc (sheet->model, row, col);
5368   if ( font_desc ) attr->font_desc = font_desc;
5369
5370   border = g_sheet_model_get_cell_border (sheet->model, row, col);
5371
5372   if ( border ) attr->border = *border;
5373
5374   return TRUE;
5375 }
5376
5377 static void
5378 gtk_sheet_button_size_request    (GtkSheet *sheet,
5379                                   const GtkSheetButton *button,
5380                                   GtkRequisition *button_requisition)
5381 {
5382   GtkRequisition requisition;
5383   GtkRequisition label_requisition;
5384
5385   label_requisition.height = DEFAULT_ROW_HEIGHT;
5386   label_requisition.width = COLUMN_MIN_WIDTH;
5387
5388   requisition.height = DEFAULT_ROW_HEIGHT;
5389   requisition.width = COLUMN_MIN_WIDTH;
5390
5391
5392   *button_requisition = requisition;
5393   button_requisition->width = MAX (requisition.width, label_requisition.width);
5394   button_requisition->height = MAX (requisition.height, label_requisition.height);
5395
5396 }
5397
5398 static void
5399 gtk_sheet_row_size_request (GtkSheet *sheet,
5400                             gint row,
5401                             guint *requisition)
5402 {
5403   GtkRequisition button_requisition;
5404
5405   gtk_sheet_button_size_request (sheet,
5406                                  g_sheet_row_get_button (sheet->row_geometry, row),
5407                                  &button_requisition);
5408
5409   *requisition = button_requisition.height;
5410
5411   sheet->row_requisition = *requisition;
5412 }
5413
5414 static void
5415 gtk_sheet_column_size_request (GtkSheet *sheet,
5416                                gint col,
5417                                guint *requisition)
5418 {
5419   GtkRequisition button_requisition;
5420
5421   GtkSheetButton *button = g_sheet_column_get_button (sheet->column_geometry, col);
5422
5423   gtk_sheet_button_size_request (sheet,
5424                                  button,
5425                                  &button_requisition);
5426
5427   gtk_sheet_button_free (button);
5428
5429   *requisition = button_requisition.width;
5430
5431   sheet->column_requisition = *requisition;
5432 }
5433
5434
5435 static void
5436 gtk_sheet_forall (GtkContainer *container,
5437                   gboolean include_internals,
5438                   GtkCallback callback,
5439                   gpointer callback_data)
5440 {
5441   GtkSheet *sheet = GTK_SHEET (container);
5442
5443   g_return_if_fail (callback != NULL);
5444
5445   if (sheet->button && sheet->button->parent)
5446     (* callback) (sheet->button, callback_data);
5447
5448   if (sheet->entry_container && GTK_IS_CONTAINER (sheet->entry_container))
5449     (* callback) (sheet->entry_container, callback_data);
5450 }
5451
5452
5453 GSheetModel *
5454 gtk_sheet_get_model (const GtkSheet *sheet)
5455 {
5456   g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);
5457
5458   return sheet->model;
5459 }
5460
5461
5462 GtkSheetButton *
5463 gtk_sheet_button_new (void)
5464 {
5465   GtkSheetButton *button = g_malloc (sizeof (GtkSheetButton));
5466
5467   button->state = GTK_STATE_NORMAL;
5468   button->label = NULL;
5469   button->label_visible = TRUE;
5470   button->justification = GTK_JUSTIFY_FILL;
5471
5472   return button;
5473 }
5474
5475
5476 void
5477 gtk_sheet_button_free (GtkSheetButton *button)
5478 {
5479   if (!button) return ;
5480
5481   g_free (button->label);
5482   g_free (button);
5483 }
5484
5485
5486 static void
5487 append_cell_text (GString *string, const GtkSheet *sheet, gint r, gint c)
5488 {
5489   gchar *celltext = gtk_sheet_cell_get_text (sheet, r, c);
5490
5491   if ( NULL == celltext)
5492     return;
5493
5494   g_string_append (string, celltext);
5495   g_free (celltext);
5496 }
5497
5498
5499 static GString *
5500 range_to_text (const GtkSheet *sheet)
5501 {
5502   gint r, c;
5503   GString *string;
5504
5505   if ( !gtk_sheet_range_isvisible (sheet, sheet->range))
5506     return NULL;
5507
5508   string = g_string_sized_new (80);
5509
5510   for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
5511     {
5512       for (c = sheet->range.col0; c < sheet->range.coli; ++c)
5513         {
5514           append_cell_text (string, sheet, r, c);
5515           g_string_append (string, "\t");
5516         }
5517       append_cell_text (string, sheet, r, c);
5518       if ( r < sheet->range.rowi)
5519         g_string_append (string, "\n");
5520     }
5521
5522   return string;
5523 }
5524
5525 static GString *
5526 range_to_html (const GtkSheet *sheet)
5527 {
5528   gint r, c;
5529   GString *string;
5530
5531   if ( !gtk_sheet_range_isvisible (sheet, sheet->range))
5532     return NULL;
5533
5534   string = g_string_sized_new (480);
5535
5536   g_string_append (string, "<html>\n");
5537   g_string_append (string, "<body>\n");
5538   g_string_append (string, "<table>\n");
5539   for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
5540     {
5541       g_string_append (string, "<tr>\n");
5542       for (c = sheet->range.col0; c <= sheet->range.coli; ++c)
5543         {
5544           g_string_append (string, "<td>");
5545           append_cell_text (string, sheet, r, c);
5546           g_string_append (string, "</td>\n");
5547         }
5548       g_string_append (string, "</tr>\n");
5549     }
5550   g_string_append (string, "</table>\n");
5551   g_string_append (string, "</body>\n");
5552   g_string_append (string, "</html>\n");
5553
5554   return string;
5555 }
5556
5557 enum {
5558   SELECT_FMT_NULL,
5559   SELECT_FMT_TEXT,
5560   SELECT_FMT_HTML
5561 };
5562
5563 static void
5564 primary_get_cb (GtkClipboard     *clipboard,
5565                 GtkSelectionData *selection_data,
5566                 guint             info,
5567                 gpointer          data)
5568 {
5569   GtkSheet *sheet = GTK_SHEET (data);
5570   GString *string = NULL;
5571
5572   switch (info)
5573     {
5574     case SELECT_FMT_TEXT:
5575       string = range_to_text (sheet);
5576       break;
5577     case SELECT_FMT_HTML:
5578       string = range_to_html (sheet);
5579       break;
5580     default:
5581       g_assert_not_reached ();
5582     }
5583
5584   gtk_selection_data_set (selection_data, selection_data->target,
5585                           8,
5586                           (const guchar *) string->str, string->len);
5587   g_string_free (string, TRUE);
5588 }
5589
5590 static void
5591 primary_clear_cb (GtkClipboard *clipboard,
5592                   gpointer      data)
5593 {
5594   GtkSheet *sheet = GTK_SHEET (data);
5595   if ( ! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
5596     return;
5597
5598   gtk_sheet_real_unselect_range (sheet, NULL);
5599 }
5600
5601 static void
5602 gtk_sheet_update_primary_selection (GtkSheet *sheet)
5603 {
5604   static const GtkTargetEntry targets[] = {
5605     { "UTF8_STRING",   0, SELECT_FMT_TEXT },
5606     { "STRING",        0, SELECT_FMT_TEXT },
5607     { "TEXT",          0, SELECT_FMT_TEXT },
5608     { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
5609     { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
5610     { "text/plain",    0, SELECT_FMT_TEXT },
5611     { "text/html",     0, SELECT_FMT_HTML }
5612   };
5613
5614   GtkClipboard *clipboard;
5615
5616   if (!GTK_WIDGET_REALIZED (sheet))
5617     return;
5618
5619   clipboard = gtk_widget_get_clipboard (GTK_WIDGET (sheet),
5620                                         GDK_SELECTION_PRIMARY);
5621
5622   if (gtk_sheet_range_isvisible (sheet, sheet->range))
5623     {
5624       if (!gtk_clipboard_set_with_owner (clipboard, targets,
5625                                          G_N_ELEMENTS (targets),
5626                                          primary_get_cb, primary_clear_cb,
5627                                          G_OBJECT (sheet)))
5628         primary_clear_cb (clipboard, sheet);
5629     }
5630   else
5631     {
5632       if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (sheet))
5633         gtk_clipboard_clear (clipboard);
5634     }
5635 }