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