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