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