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