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