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