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