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