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