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