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