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