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