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