Removed the STRING_WIDTH function
[pspp] / 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
2717 static void
2718 gtk_sheet_draw_active_cell (GtkSheet *sheet)
2719 {
2720   gint row, col;
2721   GtkSheetRange range;
2722
2723   if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (sheet))) return;
2724   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2725
2726   row = sheet->active_cell.row;
2727   col = sheet->active_cell.col;
2728
2729   if (row < 0 || col < 0) return;
2730
2731   if (!gtk_sheet_cell_isvisible (sheet, row, col))
2732     return;
2733
2734   range.col0 = range.coli = col;
2735   range.row0 = range.rowi = row;
2736
2737   gtk_sheet_draw_border (sheet, range);
2738 }
2739
2740
2741
2742 static void
2743 gtk_sheet_new_selection (GtkSheet *sheet, GtkSheetRange *range)
2744 {
2745   gint i, j, mask1, mask2;
2746   gint state, selected;
2747   gint x, y, width, height;
2748   GtkSheetRange new_range, aux_range;
2749
2750   g_return_if_fail (sheet != NULL);
2751
2752   if (range == NULL) range=&sheet->range;
2753
2754   new_range=*range;
2755
2756   range->row0 = MIN (range->row0, sheet->range.row0);
2757   range->rowi = MAX (range->rowi, sheet->range.rowi);
2758   range->col0 = MIN (range->col0, sheet->range.col0);
2759   range->coli = MAX (range->coli, sheet->range.coli);
2760
2761   range->row0 = MAX (range->row0, min_visible_row (sheet));
2762   range->rowi = MIN (range->rowi, max_visible_row (sheet));
2763   range->col0 = MAX (range->col0, min_visible_column (sheet));
2764   range->coli = MIN (range->coli, max_visible_column (sheet));
2765
2766   aux_range.row0 = MAX (new_range.row0, min_visible_row (sheet));
2767   aux_range.rowi = MIN (new_range.rowi, max_visible_row (sheet));
2768   aux_range.col0 = MAX (new_range.col0, min_visible_column (sheet));
2769   aux_range.coli = MIN (new_range.coli, max_visible_column (sheet));
2770
2771   for (i = range->row0; i <= range->rowi; i++)
2772     {
2773       for (j = range->col0; j <= range->coli; j++)
2774         {
2775
2776           state = gtk_sheet_cell_get_state (sheet, i, j);
2777           selected= (i <= new_range.rowi && i >= new_range.row0 &&
2778                      j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
2779
2780           if (state == GTK_STATE_SELECTED && selected &&
2781               (i == sheet->range.row0 || i == sheet->range.rowi ||
2782                j == sheet->range.col0 || j == sheet->range.coli ||
2783                i == new_range.row0 || i == new_range.rowi ||
2784                j == new_range.col0 || j == new_range.coli))
2785             {
2786
2787               mask1 = i == sheet->range.row0 ? 1 : 0;
2788               mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1;
2789               mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1;
2790               mask1 = j == sheet->range.coli ? mask1 + 8 : mask1;
2791
2792               mask2 = i == new_range.row0 ? 1 : 0;
2793               mask2 = i == new_range.rowi ? mask2 + 2 : mask2;
2794               mask2 = j == new_range.col0 ? mask2 + 4 : mask2;
2795               mask2 = j == new_range.coli ? mask2 + 8 : mask2;
2796
2797               if (mask1 != mask2)
2798                 {
2799                   x = g_sheet_column_start_pixel (sheet->column_geometry, j);
2800                   y = g_sheet_row_start_pixel (sheet->row_geometry, i);
2801                   width = g_sheet_column_start_pixel (sheet->column_geometry, j)- x+
2802                     g_sheet_column_get_width (sheet->column_geometry, j);
2803                   height = g_sheet_row_start_pixel (sheet->row_geometry, i) - y + g_sheet_row_get_height (sheet->row_geometry, i);
2804
2805                   if (i == sheet->range.row0)
2806                     {
2807                       y = y - 3;
2808                       height = height + 3;
2809                     }
2810                   if (i == sheet->range.rowi) height = height + 3;
2811                   if (j == sheet->range.col0)
2812                     {
2813                       x = x - 3;
2814                       width = width + 3;
2815                     }
2816                   if (j == sheet->range.coli) width = width + 3;
2817
2818                   if (i != sheet->active_cell.row || j != sheet->active_cell.col)
2819                     {
2820                       x = g_sheet_column_start_pixel (sheet->column_geometry, j);
2821                       y = g_sheet_row_start_pixel (sheet->row_geometry, i);
2822                       width = g_sheet_column_start_pixel (sheet->column_geometry, j)- x+
2823                         g_sheet_column_get_width (sheet->column_geometry, j);
2824
2825                       height = g_sheet_row_start_pixel (sheet->row_geometry, i) - y + g_sheet_row_get_height (sheet->row_geometry, i);
2826
2827                       if (i == new_range.row0)
2828                         {
2829                           y = y+2;
2830                           height = height - 2;
2831                         }
2832                       if (i == new_range.rowi) height = height - 3;
2833                       if (j == new_range.col0)
2834                         {
2835                           x = x+2;
2836                           width = width - 2;
2837                         }
2838                       if (j == new_range.coli) width = width - 3;
2839
2840                       gdk_draw_rectangle (sheet->sheet_window,
2841                                           sheet->xor_gc,
2842                                           TRUE,
2843                                           x + 1, y + 1,
2844                                           width, height);
2845                     }
2846                 }
2847             }
2848         }
2849     }
2850
2851   for (i = range->row0; i <= range->rowi; i++)
2852     {
2853       for (j = range->col0; j <= range->coli; j++)
2854         {
2855
2856           state = gtk_sheet_cell_get_state (sheet, i, j);
2857           selected= (i <= new_range.rowi && i >= new_range.row0 &&
2858                      j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
2859
2860           if (state == GTK_STATE_SELECTED && !selected)
2861             {
2862
2863               x = g_sheet_column_start_pixel (sheet->column_geometry, j);
2864               y = g_sheet_row_start_pixel (sheet->row_geometry, i);
2865               width = g_sheet_column_start_pixel (sheet->column_geometry, j) - x + g_sheet_column_get_width (sheet->column_geometry, j);
2866               height = g_sheet_row_start_pixel (sheet->row_geometry, i) - y + g_sheet_row_get_height (sheet->row_geometry, i);
2867
2868               if (i == sheet->range.row0)
2869                 {
2870                   y = y - 3;
2871                   height = height + 3;
2872                 }
2873               if (i == sheet->range.rowi) height = height + 3;
2874               if (j == sheet->range.col0)
2875                 {
2876                   x = x - 3;
2877                   width = width + 3;
2878                 }
2879               if (j == sheet->range.coli) width = width + 3;
2880
2881             }
2882         }
2883     }
2884
2885   for (i = range->row0; i <= range->rowi; i++)
2886     {
2887       for (j = range->col0; j <= range->coli; j++)
2888         {
2889
2890           state = gtk_sheet_cell_get_state (sheet, i, j);
2891           selected= (i <= new_range.rowi && i >= new_range.row0 &&
2892                      j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
2893
2894           if (state != GTK_STATE_SELECTED && selected &&
2895               (i != sheet->active_cell.row || j != sheet->active_cell.col))
2896             {
2897
2898               x = g_sheet_column_start_pixel (sheet->column_geometry, j);
2899               y = g_sheet_row_start_pixel (sheet->row_geometry, i);
2900               width = g_sheet_column_start_pixel (sheet->column_geometry, j) - x + g_sheet_column_get_width (sheet->column_geometry, j);
2901               height = g_sheet_row_start_pixel (sheet->row_geometry, i) - y + g_sheet_row_get_height (sheet->row_geometry, i);
2902
2903               if (i == new_range.row0)
2904                 {
2905                   y = y+2;
2906                   height = height - 2;
2907                 }
2908               if (i == new_range.rowi) height = height - 3;
2909               if (j == new_range.col0)
2910                 {
2911                   x = x+2;
2912                   width = width - 2;
2913                 }
2914               if (j == new_range.coli) width = width - 3;
2915
2916               gdk_draw_rectangle (sheet->sheet_window,
2917                                   sheet->xor_gc,
2918                                   TRUE,
2919                                   x + 1, y + 1,
2920                                   width, height);
2921
2922             }
2923
2924         }
2925     }
2926
2927   for (i = aux_range.row0; i <= aux_range.rowi; i++)
2928     {
2929       for (j = aux_range.col0; j <= aux_range.coli; j++)
2930         {
2931           state = gtk_sheet_cell_get_state (sheet, i, j);
2932
2933           mask1 = i == sheet->range.row0 ? 1 : 0;
2934           mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1;
2935           mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1;
2936           mask1 = j == sheet->range.coli ? mask1 + 8 : mask1;
2937
2938           mask2 = i == new_range.row0 ? 1 : 0;
2939           mask2 = i == new_range.rowi ? mask2 + 2 : mask2;
2940           mask2 = j == new_range.col0 ? mask2 + 4 : mask2;
2941           mask2 = j == new_range.coli ? mask2 + 8 : mask2;
2942           if (mask2 != mask1 || (mask2 == mask1 && state != GTK_STATE_SELECTED))
2943             {
2944               x = g_sheet_column_start_pixel (sheet->column_geometry, j);
2945               y = g_sheet_row_start_pixel (sheet->row_geometry, i);
2946               width = g_sheet_column_get_width (sheet->column_geometry, j);
2947               height = g_sheet_row_get_height (sheet->row_geometry, i);
2948               if (mask2 & 1)
2949                 gdk_draw_rectangle (sheet->sheet_window,
2950                                     sheet->xor_gc,
2951                                     TRUE,
2952                                     x + 1, y - 1,
2953                                     width, 3);
2954
2955
2956               if (mask2 & 2)
2957                 gdk_draw_rectangle (sheet->sheet_window,
2958                                     sheet->xor_gc,
2959                                     TRUE,
2960                                     x + 1, y + height - 1,
2961                                     width, 3);
2962
2963               if (mask2 & 4)
2964                 gdk_draw_rectangle (sheet->sheet_window,
2965                                     sheet->xor_gc,
2966                                     TRUE,
2967                                     x - 1, y + 1,
2968                                     3, height);
2969
2970
2971               if (mask2 & 8)
2972                 gdk_draw_rectangle (sheet->sheet_window,
2973                                     sheet->xor_gc,
2974                                     TRUE,
2975                                     x + width - 1, y + 1,
2976                                     3, height);
2977             }
2978         }
2979     }
2980
2981   *range = new_range;
2982 }
2983
2984 static void
2985 gtk_sheet_draw_border (GtkSheet *sheet, GtkSheetRange new_range)
2986 {
2987   GdkRectangle area;
2988
2989   rectangle_from_range (sheet, &new_range, &area);
2990
2991   gdk_draw_rectangle (sheet->sheet_window,
2992                       sheet->xor_gc,
2993                       FALSE,
2994                       area.x + 1,
2995                       area.y + 1,
2996                       area.width - 2,
2997                       area.height - 2);
2998 }
2999
3000
3001 static void
3002 gtk_sheet_real_select_range (GtkSheet *sheet,
3003                              const GtkSheetRange *range)
3004 {
3005   gint state;
3006
3007   g_return_if_fail (sheet != NULL);
3008
3009   if (range == NULL) range = &sheet->range;
3010
3011   memcpy (&sheet->range, range, sizeof (*range));
3012
3013   if (range->row0 < 0 || range->rowi < 0) return;
3014   if (range->col0 < 0 || range->coli < 0) return;
3015
3016   state = sheet->state;
3017
3018 #if 0
3019   if (range->coli != sheet->range.coli || range->col0 != sheet->range.col0 ||
3020       range->rowi != sheet->range.rowi || range->row0 != sheet->range.row0)
3021     {
3022       gtk_sheet_new_selection (sheet, &sheet->range);
3023     }
3024   else
3025     {
3026       gtk_sheet_range_draw_selection (sheet, sheet->range);
3027     }
3028 #endif
3029
3030   gtk_sheet_update_primary_selection (sheet);
3031
3032   g_signal_emit (sheet, sheet_signals[SELECT_RANGE], 0, &sheet->range);
3033 }
3034
3035
3036 void
3037 gtk_sheet_get_selected_range (GtkSheet *sheet, GtkSheetRange *range)
3038 {
3039   g_return_if_fail (sheet != NULL);
3040   *range = sheet->range;
3041 }
3042
3043
3044 void
3045 gtk_sheet_select_range (GtkSheet *sheet, const GtkSheetRange *range)
3046 {
3047   g_return_if_fail (sheet != NULL);
3048
3049   if (range == NULL) range=&sheet->range;
3050
3051   if (range->row0 < 0 || range->rowi < 0) return;
3052   if (range->col0 < 0 || range->coli < 0) return;
3053
3054
3055   if (sheet->state != GTK_SHEET_NORMAL)
3056     gtk_sheet_real_unselect_range (sheet, NULL);
3057
3058   sheet->range.row0 = range->row0;
3059   sheet->range.rowi = range->rowi;
3060   sheet->range.col0 = range->col0;
3061   sheet->range.coli = range->coli;
3062   sheet->active_cell.row = range->row0;
3063   sheet->active_cell.col = range->col0;
3064   sheet->selection_cell.row = range->rowi;
3065   sheet->selection_cell.col = range->coli;
3066
3067   sheet->state = GTK_SHEET_RANGE_SELECTED;
3068   gtk_sheet_real_select_range (sheet, NULL);
3069 }
3070
3071 void
3072 gtk_sheet_unselect_range (GtkSheet *sheet)
3073 {
3074   if (! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
3075     return;
3076
3077   gtk_sheet_real_unselect_range (sheet, NULL);
3078   sheet->state = GTK_STATE_NORMAL;
3079
3080   change_active_cell (sheet,
3081                  sheet->active_cell.row, sheet->active_cell.col);
3082 }
3083
3084
3085 static void
3086 gtk_sheet_real_unselect_range (GtkSheet *sheet,
3087                                const GtkSheetRange *range)
3088 {
3089   g_return_if_fail (sheet != NULL);
3090   g_return_if_fail (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)));
3091
3092   if ( range == NULL)
3093     range = &sheet->range;
3094
3095   if (range->row0 < 0 || range->rowi < 0) return;
3096   if (range->col0 < 0 || range->coli < 0) return;
3097
3098   g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, -1);
3099   g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, -1);
3100
3101 #if 0
3102   if (gtk_sheet_range_isvisible (sheet, *range))
3103     gtk_sheet_draw_backing_pixmap (sheet, *range);
3104 #endif
3105
3106   sheet->range.row0 = -1;
3107   sheet->range.rowi = -1;
3108   sheet->range.col0 = -1;
3109   sheet->range.coli = -1;
3110 }
3111
3112
3113 static gint
3114 gtk_sheet_expose (GtkWidget *widget,
3115                   GdkEventExpose *event)
3116 {
3117   GtkSheet *sheet;
3118   GtkSheetRange range;
3119
3120   g_return_val_if_fail (widget != NULL, FALSE);
3121   g_return_val_if_fail (GTK_IS_SHEET (widget), FALSE);
3122   g_return_val_if_fail (event != NULL, FALSE);
3123
3124   sheet = GTK_SHEET (widget);
3125
3126   if (!GTK_WIDGET_DRAWABLE (widget))
3127     return FALSE;
3128
3129   /* exposure events on the sheet */
3130   if (event->window == sheet->row_title_window &&
3131       sheet->row_titles_visible)
3132     {
3133       draw_row_title_buttons_range (sheet,
3134                                     min_visible_row (sheet),
3135                                     max_visible_row (sheet));
3136     }
3137
3138   if (event->window == sheet->column_title_window &&
3139       sheet->column_titles_visible)
3140     {
3141       draw_column_title_buttons_range (sheet,
3142                                        min_visible_column (sheet),
3143                                        max_visible_column (sheet));
3144     }
3145
3146
3147   range.row0 =
3148     yyy_row_ypixel_to_row (sheet,
3149                            event->area.y + sheet->vadjustment->value);
3150   range.row0--;
3151
3152   range.rowi =
3153     yyy_row_ypixel_to_row (sheet,
3154                            event->area.y +
3155                            event->area.height + sheet->vadjustment->value);
3156   range.rowi++;
3157
3158   range.col0 =
3159     column_from_xpixel (sheet,
3160                         event->area.x + sheet->hadjustment->value);
3161   range.col0--;
3162
3163   range.coli =
3164     column_from_xpixel (sheet,
3165                         event->area.x + event->area.width +
3166                         sheet->hadjustment->value);
3167   range.coli++;
3168
3169   if (event->window == sheet->sheet_window)
3170     {
3171       gtk_sheet_range_draw (sheet, &range);
3172
3173       if (sheet->state != GTK_SHEET_NORMAL)
3174         {
3175           if (gtk_sheet_range_isvisible (sheet, sheet->range))
3176             gtk_sheet_range_draw (sheet, &sheet->range);
3177
3178           if (GTK_SHEET_IN_RESIZE (sheet) || GTK_SHEET_IN_DRAG (sheet))
3179             gtk_sheet_range_draw (sheet, &sheet->drag_range);
3180
3181           if (gtk_sheet_range_isvisible (sheet, sheet->range))
3182             gtk_sheet_range_draw_selection (sheet, sheet->range);
3183           if (GTK_SHEET_IN_RESIZE (sheet) || GTK_SHEET_IN_DRAG (sheet))
3184             draw_xor_rectangle (sheet, sheet->drag_range);
3185         }
3186
3187       if ((!GTK_SHEET_IN_XDRAG (sheet)) && (!GTK_SHEET_IN_YDRAG (sheet)))
3188         {
3189           if (sheet->state == GTK_SHEET_NORMAL)
3190             gtk_sheet_draw_active_cell (sheet);
3191         }
3192     }
3193
3194   if (sheet->state != GTK_SHEET_NORMAL && GTK_SHEET_IN_SELECTION (sheet))
3195     gtk_widget_grab_focus (GTK_WIDGET (sheet));
3196
3197   (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
3198
3199   return FALSE;
3200 }
3201
3202
3203 static gboolean
3204 gtk_sheet_button_press (GtkWidget *widget,
3205                         GdkEventButton *event)
3206 {
3207   GtkSheet *sheet;
3208   GdkModifierType mods;
3209   gint x, y;
3210   gint  row, column;
3211   gboolean veto;
3212
3213   g_return_val_if_fail (widget != NULL, FALSE);
3214   g_return_val_if_fail (GTK_IS_SHEET (widget), FALSE);
3215   g_return_val_if_fail (event != NULL, FALSE);
3216
3217   sheet = GTK_SHEET (widget);
3218
3219   /* Cancel any pending tooltips */
3220   if (sheet->motion_timer)
3221     {
3222       g_source_remove (sheet->motion_timer);
3223       sheet->motion_timer = 0;
3224     }
3225
3226   gtk_widget_get_pointer (widget, &x, &y);
3227   gtk_sheet_get_pixel_info (sheet, x, y, &row, &column);
3228
3229
3230   if (event->window == sheet->column_title_window)
3231     {
3232       g_signal_emit (sheet,
3233                      sheet_signals[BUTTON_EVENT_COLUMN], 0,
3234                      column, event);
3235
3236       if (g_sheet_column_get_sensitivity (sheet->column_geometry, column))
3237         {
3238           if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
3239             g_signal_emit (sheet,
3240                            sheet_signals[DOUBLE_CLICK_COLUMN], 0, column);
3241         }
3242     }
3243   else if (event->window == sheet->row_title_window)
3244     {
3245       g_signal_emit (sheet,
3246                      sheet_signals[BUTTON_EVENT_ROW], 0,
3247                      row, event);
3248
3249
3250       if (g_sheet_row_get_sensitivity (sheet->row_geometry, row))
3251         {
3252           if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
3253             g_signal_emit (sheet,
3254                            sheet_signals[DOUBLE_CLICK_ROW], 0, row);
3255         }
3256     }
3257
3258   gdk_window_get_pointer (widget->window, NULL, NULL, &mods);
3259
3260   if (! (mods & GDK_BUTTON1_MASK)) return TRUE;
3261
3262
3263   /* press on resize windows */
3264   if (event->window == sheet->column_title_window &&
3265       gtk_sheet_columns_resizable (sheet))
3266     {
3267 #if 0
3268       gtk_widget_get_pointer (widget, &sheet->x_drag, NULL);
3269       if ( sheet->row_titles_visible)
3270         sheet->x_drag -= sheet->row_title_area.width;
3271 #endif
3272
3273       sheet->x_drag = event->x;
3274
3275       if (on_column_boundary (sheet, sheet->x_drag, &sheet->drag_cell.col))
3276         {
3277           guint req;
3278           gtk_sheet_column_size_request (sheet, sheet->drag_cell.col, &req);
3279           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_XDRAG);
3280           gdk_pointer_grab (sheet->column_title_window, FALSE,
3281                             GDK_POINTER_MOTION_HINT_MASK |
3282                             GDK_BUTTON1_MOTION_MASK |
3283                             GDK_BUTTON_RELEASE_MASK,
3284                             NULL, NULL, event->time);
3285
3286           draw_xor_vline (sheet);
3287           return TRUE;
3288         }
3289     }
3290
3291   if (event->window == sheet->row_title_window && gtk_sheet_rows_resizable (sheet))
3292     {
3293       gtk_widget_get_pointer (widget, NULL, &sheet->y_drag);
3294
3295       if (POSSIBLE_YDRAG (sheet, sheet->y_drag, &sheet->drag_cell.row))
3296         {
3297           guint req;
3298           gtk_sheet_row_size_request (sheet, sheet->drag_cell.row, &req);
3299           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_YDRAG);
3300           gdk_pointer_grab (sheet->row_title_window, FALSE,
3301                             GDK_POINTER_MOTION_HINT_MASK |
3302                             GDK_BUTTON1_MOTION_MASK |
3303                             GDK_BUTTON_RELEASE_MASK,
3304                             NULL, NULL, event->time);
3305
3306           draw_xor_hline (sheet);
3307           return TRUE;
3308         }
3309     }
3310
3311   /* the sheet itself does not handle other than single click events */
3312   if (event->type != GDK_BUTTON_PRESS) return FALSE;
3313
3314   /* selections on the sheet */
3315   if (event->window == sheet->sheet_window)
3316     {
3317       gtk_widget_get_pointer (widget, &x, &y);
3318       gtk_sheet_get_pixel_info (sheet, x, y, &row, &column);
3319       gdk_pointer_grab (sheet->sheet_window, FALSE,
3320                         GDK_POINTER_MOTION_HINT_MASK |
3321                         GDK_BUTTON1_MOTION_MASK |
3322                         GDK_BUTTON_RELEASE_MASK,
3323                         NULL, NULL, event->time);
3324       gtk_grab_add (GTK_WIDGET (sheet));
3325
3326       /* This seems to be a kludge to work around a problem where the sheet
3327          scrolls to another position.  The timeout scrolls it back to its
3328          original posn.          JMD 3 July 2007
3329       */
3330       gtk_widget_grab_focus (GTK_WIDGET (sheet));
3331
3332       if (sheet->selection_mode != GTK_SELECTION_SINGLE &&
3333           sheet->selection_mode != GTK_SELECTION_NONE &&
3334           sheet->cursor_drag->type == GDK_SIZING &&
3335           !GTK_SHEET_IN_SELECTION (sheet) && !GTK_SHEET_IN_RESIZE (sheet))
3336         {
3337           if (sheet->state == GTK_STATE_NORMAL)
3338             {
3339               row = sheet->active_cell.row;
3340               column = sheet->active_cell.col;
3341               sheet->active_cell.row = row;
3342               sheet->active_cell.col = column;
3343               sheet->drag_range = sheet->range;
3344               sheet->state = GTK_SHEET_RANGE_SELECTED;
3345               gtk_sheet_select_range (sheet, &sheet->drag_range);
3346             }
3347           sheet->x_drag = x;
3348           sheet->y_drag = y;
3349           if (row > sheet->range.rowi) row--;
3350           if (column > sheet->range.coli) column--;
3351           sheet->drag_cell.row = row;
3352           sheet->drag_cell.col = column;
3353           sheet->drag_range = sheet->range;
3354           draw_xor_rectangle (sheet, sheet->drag_range);
3355           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_RESIZE);
3356         }
3357       else if (sheet->cursor_drag->type == GDK_TOP_LEFT_ARROW &&
3358                !GTK_SHEET_IN_SELECTION (sheet)
3359                && ! GTK_SHEET_IN_DRAG (sheet)
3360                && sheet->active_cell.row >= 0
3361                && sheet->active_cell.col >= 0
3362                )
3363         {
3364           if (sheet->state == GTK_STATE_NORMAL)
3365             {
3366               row = sheet->active_cell.row;
3367               column = sheet->active_cell.col;
3368               sheet->active_cell.row = row;
3369               sheet->active_cell.col = column;
3370               sheet->drag_range = sheet->range;
3371               sheet->state = GTK_SHEET_RANGE_SELECTED;
3372               gtk_sheet_select_range (sheet, &sheet->drag_range);
3373             }
3374           sheet->x_drag = x;
3375           sheet->y_drag = y;
3376           if (row < sheet->range.row0) row++;
3377           if (row > sheet->range.rowi) row--;
3378           if (column < sheet->range.col0) column++;
3379           if (column > sheet->range.coli) column--;
3380           sheet->drag_cell.row = row;
3381           sheet->drag_cell.col = column;
3382           sheet->drag_range = sheet->range;
3383           draw_xor_rectangle (sheet, sheet->drag_range);
3384           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_DRAG);
3385         }
3386       else
3387         {
3388           veto = gtk_sheet_click_cell (sheet, row, column);
3389           if (veto) GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
3390         }
3391     }
3392
3393   if (event->window == sheet->column_title_window)
3394     {
3395       gtk_widget_get_pointer (widget, &x, &y);
3396       if ( sheet->row_titles_visible)
3397         x -= sheet->row_title_area.width;
3398
3399       x += sheet->hadjustment->value;
3400
3401       column = column_from_xpixel (sheet, x);
3402
3403       if (g_sheet_column_get_sensitivity (sheet->column_geometry, column))
3404         {
3405           veto = gtk_sheet_click_cell (sheet, -1, column);
3406           gtk_grab_add (GTK_WIDGET (sheet));
3407           gtk_widget_grab_focus (GTK_WIDGET (sheet));
3408           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
3409         }
3410     }
3411
3412   if (event->window == sheet->row_title_window)
3413     {
3414       gtk_widget_get_pointer (widget, &x, &y);
3415       if ( sheet->column_titles_visible)
3416         y -= sheet->column_title_area.height;
3417
3418       y += sheet->vadjustment->value;
3419
3420       row = yyy_row_ypixel_to_row (sheet, y);
3421       if (g_sheet_row_get_sensitivity (sheet->row_geometry, row))
3422         {
3423           veto = gtk_sheet_click_cell (sheet, row, -1);
3424           gtk_grab_add (GTK_WIDGET (sheet));
3425           gtk_widget_grab_focus (GTK_WIDGET (sheet));
3426           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
3427         }
3428     }
3429
3430   return TRUE;
3431 }
3432
3433 static gboolean
3434 gtk_sheet_click_cell (GtkSheet *sheet, gint row, gint column)
3435 {
3436   GtkSheetCell cell;
3437   gboolean forbid_move;
3438
3439   cell.row = row;
3440   cell.col = column;
3441
3442   if (row >= g_sheet_row_get_row_count (sheet->row_geometry)
3443       || column >= g_sheet_column_get_column_count (sheet->column_geometry))
3444     {
3445       return FALSE;
3446     }
3447
3448   g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
3449                  &sheet->active_cell,
3450                  &cell,
3451                  &forbid_move);
3452
3453   if (forbid_move)
3454     {
3455       if (sheet->state == GTK_STATE_NORMAL)
3456         return FALSE;
3457
3458       row = sheet->active_cell.row;
3459       column = sheet->active_cell.col;
3460
3461       change_active_cell (sheet, row, column);
3462       return FALSE;
3463     }
3464
3465   if (row == -1 && column >= 0)
3466     {
3467       gtk_sheet_select_column (sheet, column);
3468       return TRUE;
3469     }
3470
3471   if (column == -1 && row >= 0)
3472     {
3473       gtk_sheet_select_row (sheet, row);
3474       return TRUE;
3475     }
3476
3477   if (row == -1 && column == -1)
3478     {
3479       sheet->range.row0 = 0;
3480       sheet->range.col0 = 0;
3481       sheet->range.rowi = g_sheet_row_get_row_count (sheet->row_geometry) - 1;
3482       sheet->range.coli =
3483         g_sheet_column_get_column_count (sheet->column_geometry) - 1;
3484       sheet->active_cell.row = 0;
3485       sheet->active_cell.col = 0;
3486       gtk_sheet_select_range (sheet, NULL);
3487       return TRUE;
3488     }
3489
3490   if (sheet->state != GTK_SHEET_NORMAL)
3491     {
3492       sheet->state = GTK_SHEET_NORMAL;
3493       gtk_sheet_real_unselect_range (sheet, NULL);
3494     }
3495   else
3496     {
3497       change_active_cell (sheet, row, column);
3498     }
3499
3500   sheet->active_cell.row = row;
3501   sheet->active_cell.col = column;
3502   sheet->selection_cell.row = row;
3503   sheet->selection_cell.col = column;
3504   sheet->range.row0 = row;
3505   sheet->range.col0 = column;
3506   sheet->range.rowi = row;
3507   sheet->range.coli = column;
3508   sheet->state = GTK_SHEET_NORMAL;
3509   GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
3510   gtk_sheet_draw_active_cell (sheet);
3511   return TRUE;
3512 }
3513
3514 static gint
3515 gtk_sheet_button_release (GtkWidget *widget,
3516                           GdkEventButton *event)
3517 {
3518   gint y;
3519   GdkDisplay *display = gtk_widget_get_display (widget);
3520
3521   GtkSheet *sheet = GTK_SHEET (widget);
3522
3523   /* release on resize windows */
3524   if (GTK_SHEET_IN_XDRAG (sheet))
3525     {
3526       gint xpos = event->x;
3527       gint width;
3528       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_XDRAG);
3529       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
3530
3531       gdk_display_pointer_ungrab (display, event->time);
3532       draw_xor_vline (sheet);
3533
3534       width = new_column_width (sheet, sheet->drag_cell.col, &xpos);
3535
3536       gtk_sheet_set_column_width (sheet, sheet->drag_cell.col, width);
3537       return TRUE;
3538     }
3539
3540   if (GTK_SHEET_IN_YDRAG (sheet))
3541     {
3542       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_YDRAG);
3543       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
3544       gtk_widget_get_pointer (widget, NULL, &y);
3545       gdk_display_pointer_ungrab (display, event->time);
3546       draw_xor_hline (sheet);
3547
3548       gtk_sheet_set_row_height (sheet, sheet->drag_cell.row,
3549                                 new_row_height (sheet, sheet->drag_cell.row, &y));
3550       g_signal_emit_by_name (sheet->vadjustment, "value_changed");
3551       return TRUE;
3552     }
3553
3554
3555   if (GTK_SHEET_IN_DRAG (sheet))
3556     {
3557       GtkSheetRange old_range;
3558       draw_xor_rectangle (sheet, sheet->drag_range);
3559       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_DRAG);
3560       gdk_display_pointer_ungrab (display, event->time);
3561
3562       gtk_sheet_real_unselect_range (sheet, NULL);
3563
3564       sheet->active_cell.row = sheet->active_cell.row +
3565         (sheet->drag_range.row0 - sheet->range.row0);
3566       sheet->active_cell.col = sheet->active_cell.col +
3567         (sheet->drag_range.col0 - sheet->range.col0);
3568       sheet->selection_cell.row = sheet->selection_cell.row +
3569         (sheet->drag_range.row0 - sheet->range.row0);
3570       sheet->selection_cell.col = sheet->selection_cell.col +
3571         (sheet->drag_range.col0 - sheet->range.col0);
3572       old_range = sheet->range;
3573       sheet->range = sheet->drag_range;
3574       sheet->drag_range = old_range;
3575       g_signal_emit (sheet, sheet_signals[MOVE_RANGE], 0,
3576                      &sheet->drag_range, &sheet->range);
3577       gtk_sheet_select_range (sheet, &sheet->range);
3578     }
3579
3580   if (GTK_SHEET_IN_RESIZE (sheet))
3581     {
3582       GtkSheetRange old_range;
3583       draw_xor_rectangle (sheet, sheet->drag_range);
3584       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_RESIZE);
3585       gdk_display_pointer_ungrab (display, event->time);
3586
3587       gtk_sheet_real_unselect_range (sheet, NULL);
3588
3589       sheet->active_cell.row = sheet->active_cell.row +
3590         (sheet->drag_range.row0 - sheet->range.row0);
3591       sheet->active_cell.col = sheet->active_cell.col +
3592         (sheet->drag_range.col0 - sheet->range.col0);
3593       if (sheet->drag_range.row0 < sheet->range.row0)
3594         sheet->selection_cell.row = sheet->drag_range.row0;
3595       if (sheet->drag_range.rowi >= sheet->range.rowi)
3596         sheet->selection_cell.row = sheet->drag_range.rowi;
3597       if (sheet->drag_range.col0 < sheet->range.col0)
3598         sheet->selection_cell.col = sheet->drag_range.col0;
3599       if (sheet->drag_range.coli >= sheet->range.coli)
3600         sheet->selection_cell.col = sheet->drag_range.coli;
3601       old_range = sheet->range;
3602       sheet->range = sheet->drag_range;
3603       sheet->drag_range = old_range;
3604
3605       if (sheet->state == GTK_STATE_NORMAL) sheet->state = GTK_SHEET_RANGE_SELECTED;
3606       g_signal_emit (sheet, sheet_signals[RESIZE_RANGE], 0,
3607                      &sheet->drag_range, &sheet->range);
3608       gtk_sheet_select_range (sheet, &sheet->range);
3609     }
3610
3611   if (sheet->state == GTK_SHEET_NORMAL && GTK_SHEET_IN_SELECTION (sheet))
3612     {
3613       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
3614       gdk_display_pointer_ungrab (display, event->time);
3615       change_active_cell (sheet, sheet->active_cell.row,
3616                                sheet->active_cell.col);
3617     }
3618
3619   if (GTK_SHEET_IN_SELECTION)
3620     gdk_display_pointer_ungrab (display, event->time);
3621   gtk_grab_remove (GTK_WIDGET (sheet));
3622
3623   GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
3624
3625   return TRUE;
3626 }
3627
3628 \f
3629
3630
3631
3632 /* Shamelessly lifted from gtktooltips */
3633 static gboolean
3634 gtk_sheet_subtitle_paint_window (GtkWidget *tip_window)
3635 {
3636   GtkRequisition req;
3637
3638   gtk_widget_size_request (tip_window, &req);
3639   gtk_paint_flat_box (tip_window->style, tip_window->window,
3640                       GTK_STATE_NORMAL, GTK_SHADOW_OUT,
3641                       NULL, GTK_WIDGET(tip_window), "tooltip",
3642                       0, 0, req.width, req.height);
3643
3644   return FALSE;
3645 }
3646
3647 static void
3648 destroy_hover_window (GtkSheetHoverTitle *h)
3649 {
3650   gtk_widget_destroy (h->window);
3651   g_free (h);
3652 }
3653
3654 static GtkSheetHoverTitle *
3655 create_hover_window (void)
3656 {
3657   GtkSheetHoverTitle *hw = g_malloc (sizeof (*hw));
3658
3659   hw->window = gtk_window_new (GTK_WINDOW_POPUP);
3660
3661 #if GTK_CHECK_VERSION (2, 9, 0)
3662   gtk_window_set_type_hint (GTK_WINDOW (hw->window),
3663                             GDK_WINDOW_TYPE_HINT_TOOLTIP);
3664 #endif
3665
3666   gtk_widget_set_app_paintable (hw->window, TRUE);
3667   gtk_window_set_resizable (GTK_WINDOW (hw->window), FALSE);
3668   gtk_widget_set_name (hw->window, "gtk-tooltips");
3669   gtk_container_set_border_width (GTK_CONTAINER (hw->window), 4);
3670
3671   g_signal_connect (hw->window,
3672                     "expose_event",
3673                     G_CALLBACK (gtk_sheet_subtitle_paint_window),
3674                     NULL);
3675
3676   hw->label = gtk_label_new (NULL);
3677
3678
3679   gtk_label_set_line_wrap (GTK_LABEL (hw->label), TRUE);
3680   gtk_misc_set_alignment (GTK_MISC (hw->label), 0.5, 0.5);
3681
3682   gtk_container_add (GTK_CONTAINER (hw->window), hw->label);
3683
3684   gtk_widget_show (hw->label);
3685
3686   g_signal_connect (hw->window,
3687                     "destroy",
3688                     G_CALLBACK (gtk_widget_destroyed),
3689                     &hw->window);
3690
3691   return hw;
3692 }
3693
3694 #define HOVER_WINDOW_Y_OFFSET 2
3695
3696 static void
3697 show_subtitle (GtkSheet *sheet, gint row, gint column, const gchar *subtitle)
3698 {
3699   gint x, y;
3700   gint px, py;
3701   gint width;
3702
3703   if ( ! subtitle )
3704     return;
3705
3706   gtk_label_set_text (GTK_LABEL (sheet->hover_window->label),
3707                       subtitle);
3708
3709
3710   sheet->hover_window->row = row;
3711   sheet->hover_window->column = column;
3712
3713   gdk_window_get_origin (GTK_WIDGET (sheet)->window, &x, &y);
3714
3715   gtk_widget_get_pointer (GTK_WIDGET (sheet), &px, &py);
3716
3717   gtk_widget_show (sheet->hover_window->window);
3718
3719   width = GTK_WIDGET (sheet->hover_window->label)->allocation.width;
3720
3721   if (row == -1 )
3722     {
3723       x += px;
3724       x -= width / 2;
3725       y += sheet->column_title_area.y;
3726       y += sheet->column_title_area.height;
3727       y += HOVER_WINDOW_Y_OFFSET;
3728     }
3729
3730   if ( column == -1 )
3731     {
3732       y += py;
3733       x += sheet->row_title_area.x;
3734       x += sheet->row_title_area.width * 2 / 3.0;
3735     }
3736
3737   gtk_window_move (GTK_WINDOW (sheet->hover_window->window),
3738                    x, y);
3739 }
3740
3741 static gboolean
3742 motion_timeout_callback (gpointer data)
3743 {
3744   GtkSheet *sheet = GTK_SHEET (data);
3745   gint x, y;
3746   gint row, column;
3747   gtk_widget_get_pointer (GTK_WIDGET (sheet), &x, &y);
3748
3749   if ( gtk_sheet_get_pixel_info (sheet, x, y, &row, &column) )
3750     {
3751       if (sheet->row_title_under)
3752         {
3753           GSheetRow *row_geo = sheet->row_geometry;
3754           gchar *text;
3755
3756           text = g_sheet_row_get_subtitle (row_geo, row);
3757
3758           show_subtitle (sheet, row, -1, text);
3759           g_free (text);
3760         }
3761
3762       if (sheet->column_title_under)
3763         {
3764           GSheetColumn *col_geo = sheet->column_geometry;
3765           gchar *text;
3766
3767           text = g_sheet_column_get_subtitle (col_geo, column);
3768
3769           show_subtitle (sheet, -1, column, text);
3770
3771           g_free (text);
3772         }
3773     }
3774
3775   return FALSE;
3776 }
3777
3778 static gboolean
3779 gtk_sheet_motion (GtkWidget *widget,  GdkEventMotion *event)
3780 {
3781   GtkSheet *sheet;
3782   GdkModifierType mods;
3783   GdkCursorType new_cursor;
3784   gint x, y;
3785   gint row, column;
3786   GdkDisplay *display;
3787
3788   g_return_val_if_fail (widget != NULL, FALSE);
3789   g_return_val_if_fail (GTK_IS_SHEET (widget), FALSE);
3790   g_return_val_if_fail (event != NULL, FALSE);
3791
3792   sheet = GTK_SHEET (widget);
3793
3794   display = gtk_widget_get_display (widget);
3795
3796   /* selections on the sheet */
3797   x = event->x;
3798   y = event->y;
3799
3800   if (!GTK_WIDGET_VISIBLE (sheet->hover_window->window))
3801     {
3802       if ( sheet->motion_timer > 0 )
3803         g_source_remove (sheet->motion_timer);
3804       sheet->motion_timer =
3805         g_timeout_add (TIMEOUT_HOVER, motion_timeout_callback, sheet);
3806     }
3807   else
3808     {
3809       gint row, column;
3810       gint wx, wy;
3811       gtk_widget_get_pointer (widget, &wx, &wy);
3812
3813       if ( gtk_sheet_get_pixel_info (sheet, wx, wy, &row, &column) )
3814         {
3815           if ( row != sheet->hover_window->row ||
3816                column != sheet->hover_window->column)
3817             {
3818               gtk_widget_hide (sheet->hover_window->window);
3819             }
3820         }
3821     }
3822
3823   if (event->window == sheet->column_title_window &&
3824       gtk_sheet_columns_resizable (sheet))
3825     {
3826       if (!GTK_SHEET_IN_SELECTION (sheet) &&
3827           on_column_boundary (sheet, x, &column))
3828         {
3829           new_cursor = GDK_SB_H_DOUBLE_ARROW;
3830           if (new_cursor != sheet->cursor_drag->type)
3831             {
3832               gdk_cursor_unref (sheet->cursor_drag);
3833               sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_SB_H_DOUBLE_ARROW);
3834               gdk_window_set_cursor (sheet->column_title_window,
3835                                      sheet->cursor_drag);
3836             }
3837         }
3838       else
3839         {
3840           new_cursor = GDK_TOP_LEFT_ARROW;
3841           if (!GTK_SHEET_IN_XDRAG (sheet) &&
3842               new_cursor != sheet->cursor_drag->type)
3843             {
3844               gdk_cursor_unref (sheet->cursor_drag);
3845               sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
3846               gdk_window_set_cursor (sheet->column_title_window,
3847                                      sheet->cursor_drag);
3848             }
3849         }
3850     }
3851
3852   if (event->window == sheet->row_title_window &&
3853       gtk_sheet_rows_resizable (sheet))
3854     {
3855       if (!GTK_SHEET_IN_SELECTION (sheet) && POSSIBLE_YDRAG (sheet, y, &column))
3856         {
3857           new_cursor = GDK_SB_V_DOUBLE_ARROW;
3858           if (new_cursor != sheet->cursor_drag->type)
3859             {
3860               gdk_cursor_unref (sheet->cursor_drag);
3861               sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_SB_V_DOUBLE_ARROW);
3862               gdk_window_set_cursor (sheet->row_title_window, sheet->cursor_drag);
3863             }
3864         }
3865       else
3866         {
3867           new_cursor = GDK_TOP_LEFT_ARROW;
3868           if (!GTK_SHEET_IN_YDRAG (sheet) &&
3869               new_cursor != sheet->cursor_drag->type)
3870             {
3871               gdk_cursor_unref (sheet->cursor_drag);
3872               sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
3873               gdk_window_set_cursor (sheet->row_title_window, sheet->cursor_drag);
3874             }
3875         }
3876     }
3877
3878   new_cursor = GDK_PLUS;
3879   if ( event->window == sheet->sheet_window &&
3880        !POSSIBLE_DRAG (sheet, x, y, &row, &column) &&
3881        !GTK_SHEET_IN_DRAG (sheet) &&
3882        !POSSIBLE_RESIZE (sheet, x, y, &row, &column) &&
3883        !GTK_SHEET_IN_RESIZE (sheet) &&
3884        new_cursor != sheet->cursor_drag->type)
3885     {
3886       gdk_cursor_unref (sheet->cursor_drag);
3887       sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
3888       gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3889     }
3890
3891   new_cursor = GDK_TOP_LEFT_ARROW;
3892   if ( event->window == sheet->sheet_window &&
3893        ! (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
3894           GTK_SHEET_IN_RESIZE (sheet)) &&
3895        (POSSIBLE_DRAG (sheet, x, y, &row, &column) ||
3896         GTK_SHEET_IN_DRAG (sheet)) &&
3897        new_cursor != sheet->cursor_drag->type)
3898     {
3899       gdk_cursor_unref (sheet->cursor_drag);
3900       sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
3901       gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3902     }
3903
3904   new_cursor = GDK_SIZING;
3905   if ( event->window == sheet->sheet_window &&
3906        sheet->selection_mode != GTK_SELECTION_NONE &&
3907        !GTK_SHEET_IN_DRAG (sheet) &&
3908        (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
3909         GTK_SHEET_IN_RESIZE (sheet)) &&
3910        new_cursor != sheet->cursor_drag->type)
3911     {
3912       gdk_cursor_unref (sheet->cursor_drag);
3913       sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_SIZING);
3914       gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3915     }
3916
3917
3918   gdk_window_get_pointer (widget->window, &x, &y, &mods);
3919   if (! (mods & GDK_BUTTON1_MASK)) return FALSE;
3920
3921   if (GTK_SHEET_IN_XDRAG (sheet))
3922     {
3923       x = event->x;
3924
3925       new_column_width (sheet, sheet->drag_cell.col, &x);
3926 #if 0
3927       if (x != sheet->x_drag)
3928         {
3929           draw_xor_vline (sheet);
3930           sheet->x_drag = x;
3931           draw_xor_vline (sheet);
3932         }
3933 #endif
3934       return TRUE;
3935     }
3936
3937   if (GTK_SHEET_IN_YDRAG (sheet))
3938     {
3939       if (event->is_hint || event->window != widget->window)
3940         gtk_widget_get_pointer (widget, NULL, &y);
3941       else
3942         y = event->y;
3943
3944       new_row_height (sheet, sheet->drag_cell.row, &y);
3945       if (y != sheet->y_drag)
3946         {
3947           draw_xor_hline (sheet);
3948           sheet->y_drag = y;
3949           draw_xor_hline (sheet);
3950         }
3951       return TRUE;
3952     }
3953
3954   if (GTK_SHEET_IN_DRAG (sheet))
3955     {
3956       GtkSheetRange aux;
3957       column = column_from_xpixel (sheet, x)- sheet->drag_cell.col;
3958       row = yyy_row_ypixel_to_row (sheet, y) - sheet->drag_cell.row;
3959       if (sheet->state == GTK_SHEET_COLUMN_SELECTED) row = 0;
3960       if (sheet->state == GTK_SHEET_ROW_SELECTED) column = 0;
3961       sheet->x_drag = x;
3962       sheet->y_drag = y;
3963       aux = sheet->range;
3964       if (aux.row0 + row >= 0 && aux.rowi + row < g_sheet_row_get_row_count (sheet->row_geometry) &&
3965           aux.col0 + column >= 0 && aux.coli + column < g_sheet_column_get_column_count (sheet->column_geometry))
3966         {
3967           aux = sheet->drag_range;
3968           sheet->drag_range.row0 = sheet->range.row0 + row;
3969           sheet->drag_range.col0 = sheet->range.col0 + column;
3970           sheet->drag_range.rowi = sheet->range.rowi + row;
3971           sheet->drag_range.coli = sheet->range.coli + column;
3972           if (aux.row0 != sheet->drag_range.row0 ||
3973               aux.col0 != sheet->drag_range.col0)
3974             {
3975               draw_xor_rectangle (sheet, aux);
3976               draw_xor_rectangle (sheet, sheet->drag_range);
3977             }
3978         }
3979       return TRUE;
3980     }
3981
3982   if (GTK_SHEET_IN_RESIZE (sheet))
3983     {
3984       GtkSheetRange aux;
3985       gint v_h, current_col, current_row, col_threshold, row_threshold;
3986       v_h = 1;
3987       if (abs (x - g_sheet_column_start_pixel (sheet->column_geometry, sheet->drag_cell.col)) >
3988           abs (y - g_sheet_row_start_pixel (sheet->row_geometry, sheet->drag_cell.row))) v_h = 2;
3989
3990       current_col = column_from_xpixel (sheet, x);
3991       current_row = yyy_row_ypixel_to_row (sheet, y);
3992       column = current_col - sheet->drag_cell.col;
3993       row = current_row - sheet->drag_cell.row;
3994
3995       /*use half of column width resp. row height as threshold to
3996         expand selection*/
3997       col_threshold = g_sheet_column_start_pixel (sheet->column_geometry, current_col) +
3998         g_sheet_column_get_width (sheet->column_geometry, current_col) / 2;
3999       if (column > 0)
4000         {
4001           if (x < col_threshold)
4002             column -= 1;
4003         }
4004       else if (column < 0)
4005         {
4006           if (x > col_threshold)
4007             column +=1;
4008         }
4009       row_threshold = g_sheet_row_start_pixel (sheet->row_geometry, current_row) +
4010         g_sheet_row_get_height (sheet->row_geometry, current_row)/2;
4011       if (row > 0)
4012         {
4013           if (y < row_threshold)
4014             row -= 1;
4015         }
4016       else if (row < 0)
4017         {
4018           if (y > row_threshold)
4019             row +=1;
4020         }
4021
4022       if (sheet->state == GTK_SHEET_COLUMN_SELECTED) row = 0;
4023       if (sheet->state == GTK_SHEET_ROW_SELECTED) column = 0;
4024       sheet->x_drag = x;
4025       sheet->y_drag = y;
4026       aux = sheet->range;
4027
4028       if (v_h == 1)
4029         column = 0;
4030       else
4031         row = 0;
4032
4033       if (aux.row0 + row >= 0 && aux.rowi + row < g_sheet_row_get_row_count (sheet->row_geometry) &&
4034           aux.col0 + column >= 0 && aux.coli + column < g_sheet_column_get_column_count (sheet->column_geometry))
4035         {
4036           aux = sheet->drag_range;
4037           sheet->drag_range = sheet->range;
4038
4039           if (row < 0) sheet->drag_range.row0 = sheet->range.row0 + row;
4040           if (row > 0) sheet->drag_range.rowi = sheet->range.rowi + row;
4041           if (column < 0) sheet->drag_range.col0 = sheet->range.col0 + column;
4042           if (column > 0) sheet->drag_range.coli = sheet->range.coli + column;
4043
4044           if (aux.row0 != sheet->drag_range.row0 ||
4045               aux.rowi != sheet->drag_range.rowi ||
4046               aux.col0 != sheet->drag_range.col0 ||
4047               aux.coli != sheet->drag_range.coli)
4048             {
4049               draw_xor_rectangle (sheet, aux);
4050               draw_xor_rectangle (sheet, sheet->drag_range);
4051             }
4052         }
4053       return TRUE;
4054     }
4055
4056   gtk_sheet_get_pixel_info (sheet, x, y, &row, &column);
4057
4058   if (sheet->state == GTK_SHEET_NORMAL && row == sheet->active_cell.row &&
4059       column == sheet->active_cell.col) return TRUE;
4060
4061   if (GTK_SHEET_IN_SELECTION (sheet) && mods&GDK_BUTTON1_MASK)
4062     gtk_sheet_extend_selection (sheet, row, column);
4063
4064   return TRUE;
4065 }
4066
4067 static gboolean
4068 gtk_sheet_crossing_notify (GtkWidget *widget,
4069                            GdkEventCrossing *event)
4070 {
4071   GtkSheet *sheet = GTK_SHEET (widget);
4072
4073   if (event->window == sheet->column_title_window)
4074     sheet->column_title_under = event->type == GDK_ENTER_NOTIFY;
4075   else if (event->window == sheet->row_title_window)
4076     sheet->row_title_under = event->type == GDK_ENTER_NOTIFY;
4077
4078   return TRUE;
4079 }
4080
4081 static void
4082 gtk_sheet_extend_selection (GtkSheet *sheet, gint row, gint column)
4083 {
4084   GtkSheetRange range;
4085   gint state;
4086   gint r, c;
4087
4088   if (row == sheet->selection_cell.row && column == sheet->selection_cell.col)
4089     return;
4090
4091   if (sheet->selection_mode == GTK_SELECTION_SINGLE) return;
4092
4093   gtk_widget_grab_focus (GTK_WIDGET (sheet));
4094
4095   if (GTK_SHEET_IN_DRAG (sheet)) return;
4096
4097   state = sheet->state;
4098
4099   switch (sheet->state)
4100     {
4101     case GTK_SHEET_ROW_SELECTED:
4102       column = g_sheet_column_get_column_count (sheet->column_geometry) - 1;
4103       break;
4104     case GTK_SHEET_COLUMN_SELECTED:
4105       row = g_sheet_row_get_row_count (sheet->row_geometry) - 1;
4106       break;
4107     case GTK_SHEET_NORMAL:
4108       sheet->state = GTK_SHEET_RANGE_SELECTED;
4109       r = sheet->active_cell.row;
4110       c = sheet->active_cell.col;
4111       sheet->range.col0 = c;
4112       sheet->range.row0 = r;
4113       sheet->range.coli = c;
4114       sheet->range.rowi = r;
4115       gtk_sheet_range_draw_selection (sheet, sheet->range);
4116     case GTK_SHEET_RANGE_SELECTED:
4117       sheet->state = GTK_SHEET_RANGE_SELECTED;
4118     }
4119
4120   sheet->selection_cell.row = row;
4121   sheet->selection_cell.col = column;
4122
4123   range.col0 = MIN (column, sheet->active_cell.col);
4124   range.coli = MAX (column, sheet->active_cell.col);
4125   range.row0 = MIN (row, sheet->active_cell.row);
4126   range.rowi = MAX (row, sheet->active_cell.row);
4127
4128   if (range.row0 != sheet->range.row0 || range.rowi != sheet->range.rowi ||
4129       range.col0 != sheet->range.col0 || range.coli != sheet->range.coli ||
4130       state == GTK_SHEET_NORMAL)
4131     gtk_sheet_real_select_range (sheet, &range);
4132
4133 }
4134
4135 static gint
4136 gtk_sheet_entry_key_press (GtkWidget *widget,
4137                            GdkEventKey *key)
4138 {
4139   gboolean focus;
4140   g_signal_emit_by_name (widget, "key_press_event", key, &focus);
4141   return focus;
4142 }
4143
4144
4145 /* Number of rows in a step-increment */
4146 #define ROWS_PER_STEP 1
4147
4148
4149 static void
4150 page_vertical (GtkSheet *sheet, GtkScrollType dir)
4151 {
4152   gint old_row = sheet->active_cell.row ;
4153   glong vpixel = g_sheet_row_start_pixel (sheet->row_geometry, old_row);
4154
4155   gint new_row;
4156
4157   vpixel -= g_sheet_row_start_pixel (sheet->row_geometry,
4158                                      min_visible_row (sheet));
4159
4160   switch ( dir)
4161     {
4162     case GTK_SCROLL_PAGE_DOWN:
4163       gtk_adjustment_set_value (sheet->vadjustment,
4164                                 sheet->vadjustment->value +
4165                                 sheet->vadjustment->page_increment);
4166       break;
4167     case GTK_SCROLL_PAGE_UP:
4168       gtk_adjustment_set_value (sheet->vadjustment,
4169                                 sheet->vadjustment->value -
4170                                 sheet->vadjustment->page_increment);
4171
4172       break;
4173     default:
4174       g_assert_not_reached ();
4175       break;
4176     }
4177
4178
4179   vpixel += g_sheet_row_start_pixel (sheet->row_geometry,
4180                                      min_visible_row (sheet));
4181
4182   new_row =  yyy_row_ypixel_to_row (sheet, vpixel);
4183
4184   change_active_cell (sheet, new_row,
4185                            sheet->active_cell.col);
4186 }
4187
4188
4189 static void
4190 step_sheet (GtkSheet *sheet, GtkScrollType dir)
4191 {
4192   gint current_row = sheet->active_cell.row;
4193   gint current_col = sheet->active_cell.col;
4194   GtkSheetCell new_cell ;
4195   gboolean forbidden = FALSE;
4196
4197   new_cell.row = current_row;
4198   new_cell.col = current_col;
4199
4200   switch ( dir)
4201     {
4202     case GTK_SCROLL_STEP_DOWN:
4203       new_cell.row++;
4204       break;
4205     case GTK_SCROLL_STEP_UP:
4206       new_cell.row--;
4207       break;
4208     case GTK_SCROLL_STEP_RIGHT:
4209       new_cell.col++;
4210       break;
4211     case GTK_SCROLL_STEP_LEFT:
4212       new_cell.col--;
4213       break;
4214     default:
4215       g_assert_not_reached ();
4216       break;
4217     }
4218
4219
4220   maximize_int (&new_cell.row, 0);
4221   maximize_int (&new_cell.col, 0);
4222
4223   minimize_int (&new_cell.row,
4224                 g_sheet_row_get_row_count (sheet->row_geometry) - 1);
4225
4226   minimize_int (&new_cell.col,
4227                 g_sheet_column_get_column_count (sheet->column_geometry) - 1);
4228
4229   g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
4230                  &sheet->active_cell,
4231                  &new_cell,
4232                 &forbidden);
4233
4234   if (forbidden)
4235     return;
4236
4237   change_active_cell (sheet, new_cell.row, new_cell.col);
4238
4239   if ( new_cell.col > max_fully_visible_column (sheet))
4240     {
4241       glong hpos  =
4242         g_sheet_column_start_pixel (sheet->column_geometry,
4243                                     new_cell.col + 1);
4244       hpos -= sheet->hadjustment->page_size;
4245
4246       gtk_adjustment_set_value (sheet->hadjustment,
4247                                 hpos);
4248     }
4249   else if ( new_cell.col < min_fully_visible_column (sheet))
4250     {
4251       glong hpos  =
4252         g_sheet_column_start_pixel (sheet->column_geometry,
4253                                     new_cell.col);
4254
4255       gtk_adjustment_set_value (sheet->hadjustment,
4256                                 hpos);
4257     }
4258
4259
4260   if ( new_cell.row > max_fully_visible_row (sheet))
4261     {
4262       glong vpos  =
4263         g_sheet_row_start_pixel (sheet->row_geometry,
4264                                     new_cell.row + 1);
4265       vpos -= sheet->vadjustment->page_size;
4266
4267       gtk_adjustment_set_value (sheet->vadjustment,
4268                                 vpos);
4269     }
4270   else if ( new_cell.row < min_fully_visible_row (sheet))
4271     {
4272       glong vpos  =
4273         g_sheet_row_start_pixel (sheet->row_geometry,
4274                                     new_cell.row);
4275
4276       gtk_adjustment_set_value (sheet->vadjustment,
4277                                 vpos);
4278     }
4279 }
4280
4281
4282 static gboolean
4283 gtk_sheet_key_press (GtkWidget *widget,
4284                      GdkEventKey *key)
4285 {
4286   GtkSheet *sheet = GTK_SHEET (widget);
4287
4288   GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
4289
4290   switch (key->keyval)
4291     {
4292     case GDK_Tab:
4293     case GDK_Right:
4294       step_sheet (sheet, GTK_SCROLL_STEP_RIGHT);
4295       break;
4296     case GDK_ISO_Left_Tab:
4297     case GDK_Left:
4298       step_sheet (sheet, GTK_SCROLL_STEP_LEFT);
4299       break;
4300     case GDK_Return:
4301     case GDK_Down:
4302       step_sheet (sheet, GTK_SCROLL_STEP_DOWN);
4303       break;
4304     case GDK_Up:
4305       step_sheet (sheet, GTK_SCROLL_STEP_UP);
4306       break;
4307
4308     case GDK_Page_Down:
4309       page_vertical (sheet, GTK_SCROLL_PAGE_DOWN);
4310       break;
4311     case GDK_Page_Up:
4312       page_vertical (sheet, GTK_SCROLL_PAGE_UP);
4313       break;
4314
4315     case GDK_Home:
4316       gtk_adjustment_set_value (sheet->vadjustment,
4317                                 sheet->vadjustment->lower);
4318
4319       change_active_cell (sheet,  0,
4320                                sheet->active_cell.col);
4321
4322       break;
4323
4324     case GDK_End:
4325       gtk_adjustment_set_value (sheet->vadjustment,
4326                                 sheet->vadjustment->upper -
4327                                 sheet->vadjustment->page_size -
4328                                 sheet->vadjustment->page_increment);
4329
4330       /*
4331         change_active_cellx (sheet,
4332         g_sheet_row_get_row_count (sheet->row_geometry) - 1,
4333         sheet->active_cell.col);
4334       */
4335       break;
4336     case GDK_Delete:
4337       gtk_sheet_real_cell_clear (sheet, sheet->active_cell.row, sheet->active_cell.col);
4338       break;
4339     default:
4340       return FALSE;
4341       break;
4342     }
4343
4344   return TRUE;
4345 }
4346
4347 static void
4348 gtk_sheet_size_request (GtkWidget *widget,
4349                         GtkRequisition *requisition)
4350 {
4351   GtkSheet *sheet;
4352
4353   g_return_if_fail (widget != NULL);
4354   g_return_if_fail (GTK_IS_SHEET (widget));
4355   g_return_if_fail (requisition != NULL);
4356
4357   sheet = GTK_SHEET (widget);
4358
4359   requisition->width = 3 * DEFAULT_COLUMN_WIDTH;
4360   requisition->height = 3 * DEFAULT_ROW_HEIGHT;
4361
4362   /* compute the size of the column title area */
4363   if (sheet->column_titles_visible)
4364     requisition->height += sheet->column_title_area.height;
4365
4366   /* compute the size of the row title area */
4367   if (sheet->row_titles_visible)
4368     requisition->width += sheet->row_title_area.width;
4369 }
4370
4371
4372 static void
4373 gtk_sheet_size_allocate (GtkWidget *widget,
4374                          GtkAllocation *allocation)
4375 {
4376   GtkSheet *sheet;
4377   GtkAllocation sheet_allocation;
4378   gint border_width;
4379
4380   g_return_if_fail (widget != NULL);
4381   g_return_if_fail (GTK_IS_SHEET (widget));
4382   g_return_if_fail (allocation != NULL);
4383
4384   sheet = GTK_SHEET (widget);
4385   widget->allocation = *allocation;
4386   border_width = GTK_CONTAINER (widget)->border_width;
4387
4388   if (GTK_WIDGET_REALIZED (widget))
4389     gdk_window_move_resize (widget->window,
4390                             allocation->x + border_width,
4391                             allocation->y + border_width,
4392                             allocation->width - 2 * border_width,
4393                             allocation->height - 2 * border_width);
4394
4395   /* use internal allocation structure for all the math
4396    * because it's easier than always subtracting the container
4397    * border width */
4398   sheet->internal_allocation.x = 0;
4399   sheet->internal_allocation.y = 0;
4400   sheet->internal_allocation.width = allocation->width - 2 * border_width;
4401   sheet->internal_allocation.height = allocation->height - 2 * border_width;
4402
4403   sheet_allocation.x = 0;
4404   sheet_allocation.y = 0;
4405   sheet_allocation.width = allocation->width - 2 * border_width;
4406   sheet_allocation.height = allocation->height - 2 * border_width;
4407
4408   if (GTK_WIDGET_REALIZED (widget))
4409     gdk_window_move_resize (sheet->sheet_window,
4410                             sheet_allocation.x,
4411                             sheet_allocation.y,
4412                             sheet_allocation.width,
4413                             sheet_allocation.height);
4414
4415   /* position the window which holds the column title buttons */
4416   sheet->column_title_area.x = 0;
4417   sheet->column_title_area.y = 0;
4418
4419   if (sheet->row_titles_visible)
4420     {
4421       sheet->column_title_area.x = sheet->row_title_area.width;
4422     }
4423
4424   sheet->column_title_area.width = sheet_allocation.width ;
4425
4426
4427   if (GTK_WIDGET_REALIZED (widget) && sheet->column_titles_visible)
4428     gdk_window_move_resize (sheet->column_title_window,
4429                             sheet->column_title_area.x,
4430                             sheet->column_title_area.y,
4431                             sheet->column_title_area.width,
4432                             sheet->column_title_area.height);
4433
4434
4435   /* column button allocation */
4436   draw_column_title_buttons (sheet);
4437
4438   /* position the window which holds the row title buttons */
4439   sheet->row_title_area.x = 0;
4440   sheet->row_title_area.y = 0;
4441   if (sheet->column_titles_visible)
4442     {
4443       sheet->row_title_area.y = sheet->column_title_area.height;
4444     }
4445
4446   sheet->row_title_area.height = sheet_allocation.height -
4447     sheet->row_title_area.y;
4448
4449   if (GTK_WIDGET_REALIZED (widget) && sheet->row_titles_visible)
4450     gdk_window_move_resize (sheet->row_title_window,
4451                             sheet->row_title_area.x,
4452                             sheet->row_title_area.y,
4453                             sheet->row_title_area.width,
4454                             sheet->row_title_area.height);
4455
4456
4457   /* row button allocation */
4458   draw_row_title_buttons (sheet);
4459   draw_column_title_buttons (sheet);
4460
4461   /* set the scrollbars adjustments */
4462   adjust_scrollbars (sheet);
4463 }
4464
4465 static void
4466 draw_column_title_buttons (GtkSheet *sheet)
4467 {
4468   gint x, width;
4469
4470   if (!sheet->column_titles_visible) return;
4471   if (!GTK_WIDGET_REALIZED (sheet))
4472     return;
4473
4474   gdk_drawable_get_size (sheet->sheet_window, &width, NULL);
4475   x = 0;
4476
4477   if (sheet->row_titles_visible)
4478     {
4479       x = sheet->row_title_area.width;
4480     }
4481
4482   if (sheet->column_title_area.width != width || sheet->column_title_area.x != x)
4483     {
4484       sheet->column_title_area.width = width;
4485       sheet->column_title_area.x = x;
4486       gdk_window_move_resize (sheet->column_title_window,
4487                               sheet->column_title_area.x,
4488                               sheet->column_title_area.y,
4489                               sheet->column_title_area.width,
4490                               sheet->column_title_area.height);
4491     }
4492
4493   if (max_visible_column (sheet) ==
4494       g_sheet_column_get_column_count (sheet->column_geometry) - 1)
4495     gdk_window_clear_area (sheet->column_title_window,
4496                            0, 0,
4497                            sheet->column_title_area.width,
4498                            sheet->column_title_area.height);
4499
4500   if (!GTK_WIDGET_DRAWABLE (sheet)) return;
4501
4502   size_allocate_global_button (sheet);
4503
4504   draw_column_title_buttons_range (sheet, min_visible_column (sheet), 
4505                                    max_visible_column (sheet));
4506 }
4507
4508 static void
4509 draw_row_title_buttons (GtkSheet *sheet)
4510 {
4511   gint y = 0;
4512   gint height;
4513
4514   if (!sheet->row_titles_visible) return;
4515   if (!GTK_WIDGET_REALIZED (sheet))
4516     return;
4517
4518   gdk_drawable_get_size (sheet->sheet_window, NULL, &height);
4519
4520   if (sheet->column_titles_visible)
4521     {
4522       y = sheet->column_title_area.height;
4523     }
4524
4525   if (sheet->row_title_area.height != height || sheet->row_title_area.y != y)
4526     {
4527       sheet->row_title_area.y = y;
4528       sheet->row_title_area.height = height;
4529       gdk_window_move_resize (sheet->row_title_window,
4530                               sheet->row_title_area.x,
4531                               sheet->row_title_area.y,
4532                               sheet->row_title_area.width,
4533                               sheet->row_title_area.height);
4534     }
4535
4536   if (max_visible_row (sheet) == g_sheet_row_get_row_count (sheet->row_geometry) - 1)
4537     gdk_window_clear_area (sheet->row_title_window,
4538                            0, 0,
4539                            sheet->row_title_area.width,
4540                            sheet->row_title_area.height);
4541
4542   if (!GTK_WIDGET_DRAWABLE (sheet)) return;
4543
4544   size_allocate_global_button (sheet);
4545
4546
4547   draw_row_title_buttons_range (sheet, min_visible_row (sheet),
4548                                 max_visible_row (sheet));
4549 }
4550
4551
4552 static void
4553 gtk_sheet_size_allocate_entry (GtkSheet *sheet)
4554 {
4555   GtkAllocation entry_alloc;
4556   GtkSheetCellAttr attributes = { 0 };
4557   GtkEntry *sheet_entry;
4558
4559
4560   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4561   if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
4562
4563   sheet_entry = GTK_ENTRY (gtk_sheet_get_entry (sheet));
4564
4565   if ( ! gtk_sheet_get_attributes (sheet, sheet->active_cell.row,
4566                                    sheet->active_cell.col,
4567                                    &attributes) )
4568     return ;
4569
4570   gtk_widget_ensure_style (GTK_WIDGET (sheet_entry));
4571
4572   if ( GTK_WIDGET_REALIZED (sheet->entry_widget) )
4573     {
4574       GtkStyle *style = GTK_WIDGET (sheet_entry)->style;
4575
4576       style->bg[GTK_STATE_NORMAL] = attributes.background;
4577       style->fg[GTK_STATE_NORMAL] = attributes.foreground;
4578       style->text[GTK_STATE_NORMAL] = attributes.foreground;
4579       style->bg[GTK_STATE_ACTIVE] = attributes.background;
4580       style->fg[GTK_STATE_ACTIVE] = attributes.foreground;
4581       style->text[GTK_STATE_ACTIVE] = attributes.foreground;
4582
4583       pango_font_description_free (style->font_desc);
4584       g_assert (attributes.font_desc);
4585       style->font_desc = pango_font_description_copy (attributes.font_desc);
4586     }
4587
4588   rectangle_from_cell (sheet, sheet->active_cell.row,
4589                        sheet->active_cell.col, &entry_alloc);
4590
4591   gtk_widget_set_size_request (sheet->entry_widget, entry_alloc.width,
4592                                entry_alloc.height);
4593   gtk_widget_size_allocate (sheet->entry_widget, &entry_alloc);
4594 }
4595
4596
4597 static void
4598 create_sheet_entry (GtkSheet *sheet)
4599 {
4600   if (sheet->entry_widget)
4601     {
4602       gtk_widget_unparent (sheet->entry_widget);
4603     }
4604
4605   if (sheet->entry_type)
4606     {
4607       sheet->entry_container = g_object_new (sheet->entry_type, NULL);
4608       g_object_ref_sink (sheet->entry_container);
4609       sheet->entry_widget = gtk_sheet_get_entry (sheet);
4610
4611       if  ( NULL == sheet->entry_widget)
4612         {
4613           g_warning ("Entry type is %s. It must be GtkEntry subclass, or a widget containing one. "
4614                      "Using default", g_type_name (sheet->entry_type));
4615           g_object_unref (sheet->entry_container);
4616           sheet->entry_widget = sheet->entry_container = gtk_entry_new ();
4617         }
4618       else
4619         {
4620           sheet->entry_widget = sheet->entry_container ;
4621         }
4622     }
4623   else
4624     {
4625       sheet->entry_widget = sheet->entry_container = gtk_entry_new ();
4626       g_object_ref_sink (sheet->entry_container);
4627     }
4628
4629   gtk_widget_size_request (sheet->entry_widget, NULL);
4630
4631   if (GTK_WIDGET_REALIZED (sheet))
4632     {
4633       gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
4634       gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
4635       gtk_widget_realize (sheet->entry_widget);
4636     }
4637
4638   g_signal_connect_swapped (sheet->entry_widget, "key_press_event",
4639                             G_CALLBACK (gtk_sheet_entry_key_press),
4640                             sheet);
4641
4642   sheet->entry_handler_id =
4643     g_signal_connect (sheet->entry_widget,
4644                       "changed",
4645                       G_CALLBACK (gtk_sheet_entry_changed),
4646                       sheet);
4647
4648   gtk_widget_show (sheet->entry_widget);
4649 }
4650
4651
4652 /* Finds the last child widget that happens to be of type GtkEntry */
4653 static void
4654 find_entry (GtkWidget *w, gpointer user_data)
4655 {
4656   GtkWidget **entry = user_data;
4657   if ( GTK_IS_ENTRY (w))
4658     {
4659       *entry = w;
4660     }
4661 }
4662
4663 GtkWidget *
4664 gtk_sheet_get_entry (GtkSheet *sheet)
4665 {
4666   GtkWidget *parent;
4667   GtkWidget *entry = NULL;
4668
4669   g_return_val_if_fail (sheet != NULL, NULL);
4670   g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);
4671   g_return_val_if_fail (sheet->entry_widget != NULL, NULL);
4672
4673   if (GTK_IS_ENTRY (sheet->entry_container))
4674     return (sheet->entry_container);
4675
4676   parent = sheet->entry_container;
4677
4678   if (GTK_IS_CONTAINER (parent))
4679     {
4680       gtk_container_forall (GTK_CONTAINER (parent), find_entry, &entry);
4681
4682       if (GTK_IS_ENTRY (entry))
4683         return entry;
4684     }
4685
4686   if (!GTK_IS_ENTRY (entry)) return NULL;
4687
4688   return (entry);
4689
4690 }
4691
4692 GtkWidget *
4693 gtk_sheet_get_entry_widget (GtkSheet *sheet)
4694 {
4695   g_return_val_if_fail (sheet != NULL, NULL);
4696   g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);
4697   g_return_val_if_fail (sheet->entry_widget != NULL, NULL);
4698
4699   return (sheet->entry_widget);
4700 }
4701
4702
4703 static void
4704 draw_button (GtkSheet *sheet, GdkWindow *window,
4705                        GtkSheetButton *button, gboolean is_sensitive,
4706                        GdkRectangle allocation)
4707 {
4708   GtkShadowType shadow_type;
4709   gint text_width = 0, text_height = 0;
4710   PangoAlignment align = PANGO_ALIGN_LEFT;
4711
4712   gboolean rtl ;
4713
4714   gint state = 0;
4715
4716   g_return_if_fail (sheet != NULL);
4717   g_return_if_fail (button != NULL);
4718
4719
4720   rtl = gtk_widget_get_direction (GTK_WIDGET (sheet)) == GTK_TEXT_DIR_RTL;
4721
4722   gdk_window_clear_area (window,
4723                          allocation.x, allocation.y,
4724                          allocation.width, allocation.height);
4725
4726   gtk_paint_box (sheet->button->style, window,
4727                  GTK_STATE_NORMAL, GTK_SHADOW_OUT,
4728                  &allocation, GTK_WIDGET (sheet->button),
4729                  "buttondefault",
4730                  allocation.x, allocation.y,
4731                  allocation.width, allocation.height);
4732
4733   state = button->state;
4734   if (!is_sensitive) state = GTK_STATE_INSENSITIVE;
4735
4736   if (state == GTK_STATE_ACTIVE)
4737     shadow_type = GTK_SHADOW_IN;
4738   else
4739     shadow_type = GTK_SHADOW_OUT;
4740
4741   if (state != GTK_STATE_NORMAL && state != GTK_STATE_INSENSITIVE)
4742     gtk_paint_box (sheet->button->style, window,
4743                    button->state, shadow_type,
4744                    &allocation, GTK_WIDGET (sheet->button),
4745                    "button",
4746                    allocation.x, allocation.y,
4747                    allocation.width, allocation.height);
4748
4749   if (button->label_visible)
4750     {
4751       text_height = DEFAULT_ROW_HEIGHT -
4752         2 * COLUMN_TITLES_HEIGHT;
4753
4754       gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4755                                  &allocation);
4756       gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc,
4757                                  &allocation);
4758
4759       allocation.y += 2 * sheet->button->style->ythickness;
4760
4761       if (button->label && strlen (button->label)>0)
4762         {
4763           PangoRectangle rect;
4764           gchar *line = button->label;
4765
4766           PangoLayout *layout = NULL;
4767           gint real_x = allocation.x;
4768           gint real_y = allocation.y;
4769
4770           layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), line);
4771           pango_layout_get_extents (layout, NULL, &rect);
4772
4773           text_width = PANGO_PIXELS (rect.width);
4774           switch (button->justification)
4775             {
4776             case GTK_JUSTIFY_LEFT:
4777               real_x = allocation.x + COLUMN_TITLES_HEIGHT;
4778               align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4779               break;
4780             case GTK_JUSTIFY_RIGHT:
4781               real_x = allocation.x + allocation.width - text_width - COLUMN_TITLES_HEIGHT;
4782               align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
4783               break;
4784             case GTK_JUSTIFY_CENTER:
4785             default:
4786               real_x = allocation.x + (allocation.width - text_width)/2;
4787               align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4788               pango_layout_set_justify (layout, TRUE);
4789             }
4790           pango_layout_set_alignment (layout, align);
4791           gtk_paint_layout (GTK_WIDGET (sheet)->style,
4792                             window,
4793                             state,
4794                             FALSE,
4795                             &allocation,
4796                             GTK_WIDGET (sheet),
4797                             "label",
4798                             real_x, real_y,
4799                             layout);
4800           g_object_unref (layout);
4801         }
4802
4803       gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4804                                  NULL);
4805       gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc, NULL);
4806
4807     }
4808
4809   gtk_sheet_button_free (button);
4810 }
4811
4812
4813 /* Draw the column title buttons FIRST through to LAST */
4814 static void
4815 draw_column_title_buttons_range (GtkSheet *sheet, gint first, gint last)
4816 {
4817   GdkRegion *region;
4818   gint col;
4819   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4820
4821   if (!sheet->column_titles_visible) return;
4822
4823   g_return_if_fail (first >= min_visible_column (sheet));
4824   g_return_if_fail (last <= max_visible_column (sheet));
4825
4826   region =
4827     gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->column_title_window));
4828
4829   gdk_window_begin_paint_region (sheet->column_title_window, region);
4830
4831   for (col = first ; col <= last ; ++col)
4832     {
4833       GdkRectangle allocation;
4834       gboolean is_sensitive = FALSE;
4835
4836       GtkSheetButton *
4837         button = g_sheet_column_get_button (sheet->column_geometry, col);
4838       allocation.y = 0;
4839       allocation.x =
4840         g_sheet_column_start_pixel (sheet->column_geometry, col)
4841         + CELL_SPACING;
4842       allocation.x -= sheet->hadjustment->value;
4843
4844       allocation.height = sheet->column_title_area.height;
4845       allocation.width =
4846         g_sheet_column_get_width (sheet->column_geometry, col);
4847       is_sensitive =
4848         g_sheet_column_get_sensitivity (sheet->column_geometry, col);
4849
4850       draw_button (sheet, sheet->column_title_window,
4851                    button, is_sensitive, allocation);
4852     }
4853
4854   gdk_window_end_paint (sheet->column_title_window);
4855 }
4856
4857
4858 static void
4859 draw_row_title_buttons_range (GtkSheet *sheet, gint first, gint last)
4860 {
4861   GdkRegion *region;
4862   gint row;
4863   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4864
4865   if (!sheet->row_titles_visible) return;
4866
4867   g_return_if_fail (first >= min_visible_row (sheet));
4868   g_return_if_fail (last <= max_visible_row (sheet));
4869
4870
4871   region =
4872     gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->row_title_window));
4873
4874   gdk_window_begin_paint_region (sheet->row_title_window, region);
4875
4876
4877   for (row = first; row <= last; ++row)
4878     {
4879       GdkRectangle allocation;
4880
4881       gboolean is_sensitive = FALSE;
4882
4883       GtkSheetButton *button =
4884         g_sheet_row_get_button (sheet->row_geometry, row);
4885       allocation.x = 0;
4886       allocation.y = g_sheet_row_start_pixel (sheet->row_geometry, row)
4887         + CELL_SPACING;
4888       allocation.y -= sheet->vadjustment->value;
4889
4890       allocation.width = sheet->row_title_area.width;
4891       allocation.height = g_sheet_row_get_height (sheet->row_geometry, row);
4892       is_sensitive = g_sheet_row_get_sensitivity (sheet->row_geometry, row);
4893
4894       draw_button (sheet, sheet->row_title_window,
4895                    button, is_sensitive, allocation);
4896     }
4897
4898   gdk_window_end_paint (sheet->row_title_window);
4899 }
4900
4901 /* SCROLLBARS
4902  *
4903  * functions:
4904  * adjust_scrollbars
4905  * vadjustment_value_changed
4906  * hadjustment_value_changed */
4907
4908 static void
4909 adjust_scrollbars (GtkSheet *sheet)
4910 {
4911   gint width, height;
4912
4913   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4914     return;
4915
4916   gdk_drawable_get_size (sheet->sheet_window, &width, &height);
4917
4918   if ( sheet->row_titles_visible)
4919     width -= sheet->row_title_area.width;
4920
4921   if (sheet->column_titles_visible)
4922     height -= sheet->column_title_area.height;
4923
4924   if (sheet->vadjustment)
4925     {
4926       glong last_row = g_sheet_row_get_row_count (sheet->row_geometry) - 1;
4927
4928       sheet->vadjustment->step_increment =
4929         ROWS_PER_STEP *
4930         g_sheet_row_get_height (sheet->row_geometry, last_row);
4931
4932       sheet->vadjustment->page_increment =
4933         height -
4934         sheet->column_title_area.height -
4935         g_sheet_row_get_height (sheet->row_geometry, last_row);
4936
4937
4938
4939       sheet->vadjustment->upper =
4940         g_sheet_row_start_pixel (sheet->row_geometry, last_row)
4941         +
4942         g_sheet_row_get_height (sheet->row_geometry, last_row)
4943         ;
4944
4945       sheet->vadjustment->lower = 0;
4946       sheet->vadjustment->page_size = height;
4947
4948       g_signal_emit_by_name (sheet->vadjustment, "changed");
4949     }
4950
4951   if (sheet->hadjustment)
4952     {
4953       gint last_col;
4954       sheet->hadjustment->step_increment = 1;
4955
4956       sheet->hadjustment->page_increment = width;
4957
4958       last_col = g_sheet_column_get_column_count (sheet->column_geometry) - 1;
4959
4960       sheet->hadjustment->upper =
4961         g_sheet_column_start_pixel (sheet->column_geometry, last_col)
4962         +
4963         g_sheet_column_get_width (sheet->column_geometry, last_col)
4964         ;
4965
4966       sheet->hadjustment->lower = 0;
4967       sheet->hadjustment->page_size = width;
4968
4969       g_signal_emit_by_name (sheet->hadjustment, "changed");
4970     }
4971 }
4972
4973 static void
4974 vadjustment_value_changed (GtkAdjustment *adjustment,
4975                            gpointer data)
4976 {
4977   GdkRegion *region;
4978   GtkSheet *sheet = GTK_SHEET (data);
4979
4980   g_return_if_fail (adjustment != NULL);
4981
4982   if ( ! GTK_WIDGET_REALIZED (sheet)) return;
4983
4984
4985   gtk_widget_hide (sheet->entry_widget);
4986
4987   region =
4988     gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
4989
4990   gdk_window_begin_paint_region (sheet->sheet_window, region);
4991
4992
4993   gtk_sheet_range_draw (sheet, NULL);
4994   draw_row_title_buttons (sheet);
4995   //  size_allocate_global_button (sheet);
4996
4997   gdk_window_end_paint (sheet->sheet_window);
4998 }
4999
5000
5001 static void
5002 hadjustment_value_changed (GtkAdjustment *adjustment,
5003                            gpointer data)
5004 {
5005   GdkRegion *region;
5006   GtkSheet *sheet = GTK_SHEET (data);
5007
5008   g_return_if_fail (adjustment != NULL);
5009
5010   if ( ! GTK_WIDGET_REALIZED (sheet)) return;
5011
5012   gtk_widget_hide (sheet->entry_widget);
5013
5014
5015   region =
5016     gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
5017
5018   gdk_window_begin_paint_region (sheet->sheet_window, region);
5019
5020   gtk_sheet_range_draw (sheet, NULL);
5021   draw_column_title_buttons (sheet);
5022   //  size_allocate_global_button (sheet);
5023
5024   gdk_window_end_paint (sheet->sheet_window);
5025 }
5026
5027
5028 /* COLUMN RESIZING */
5029 static void
5030 draw_xor_vline (GtkSheet *sheet)
5031 {
5032   gint height;
5033   gint xpos = sheet->x_drag;
5034   gdk_drawable_get_size (sheet->sheet_window,
5035                          NULL, &height);
5036
5037
5038   if (sheet->row_titles_visible)
5039     xpos += sheet->row_title_area.width;
5040
5041   gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
5042                  xpos,
5043                  sheet->column_title_area.height,
5044                  xpos,
5045                  height + CELL_SPACING);
5046 }
5047
5048 /* ROW RESIZING */
5049 static void
5050 draw_xor_hline (GtkSheet *sheet)
5051
5052 {
5053   gint width;
5054   gint ypos = sheet->y_drag;
5055
5056   gdk_drawable_get_size (sheet->sheet_window,
5057                          &width, NULL);
5058
5059
5060   if (sheet->column_titles_visible)
5061     ypos += sheet->column_title_area.height;
5062
5063   gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
5064                  sheet->row_title_area.width,
5065                  ypos,
5066                  width + CELL_SPACING,
5067                  ypos);
5068 }
5069
5070 /* SELECTED RANGE */
5071 static void
5072 draw_xor_rectangle (GtkSheet *sheet, GtkSheetRange range)
5073 {
5074   gint i = 0;
5075   GdkRectangle clip_area, area;
5076   GdkGCValues values;
5077
5078   area.x = g_sheet_column_start_pixel (sheet->column_geometry, range.col0);
5079   area.y = g_sheet_row_start_pixel (sheet->row_geometry, range.row0);
5080   area.width = g_sheet_column_start_pixel (sheet->column_geometry, range.coli)- area.x+
5081     g_sheet_column_get_width (sheet->column_geometry, range.coli);
5082   area.height = g_sheet_row_start_pixel (sheet->row_geometry, range.rowi)- area.y +
5083     g_sheet_row_get_height (sheet->row_geometry, range.rowi);
5084
5085   clip_area.x = sheet->row_title_area.width;
5086   clip_area.y = sheet->column_title_area.height;
5087
5088   gdk_drawable_get_size (sheet->sheet_window,
5089                          &clip_area.width, &clip_area.height);
5090
5091   if (!sheet->row_titles_visible) clip_area.x = 0;
5092   if (!sheet->column_titles_visible) clip_area.y = 0;
5093
5094   if (area.x < 0)
5095     {
5096       area.width = area.width + area.x;
5097       area.x = 0;
5098     }
5099   if (area.width > clip_area.width) area.width = clip_area.width + 10;
5100   if (area.y < 0)
5101     {
5102       area.height = area.height + area.y;
5103       area.y = 0;
5104     }
5105   if (area.height > clip_area.height) area.height = clip_area.height + 10;
5106
5107   clip_area.x--;
5108   clip_area.y--;
5109   clip_area.width += 3;
5110   clip_area.height += 3;
5111
5112   gdk_gc_get_values (sheet->xor_gc, &values);
5113
5114   gdk_gc_set_clip_rectangle (sheet->xor_gc, &clip_area);
5115
5116   gdk_draw_rectangle (sheet->sheet_window,
5117                       sheet->xor_gc,
5118                       FALSE,
5119                       area.x + i, area.y + i,
5120                       area.width - 2 * i, area.height - 2 * i);
5121
5122
5123   gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
5124
5125   gdk_gc_set_foreground (sheet->xor_gc, &values.foreground);
5126 }
5127
5128
5129 /* this function returns the new width of the column being resized given
5130  * the COLUMN and X position of the cursor; the x cursor position is passed
5131  * in as a pointer and automaticaly corrected if it's outside the acceptable
5132  * range */
5133 static guint
5134 new_column_width (GtkSheet *sheet, gint column, gint *x)
5135 {
5136   gint left_pos = g_sheet_column_start_pixel (sheet->column_geometry, column)
5137     - sheet->hadjustment->value;
5138
5139   gint width = *x - left_pos;
5140
5141   if ( width < sheet->column_requisition)
5142     {
5143       width = sheet->column_requisition;
5144       *x = left_pos + width;
5145     }
5146
5147   g_sheet_column_set_width (sheet->column_geometry, column, width);
5148
5149   draw_column_title_buttons (sheet);
5150
5151   return width;
5152 }
5153
5154 /* this function returns the new height of the row being resized given
5155  * the row and y position of the cursor; the y cursor position is passed
5156  * in as a pointer and automaticaly corrected if it's beyond min / max limits */
5157 static guint
5158 new_row_height (GtkSheet *sheet, gint row, gint *y)
5159 {
5160   gint height;
5161   guint min_height;
5162
5163   gint cy = *y;
5164   min_height = sheet->row_requisition;
5165
5166   /* you can't shrink a row to less than its minimum height */
5167   if (cy < g_sheet_row_start_pixel (sheet->row_geometry, row) + min_height)
5168
5169     {
5170       *y = cy = g_sheet_row_start_pixel (sheet->row_geometry, row) + min_height;
5171     }
5172
5173   /* calculate new row height making sure it doesn't end up
5174    * less than the minimum height */
5175   height = (cy - g_sheet_row_start_pixel (sheet->row_geometry, row));
5176   if (height < min_height)
5177     height = min_height;
5178
5179   g_sheet_row_set_height (sheet->row_geometry, row, height);
5180   draw_row_title_buttons (sheet);
5181
5182   return height;
5183 }
5184
5185 static void
5186 gtk_sheet_set_column_width (GtkSheet *sheet,
5187                             gint column,
5188                             guint width)
5189 {
5190   guint min_width;
5191
5192   g_return_if_fail (sheet != NULL);
5193   g_return_if_fail (GTK_IS_SHEET (sheet));
5194
5195   if (column < 0 || column >= g_sheet_column_get_column_count (sheet->column_geometry))
5196     return;
5197
5198   gtk_sheet_column_size_request (sheet, column, &min_width);
5199   if (width < min_width) return;
5200
5201   g_sheet_column_set_width (sheet->column_geometry, column, width);
5202
5203   if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
5204     {
5205       draw_column_title_buttons (sheet);
5206       adjust_scrollbars (sheet);
5207       gtk_sheet_size_allocate_entry (sheet);
5208       gtk_sheet_range_draw (sheet, NULL);
5209     }
5210 }
5211
5212
5213
5214 static void
5215 gtk_sheet_set_row_height (GtkSheet *sheet,
5216                           gint row,
5217                           guint height)
5218 {
5219   guint min_height;
5220
5221   g_return_if_fail (sheet != NULL);
5222   g_return_if_fail (GTK_IS_SHEET (sheet));
5223
5224   if (row < 0 || row >= g_sheet_row_get_row_count (sheet->row_geometry))
5225     return;
5226
5227   gtk_sheet_row_size_request (sheet, row, &min_height);
5228   if (height < min_height) return;
5229
5230   g_sheet_row_set_height (sheet->row_geometry, row, height);
5231
5232   if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) )
5233     {
5234       draw_row_title_buttons (sheet);
5235       adjust_scrollbars (sheet);
5236       gtk_sheet_size_allocate_entry (sheet);
5237       gtk_sheet_range_draw (sheet, NULL);
5238     }
5239 }
5240
5241 gboolean
5242 gtk_sheet_get_attributes (const GtkSheet *sheet, gint row, gint col,
5243                           GtkSheetCellAttr *attr)
5244 {
5245   GdkColor *fg, *bg;
5246   const GtkJustification *j ;
5247   const PangoFontDescription *font_desc ;
5248   const GtkSheetCellBorder *border ;
5249   GdkColormap *colormap;
5250
5251   g_return_val_if_fail (sheet != NULL, FALSE);
5252   g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE);
5253
5254   if (row < 0 || col < 0) return FALSE;
5255
5256
5257   attr->foreground = GTK_WIDGET (sheet)->style->black;
5258   attr->background = sheet->color[BG_COLOR];
5259
5260   attr->border.width = 0;
5261   attr->border.line_style = GDK_LINE_SOLID;
5262   attr->border.cap_style = GDK_CAP_NOT_LAST;
5263   attr->border.join_style = GDK_JOIN_MITER;
5264   attr->border.mask = 0;
5265   attr->border.color = GTK_WIDGET (sheet)->style->black;
5266   attr->font_desc = GTK_WIDGET (sheet)->style->font_desc;
5267
5268   attr->is_editable = g_sheet_model_is_editable (sheet->model, row, col);
5269   attr->is_visible = g_sheet_model_is_visible (sheet->model, row, col);
5270
5271
5272   colormap = gtk_widget_get_colormap (GTK_WIDGET (sheet));
5273   fg = g_sheet_model_get_foreground (sheet->model, row, col);
5274   if ( fg )
5275     {
5276       gdk_colormap_alloc_color (colormap, fg, TRUE, TRUE);
5277       attr->foreground = *fg;
5278     }
5279
5280   bg = g_sheet_model_get_background (sheet->model, row, col);
5281   if ( bg )
5282     {
5283       gdk_colormap_alloc_color (colormap, bg, TRUE, TRUE);
5284       attr->background = *bg;
5285     }
5286
5287   attr->justification =
5288     g_sheet_column_get_justification (sheet->column_geometry, col);
5289
5290   j = g_sheet_model_get_justification (sheet->model, row, col);
5291   if (j)
5292     attr->justification = *j;
5293
5294   font_desc = g_sheet_model_get_font_desc (sheet->model, row, col);
5295   if ( font_desc ) attr->font_desc = font_desc;
5296
5297   border = g_sheet_model_get_cell_border (sheet->model, row, col);
5298
5299   if ( border ) attr->border = *border;
5300
5301   return TRUE;
5302 }
5303
5304 static void
5305 gtk_sheet_button_size_request    (GtkSheet *sheet,
5306                                   const GtkSheetButton *button,
5307                                   GtkRequisition *button_requisition)
5308 {
5309   GtkRequisition requisition;
5310   GtkRequisition label_requisition;
5311
5312   label_requisition.height = DEFAULT_ROW_HEIGHT;
5313   label_requisition.width = COLUMN_MIN_WIDTH;
5314
5315   requisition.height = DEFAULT_ROW_HEIGHT;
5316   requisition.width = COLUMN_MIN_WIDTH;
5317
5318
5319   *button_requisition = requisition;
5320   button_requisition->width = MAX (requisition.width, label_requisition.width);
5321   button_requisition->height = MAX (requisition.height, label_requisition.height);
5322
5323 }
5324
5325 static void
5326 gtk_sheet_row_size_request (GtkSheet *sheet,
5327                             gint row,
5328                             guint *requisition)
5329 {
5330   GtkRequisition button_requisition;
5331
5332   gtk_sheet_button_size_request (sheet,
5333                                  g_sheet_row_get_button (sheet->row_geometry, row),
5334                                  &button_requisition);
5335
5336   *requisition = button_requisition.height;
5337
5338   sheet->row_requisition = *requisition;
5339 }
5340
5341 static void
5342 gtk_sheet_column_size_request (GtkSheet *sheet,
5343                                gint col,
5344                                guint *requisition)
5345 {
5346   GtkRequisition button_requisition;
5347
5348   GtkSheetButton *button = g_sheet_column_get_button (sheet->column_geometry, col);
5349
5350   gtk_sheet_button_size_request (sheet,
5351                                  button,
5352                                  &button_requisition);
5353
5354   gtk_sheet_button_free (button);
5355
5356   *requisition = button_requisition.width;
5357
5358   sheet->column_requisition = *requisition;
5359 }
5360
5361
5362 static void
5363 gtk_sheet_forall (GtkContainer *container,
5364                   gboolean include_internals,
5365                   GtkCallback callback,
5366                   gpointer callback_data)
5367 {
5368   GtkSheet *sheet = GTK_SHEET (container);
5369
5370   g_return_if_fail (callback != NULL);
5371
5372   if (sheet->button && sheet->button->parent)
5373     (* callback) (sheet->button, callback_data);
5374
5375   if (sheet->entry_container && GTK_IS_CONTAINER (sheet->entry_container))
5376     (* callback) (sheet->entry_container, callback_data);
5377 }
5378
5379
5380 GSheetModel *
5381 gtk_sheet_get_model (const GtkSheet *sheet)
5382 {
5383   g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);
5384
5385   return sheet->model;
5386 }
5387
5388
5389 GtkSheetButton *
5390 gtk_sheet_button_new (void)
5391 {
5392   GtkSheetButton *button = g_malloc (sizeof (GtkSheetButton));
5393
5394   button->state = GTK_STATE_NORMAL;
5395   button->label = NULL;
5396   button->label_visible = TRUE;
5397   button->justification = GTK_JUSTIFY_FILL;
5398
5399   return button;
5400 }
5401
5402
5403 void
5404 gtk_sheet_button_free (GtkSheetButton *button)
5405 {
5406   if (!button) return ;
5407
5408   g_free (button->label);
5409   g_free (button);
5410 }
5411
5412
5413 static void
5414 append_cell_text (GString *string, const GtkSheet *sheet, gint r, gint c)
5415 {
5416   gchar *celltext = gtk_sheet_cell_get_text (sheet, r, c);
5417
5418   if ( NULL == celltext)
5419     return;
5420
5421   g_string_append (string, celltext);
5422   g_free (celltext);
5423 }
5424
5425
5426 static GString *
5427 range_to_text (const GtkSheet *sheet)
5428 {
5429   gint r, c;
5430   GString *string;
5431
5432   if ( !gtk_sheet_range_isvisible (sheet, sheet->range))
5433     return NULL;
5434
5435   string = g_string_sized_new (80);
5436
5437   for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
5438     {
5439       for (c = sheet->range.col0; c < sheet->range.coli; ++c)
5440         {
5441           append_cell_text (string, sheet, r, c);
5442           g_string_append (string, "\t");
5443         }
5444       append_cell_text (string, sheet, r, c);
5445       if ( r < sheet->range.rowi)
5446         g_string_append (string, "\n");
5447     }
5448
5449   return string;
5450 }
5451
5452 static GString *
5453 range_to_html (const GtkSheet *sheet)
5454 {
5455   gint r, c;
5456   GString *string;
5457
5458   if ( !gtk_sheet_range_isvisible (sheet, sheet->range))
5459     return NULL;
5460
5461   string = g_string_sized_new (480);
5462
5463   g_string_append (string, "<html>\n");
5464   g_string_append (string, "<body>\n");
5465   g_string_append (string, "<table>\n");
5466   for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
5467     {
5468       g_string_append (string, "<tr>\n");
5469       for (c = sheet->range.col0; c <= sheet->range.coli; ++c)
5470         {
5471           g_string_append (string, "<td>");
5472           append_cell_text (string, sheet, r, c);
5473           g_string_append (string, "</td>\n");
5474         }
5475       g_string_append (string, "</tr>\n");
5476     }
5477   g_string_append (string, "</table>\n");
5478   g_string_append (string, "</body>\n");
5479   g_string_append (string, "</html>\n");
5480
5481   return string;
5482 }
5483
5484 enum {
5485   SELECT_FMT_NULL,
5486   SELECT_FMT_TEXT,
5487   SELECT_FMT_HTML
5488 };
5489
5490 static void
5491 primary_get_cb (GtkClipboard     *clipboard,
5492                 GtkSelectionData *selection_data,
5493                 guint             info,
5494                 gpointer          data)
5495 {
5496   GtkSheet *sheet = GTK_SHEET (data);
5497   GString *string = NULL;
5498
5499   switch (info)
5500     {
5501     case SELECT_FMT_TEXT:
5502       string = range_to_text (sheet);
5503       break;
5504     case SELECT_FMT_HTML:
5505       string = range_to_html (sheet);
5506       break;
5507     default:
5508       g_assert_not_reached ();
5509     }
5510
5511   gtk_selection_data_set (selection_data, selection_data->target,
5512                           8,
5513                           (const guchar *) string->str, string->len);
5514   g_string_free (string, TRUE);
5515 }
5516
5517 static void
5518 primary_clear_cb (GtkClipboard *clipboard,
5519                   gpointer      data)
5520 {
5521   GtkSheet *sheet = GTK_SHEET (data);
5522   if ( ! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
5523     return;
5524
5525   gtk_sheet_real_unselect_range (sheet, NULL);
5526 }
5527
5528 static void
5529 gtk_sheet_update_primary_selection (GtkSheet *sheet)
5530 {
5531   static const GtkTargetEntry targets[] = {
5532     { "UTF8_STRING",   0, SELECT_FMT_TEXT },
5533     { "STRING",        0, SELECT_FMT_TEXT },
5534     { "TEXT",          0, SELECT_FMT_TEXT },
5535     { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
5536     { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
5537     { "text/plain",    0, SELECT_FMT_TEXT },
5538     { "text/html",     0, SELECT_FMT_HTML }
5539   };
5540
5541   GtkClipboard *clipboard;
5542
5543   if (!GTK_WIDGET_REALIZED (sheet))
5544     return;
5545
5546   clipboard = gtk_widget_get_clipboard (GTK_WIDGET (sheet),
5547                                         GDK_SELECTION_PRIMARY);
5548
5549   if (gtk_sheet_range_isvisible (sheet, sheet->range))
5550     {
5551       if (!gtk_clipboard_set_with_owner (clipboard, targets,
5552                                          G_N_ELEMENTS (targets),
5553                                          primary_get_cb, primary_clear_cb,
5554                                          G_OBJECT (sheet)))
5555         primary_clear_cb (clipboard, sheet);
5556     }
5557   else
5558     {
5559       if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (sheet))
5560         gtk_clipboard_clear (clipboard);
5561     }
5562 }