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