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