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