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