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