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