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