Remove inline qualifiers to keep gcc 4.2 happy
[pspp-builds.git] / lib / gtksheet / gtksheet.c
1 /*
2  * Copyright (C) 2006, 2008 Free Software Foundation
3  *
4  * This version of GtkSheet has been *heavily* modified, for the specific
5  * requirements of PSPPIRE.  The changes are copyright by the
6  * Free Software Foundation.  The copyright notice for the original work is
7  * below.
8  */
9
10 /* GtkSheet widget for Gtk+.
11  * Copyright (C) 1999-2001 Adrian E. Feiguin <adrian@ifir.ifir.edu.ar>
12  *
13  * Based on GtkClist widget by Jay Painter, but major changes.
14  * Memory allocation routines inspired on SC (Spreadsheet Calculator)
15  *
16  * This library is free software; you can redistribute it and/or
17  * modify it under the terms of the GNU Lesser General Public
18  * License as published by the Free Software Foundation; either
19  * version 2.1 of the License, or (at your option) any later version.
20  *
21  * This library is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24  * Lesser General Public License for more details.
25  *
26  * You should have received a copy of the GNU Lesser General Public
27  * License along with this library; if not, write to the Free Software
28  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
29  */
30
31 /**
32  * SECTION:gtksheet
33  * @short_description: spreadsheet widget for gtk2
34  *
35  * GtkSheet is a matrix widget for GTK+. It consists of an scrollable grid of
36  * cells where you can allocate text. Cell contents can be edited interactively
37  * through a specially designed entry, GtkItemEntry. It is also a container
38  * subclass, allowing you to display buttons, curves, pixmaps and any other
39  * widgets in it.
40  *
41  * You can also set many attributes as: border, foreground and background color,
42  * text justification, and more.
43  *
44  * The testgtksheet program shows how easy is to create a spreadsheet-like GUI
45  * using this widget.
46  */
47 #include <config.h>
48
49 #include <string.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <glib.h>
53 #include <gdk/gdk.h>
54 #include <gdk/gdkkeysyms.h>
55 #include <gtk/gtksignal.h>
56 #include <gtk/gtklabel.h>
57 #include <gtk/gtkbutton.h>
58 #include <gtk/gtkadjustment.h>
59 #include <gtk/gtktable.h>
60 #include <gtk/gtkbox.h>
61 #include <gtk/gtkmain.h>
62 #include <gtk/gtktypeutils.h>
63 #include <gtk/gtkentry.h>
64 #include <gtk/gtkcontainer.h>
65 #include <gtk/gtkpixmap.h>
66 #include <pango/pango.h>
67 #include "gtkitementry.h"
68 #include "gtksheet.h"
69 #include "gtkextra-marshal.h"
70 #include "gsheetmodel.h"
71
72 /* sheet flags */
73 enum
74   {
75     GTK_SHEET_IS_FROZEN = 1 << 1,
76     GTK_SHEET_IN_XDRAG = 1 << 2,
77     GTK_SHEET_IN_YDRAG = 1 << 3,
78     GTK_SHEET_IN_DRAG = 1 << 4,
79     GTK_SHEET_IN_SELECTION = 1 << 5,
80     GTK_SHEET_IN_RESIZE = 1 << 6,
81     GTK_SHEET_REDRAW_PENDING = 1 << 7,
82   };
83
84 #define GTK_SHEET_FLAGS(sheet) (GTK_SHEET (sheet)->flags)
85 #define GTK_SHEET_SET_FLAGS(sheet,flag) (GTK_SHEET_FLAGS (sheet) |= (flag))
86 #define GTK_SHEET_UNSET_FLAGS(sheet,flag) (GTK_SHEET_FLAGS (sheet) &= ~ (flag))
87
88 #define GTK_SHEET_IS_FROZEN(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IS_FROZEN)
89 #define GTK_SHEET_IN_XDRAG(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_XDRAG)
90 #define GTK_SHEET_IN_YDRAG(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_YDRAG)
91 #define GTK_SHEET_IN_DRAG(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_DRAG)
92 #define GTK_SHEET_IN_SELECTION(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_SELECTION)
93 #define GTK_SHEET_IN_RESIZE(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_RESIZE)
94 #define GTK_SHEET_REDRAW_PENDING(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_REDRAW_PENDING)
95
96 #define CELL_SPACING 1
97 #define DRAG_WIDTH 6
98 #define TIMEOUT_HOVER 300
99 #define COLUMN_MIN_WIDTH 10
100 #define CELLOFFSET 4
101 #define DEFAULT_COLUMN_WIDTH 80
102
103 static void gtk_sheet_update_primary_selection (GtkSheet *sheet);
104 static void gtk_sheet_column_title_button_draw (GtkSheet *sheet, gint column);
105
106 static void gtk_sheet_row_title_button_draw (GtkSheet *sheet, gint row);
107
108
109 static gboolean gtk_sheet_cell_empty (const GtkSheet *sheet, gint row, gint col);
110
111 static inline  void
112 dispose_string (const GtkSheet *sheet, gchar *text)
113 {
114   GSheetModel *model = gtk_sheet_get_model (sheet);
115
116   if ( ! model )
117     return;
118
119   if (g_sheet_model_free_strings (model))
120     g_free (text);
121 }
122
123 static inline
124 guint DEFAULT_ROW_HEIGHT (GtkWidget *widget)
125 {
126   if (!widget->style->font_desc) return 24;
127   else
128     {
129       PangoContext *context = gtk_widget_get_pango_context (widget);
130       PangoFontMetrics *metrics =
131         pango_context_get_metrics (context,
132                                    widget->style->font_desc,
133                                    pango_context_get_language (context));
134
135       guint val = pango_font_metrics_get_descent (metrics) +
136         pango_font_metrics_get_ascent (metrics);
137
138       pango_font_metrics_unref (metrics);
139
140       return PANGO_PIXELS (val) + 2 * CELLOFFSET;
141     }
142 }
143
144 static inline
145 guint DEFAULT_FONT_ASCENT (GtkWidget *widget)
146 {
147   if (!widget->style->font_desc) return 12;
148   else
149     {
150       PangoContext *context = gtk_widget_get_pango_context (widget);
151       PangoFontMetrics *metrics =
152         pango_context_get_metrics (context,
153                                    widget->style->font_desc,
154                                    pango_context_get_language (context));
155       guint val = pango_font_metrics_get_ascent (metrics);
156       pango_font_metrics_unref (metrics);
157       return PANGO_PIXELS (val);
158     }
159 }
160
161 static inline
162 guint STRING_WIDTH (GtkWidget *widget,
163                     const PangoFontDescription *font, const gchar *text)
164 {
165   PangoRectangle rect;
166   PangoLayout *layout;
167
168   layout = gtk_widget_create_pango_layout (widget, text);
169   pango_layout_set_font_description (layout, font);
170
171   pango_layout_get_extents (layout, NULL, &rect);
172
173   g_object_unref (layout);
174   return PANGO_PIXELS (rect.width);
175 }
176
177 static inline
178 guint DEFAULT_FONT_DESCENT (GtkWidget *widget)
179 {
180   if (!widget->style->font_desc) return 12;
181   else
182     {
183       PangoContext *context = gtk_widget_get_pango_context (widget);
184       PangoFontMetrics *metrics =
185         pango_context_get_metrics (context,
186                                    widget->style->font_desc,
187                                    pango_context_get_language (context));
188       guint val = pango_font_metrics_get_descent (metrics);
189       pango_font_metrics_unref (metrics);
190       return PANGO_PIXELS (val);
191     }
192 }
193
194
195 static gint
196 yyy_row_is_visible (const GtkSheet *sheet, gint row)
197 {
198   GSheetRow *row_geo = sheet->row_geometry;
199
200   return g_sheet_row_get_visibility (row_geo, row);
201 }
202
203
204 static gint
205 yyy_row_is_sensitive (const GtkSheet *sheet, gint row)
206 {
207   GSheetRow *row_geo = sheet->row_geometry;
208
209   return g_sheet_row_get_sensitivity (row_geo, row);
210 }
211
212
213
214 static inline gint
215 yyy_row_count (const GtkSheet *sheet)
216 {
217   GSheetRow *row_geo = sheet->row_geometry;
218
219   return g_sheet_row_get_row_count (row_geo);
220 }
221
222 static inline gint
223 yyy_row_height (const GtkSheet *sheet, gint row)
224 {
225   GSheetRow *row_geo = sheet->row_geometry;
226
227   return g_sheet_row_get_height (row_geo, row);
228 }
229
230 static gint
231 yyy_row_top_ypixel (const GtkSheet *sheet, gint row)
232 {
233   GSheetRow *geo = sheet->row_geometry;
234
235   gint y = g_sheet_row_start_pixel (geo, row);
236
237   if ( sheet->column_titles_visible )
238     y += sheet->column_title_area.height;
239
240   return y;
241 }
242
243
244 /* Return the row containing pixel Y */
245 static gint
246 yyy_row_ypixel_to_row (const GtkSheet *sheet, gint y)
247 {
248   GSheetRow *geo = sheet->row_geometry;
249
250   gint cy = sheet->voffset;
251
252   if (sheet->column_titles_visible)
253     cy += sheet->column_title_area.height;
254
255   if (y < cy) return 0;
256
257   return g_sheet_row_pixel_to_row (geo, y - cy);
258 }
259
260
261 /* gives the top pixel of the given row in context of
262  * the sheet's voffset */
263 static inline gint
264 ROW_TOP_YPIXEL (const GtkSheet *sheet, gint row)
265 {
266   return (sheet->voffset + yyy_row_top_ypixel (sheet, row));
267 }
268
269
270 /* returns the row index from a y pixel location in the
271  * context of the sheet's voffset */
272 static inline gint
273 ROW_FROM_YPIXEL (const GtkSheet *sheet, gint y)
274 {
275   return (yyy_row_ypixel_to_row (sheet, y));
276 }
277
278 static inline GtkSheetButton *
279 xxx_column_button (const GtkSheet *sheet, gint col)
280 {
281   GSheetColumn *col_geo = sheet->column_geometry;
282   if ( col < 0 ) return NULL ;
283
284   return g_sheet_column_get_button (col_geo, col);
285 }
286
287
288 static inline gint
289 xxx_column_left_xpixel (const GtkSheet *sheet, gint col)
290 {
291   GSheetColumn *geo = sheet->column_geometry;
292
293   gint x = g_sheet_column_start_pixel (geo, col);
294
295   if ( sheet->row_titles_visible )
296     x += sheet->row_title_area.width;
297
298   return x;
299 }
300
301 static inline gint
302 xxx_column_width (const GtkSheet *sheet, gint col)
303 {
304   GSheetColumn *col_geo = sheet->column_geometry;
305
306   return g_sheet_column_get_width (col_geo, col);
307 }
308
309
310 static inline void
311 xxx_set_column_width (GtkSheet *sheet, gint col, gint width)
312 {
313   if ( sheet->column_geometry )
314     g_sheet_column_set_width (sheet->column_geometry, col, width);
315 }
316
317 static inline void
318 xxx_column_set_left_column (GtkSheet *sheet, gint col, gint i)
319 {
320   GSheetColumn *col_geo = sheet->column_geometry;
321
322   g_sheet_column_set_left_text_column (col_geo, col, i);
323 }
324
325 static inline gint
326 xxx_column_left_column (const GtkSheet *sheet, gint col)
327 {
328   GSheetColumn *col_geo = sheet->column_geometry;
329
330   return g_sheet_column_get_left_text_column (col_geo, col);
331 }
332
333 static inline void
334 xxx_column_set_right_column (GtkSheet *sheet, gint col, gint i)
335 {
336   GSheetColumn *col_geo = sheet->column_geometry;
337
338   g_sheet_column_set_right_text_column (col_geo, col, i);
339 }
340
341 static inline gint
342 xxx_column_right_column (const GtkSheet *sheet, gint col)
343 {
344   GSheetColumn *col_geo = sheet->column_geometry;
345
346   return g_sheet_column_get_right_text_column (col_geo, col);
347 }
348
349 static inline GtkJustification
350 xxx_column_justification (const GtkSheet *sheet, gint col)
351 {
352   GSheetColumn *col_geo = sheet->column_geometry;
353
354   return g_sheet_column_get_justification (col_geo, col);
355 }
356
357 static inline gint
358 xxx_column_is_visible (const GtkSheet *sheet, gint col)
359 {
360   GSheetColumn *col_geo = sheet->column_geometry;
361
362   return g_sheet_column_get_visibility (col_geo, col);
363 }
364
365
366 static inline gint
367 xxx_column_is_sensitive (const GtkSheet *sheet, gint col)
368 {
369   GSheetColumn *col_geo = sheet->column_geometry;
370
371   return g_sheet_column_get_sensitivity (col_geo, col);
372 }
373
374
375 /* gives the left pixel of the given column in context of
376  * the sheet's hoffset */
377 static inline gint
378 COLUMN_LEFT_XPIXEL (const GtkSheet *sheet, gint ncol)
379 {
380   return (sheet->hoffset + xxx_column_left_xpixel (sheet, ncol));
381 }
382
383 static inline gint
384 xxx_column_count (const GtkSheet *sheet)
385 {
386   GSheetColumn *col_geo = sheet->column_geometry;
387
388   return g_sheet_column_get_column_count (col_geo);
389 }
390
391 /* returns the column index from a x pixel location in the
392  * context of the sheet's hoffset */
393 static inline gint
394 COLUMN_FROM_XPIXEL (const GtkSheet * sheet,
395                     gint x)
396 {
397   gint i, cx;
398
399   cx = sheet->hoffset;
400   if ( sheet->row_titles_visible )
401     cx += sheet->row_title_area.width;
402
403   if (x < cx) return 0;
404   for (i = 0; i < xxx_column_count (sheet); i++)
405     {
406       if (x >= cx && x <= (cx + xxx_column_width (sheet, i)) &&
407           xxx_column_is_visible (sheet, i))
408         return i;
409       if ( xxx_column_is_visible (sheet, i))
410         cx += xxx_column_width (sheet, i);
411     }
412
413   /* no match */
414   return xxx_column_count (sheet) - 1;
415 }
416
417 /* returns the total height of the sheet */
418 static inline gint SHEET_HEIGHT (GtkSheet *sheet)
419 {
420   const gint n_rows = yyy_row_count (sheet);
421
422   return yyy_row_top_ypixel (sheet, n_rows - 1) +
423     yyy_row_height (sheet, n_rows - 1);
424 }
425
426
427 static inline GtkSheetButton *
428 yyy_row_button (GtkSheet *sheet, gint row)
429 {
430   GSheetRow *row_geo = sheet->row_geometry;
431
432   return g_sheet_row_get_button (row_geo, row);
433 }
434
435
436
437
438 static inline void
439 yyy_set_row_height (GtkSheet *sheet, gint row, gint height)
440 {
441   if ( sheet->row_geometry )
442     g_sheet_row_set_height (sheet->row_geometry, row, height);
443 }
444
445
446
447 /* returns the total width of the sheet */
448 static inline gint SHEET_WIDTH (GtkSheet *sheet)
449 {
450   gint i, cx;
451
452   cx = ( sheet->row_titles_visible ? sheet->row_title_area.width : 0);
453
454   for (i = 0; i < xxx_column_count (sheet); i++)
455     if (xxx_column_is_visible (sheet, i))
456       cx += xxx_column_width (sheet, i);
457
458   return cx;
459 }
460
461 #define MIN_VISIBLE_ROW(sheet) \
462     ROW_FROM_YPIXEL (sheet, sheet->column_title_area.height + 1)
463
464 #define MAX_VISIBLE_ROW(sheet) \
465     ROW_FROM_YPIXEL (sheet, sheet->sheet_window_height - 1)
466
467 #define MIN_VISIBLE_COLUMN(sheet) \
468     COLUMN_FROM_XPIXEL (sheet, sheet->row_title_area.width + 1)
469
470 #define MAX_VISIBLE_COLUMN(sheet) \
471     COLUMN_FROM_XPIXEL (sheet, sheet->sheet_window_width)
472
473
474
475 static inline gboolean
476 POSSIBLE_XDRAG (const GtkSheet *sheet, gint x, gint *drag_column)
477 {
478   gint column, xdrag;
479
480   column = COLUMN_FROM_XPIXEL (sheet, x);
481   *drag_column = column;
482
483   xdrag = COLUMN_LEFT_XPIXEL (sheet, column) + CELL_SPACING;
484   if (x <= xdrag + DRAG_WIDTH / 2 && column != 0)
485     {
486       while (! xxx_column_is_visible (sheet, column - 1) && column > 0) column--;
487       *drag_column = column - 1;
488       return xxx_column_is_sensitive (sheet, column - 1);
489     }
490
491   xdrag += xxx_column_width (sheet, column);
492   if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2)
493     return xxx_column_is_sensitive (sheet, column);
494
495   return FALSE;
496 }
497
498 static inline gboolean
499 POSSIBLE_YDRAG (const GtkSheet *sheet, gint y, gint *drag_row)
500 {
501   gint row, ydrag;
502   row = ROW_FROM_YPIXEL (sheet, y);
503   *drag_row = row;
504
505   ydrag = ROW_TOP_YPIXEL (sheet, row)+CELL_SPACING;
506   if (y <= ydrag + DRAG_WIDTH / 2 && row != 0)
507     {
508       while (!yyy_row_is_visible (sheet, row - 1) && row > 0) row--;
509       *drag_row = row - 1;
510       return yyy_row_is_sensitive (sheet, row - 1);
511     }
512
513   ydrag +=yyy_row_height (sheet, row);
514
515   if (y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2)
516     return yyy_row_is_sensitive (sheet, row);
517
518
519   return FALSE;
520 }
521
522 static inline gboolean
523 POSSIBLE_DRAG (const GtkSheet *sheet, gint x, gint y,
524                gint *drag_row, gint *drag_column)
525 {
526   gint ydrag, xdrag;
527
528   /* Can't drag if nothing is selected */
529   if ( sheet->range.row0 < 0 || sheet->range.rowi < 0 ||
530        sheet->range.col0 < 0 || sheet->range.coli < 0 )
531     return FALSE;
532
533   *drag_column = COLUMN_FROM_XPIXEL (sheet, x);
534   *drag_row = ROW_FROM_YPIXEL (sheet, y);
535
536   if (x >= COLUMN_LEFT_XPIXEL (sheet, sheet->range.col0) - DRAG_WIDTH / 2 &&
537       x <= COLUMN_LEFT_XPIXEL (sheet, sheet->range.coli) +
538       xxx_column_width (sheet, sheet->range.coli) + DRAG_WIDTH / 2)
539     {
540       ydrag = ROW_TOP_YPIXEL (sheet, sheet->range.row0);
541       if (y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2)
542         {
543           *drag_row = sheet->range.row0;
544           return TRUE;
545         }
546       ydrag = ROW_TOP_YPIXEL (sheet, sheet->range.rowi) +
547         yyy_row_height (sheet, sheet->range.rowi);
548       if (y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2)
549         {
550           *drag_row = sheet->range.rowi;
551           return TRUE;
552         }
553     }
554
555   if (y >= ROW_TOP_YPIXEL (sheet, sheet->range.row0) - DRAG_WIDTH / 2 &&
556       y <= ROW_TOP_YPIXEL (sheet, sheet->range.rowi) +
557       yyy_row_height (sheet, sheet->range.rowi) + DRAG_WIDTH / 2)
558     {
559       xdrag = COLUMN_LEFT_XPIXEL (sheet, sheet->range.col0);
560       if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2)
561         {
562           *drag_column = sheet->range.col0;
563           return TRUE;
564         }
565       xdrag = COLUMN_LEFT_XPIXEL (sheet, sheet->range.coli) +
566         xxx_column_width (sheet, sheet->range.coli);
567       if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2)
568         {
569           *drag_column = sheet->range.coli;
570           return TRUE;
571         }
572     }
573
574   return FALSE;
575 }
576
577 static inline gboolean
578 POSSIBLE_RESIZE (const GtkSheet *sheet, gint x, gint y,
579                  gint *drag_row, gint *drag_column)
580 {
581   gint xdrag, ydrag;
582
583   /* Can't drag if nothing is selected */
584   if ( sheet->range.row0 < 0 || sheet->range.rowi < 0 ||
585        sheet->range.col0 < 0 || sheet->range.coli < 0 )
586     return FALSE;
587
588   xdrag = COLUMN_LEFT_XPIXEL (sheet, sheet->range.coli)+
589     xxx_column_width (sheet, sheet->range.coli);
590
591   ydrag = ROW_TOP_YPIXEL (sheet, sheet->range.rowi)+
592     yyy_row_height (sheet, sheet->range.rowi);
593
594   if (sheet->state == GTK_SHEET_COLUMN_SELECTED)
595     ydrag = ROW_TOP_YPIXEL (sheet, MIN_VISIBLE_ROW (sheet));
596
597   if (sheet->state == GTK_SHEET_ROW_SELECTED)
598     xdrag = COLUMN_LEFT_XPIXEL (sheet, MIN_VISIBLE_COLUMN (sheet));
599
600   *drag_column = COLUMN_FROM_XPIXEL (sheet, x);
601   *drag_row = ROW_FROM_YPIXEL (sheet, y);
602
603   if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2 &&
604       y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2) return TRUE;
605
606   return FALSE;
607 }
608
609 static void gtk_sheet_class_init                 (GtkSheetClass * klass);
610 static void gtk_sheet_init                       (GtkSheet * sheet);
611 static void gtk_sheet_dispose                    (GObject * object);
612 static void gtk_sheet_finalize                   (GObject * object);
613 static void gtk_sheet_style_set                  (GtkWidget *widget,
614                                                   GtkStyle *previous_style);
615 static void gtk_sheet_realize                    (GtkWidget * widget);
616 static void gtk_sheet_unrealize                  (GtkWidget * widget);
617 static void gtk_sheet_map                        (GtkWidget * widget);
618 static void gtk_sheet_unmap                      (GtkWidget * widget);
619 static gint gtk_sheet_expose                     (GtkWidget * widget,
620                                                   GdkEventExpose * event);
621 static void gtk_sheet_forall                     (GtkContainer *container,
622                                                   gboolean include_internals,
623                                                   GtkCallback callback,
624                                                   gpointer callback_data);
625
626 static void gtk_sheet_set_scroll_adjustments     (GtkSheet *sheet,
627                                                   GtkAdjustment *hadjustment,
628                                                   GtkAdjustment *vadjustment);
629
630 static gint gtk_sheet_button_press               (GtkWidget * widget,
631                                                   GdkEventButton * event);
632 static gint gtk_sheet_button_release             (GtkWidget * widget,
633                                                   GdkEventButton * event);
634 static gint gtk_sheet_motion                     (GtkWidget * widget,
635                                                   GdkEventMotion * event);
636 static gint gtk_sheet_entry_key_press            (GtkWidget *widget,
637                                                   GdkEventKey *key);
638 static gint gtk_sheet_key_press                  (GtkWidget *widget,
639                                                   GdkEventKey *key);
640 static void gtk_sheet_size_request               (GtkWidget * widget,
641                                                   GtkRequisition * requisition);
642 static void gtk_sheet_size_allocate              (GtkWidget * widget,
643                                                   GtkAllocation * allocation);
644
645 /* Sheet queries */
646
647 static gboolean gtk_sheet_range_isvisible (const GtkSheet * sheet,
648                                            GtkSheetRange range);
649 static gboolean gtk_sheet_cell_isvisible  (GtkSheet * sheet,
650                                            gint row, gint column);
651 /* Drawing Routines */
652
653 /* draw cell background and frame */
654 static void gtk_sheet_cell_draw_default          (GtkSheet *sheet,
655                                                   gint row, gint column);
656
657 /* draw cell contents */
658 static void gtk_sheet_cell_draw_label            (GtkSheet *sheet,
659                                                   gint row, gint column);
660
661 /* draw visible part of range. If range == NULL then draw the whole screen */
662 static void gtk_sheet_range_draw                 (GtkSheet *sheet,
663                                                   const GtkSheetRange *range);
664
665 /* highlight the visible part of the selected range */
666 static void gtk_sheet_range_draw_selection       (GtkSheet *sheet,
667                                                   GtkSheetRange range);
668
669 /* Selection */
670
671 static gboolean gtk_sheet_move_query             (GtkSheet *sheet,
672                                                   gint row, gint column);
673 static void gtk_sheet_real_select_range          (GtkSheet * sheet,
674                                                   const GtkSheetRange * range);
675 static void gtk_sheet_real_unselect_range        (GtkSheet * sheet,
676                                                   const GtkSheetRange * range);
677 static void gtk_sheet_extend_selection           (GtkSheet *sheet,
678                                                   gint row, gint column);
679 static void gtk_sheet_new_selection              (GtkSheet *sheet,
680                                                   GtkSheetRange *range);
681 static void gtk_sheet_draw_border                (GtkSheet *sheet,
682                                                   GtkSheetRange range);
683 static void gtk_sheet_draw_corners               (GtkSheet *sheet,
684                                                   GtkSheetRange range);
685
686
687 /* Active Cell handling */
688
689 static void gtk_sheet_entry_changed              (GtkWidget *widget,
690                                                   gpointer data);
691 static void gtk_sheet_deactivate_cell    (GtkSheet *sheet);
692 static void gtk_sheet_hide_active_cell           (GtkSheet *sheet);
693 static gboolean gtk_sheet_activate_cell          (GtkSheet *sheet,
694                                                   gint row, gint col);
695 static void gtk_sheet_draw_active_cell           (GtkSheet *sheet);
696 static void gtk_sheet_show_active_cell           (GtkSheet *sheet);
697 static void gtk_sheet_click_cell                 (GtkSheet *sheet,
698                                                   gint row,
699                                                   gint column,
700                                                   gboolean *veto);
701
702 /* Backing Pixmap */
703
704 static void gtk_sheet_make_backing_pixmap        (GtkSheet *sheet,
705                                                   guint width, guint height);
706 static void gtk_sheet_draw_backing_pixmap        (GtkSheet *sheet,
707                                                   GtkSheetRange range);
708 /* Scrollbars */
709
710 static void adjust_scrollbars                    (GtkSheet * sheet);
711 static void vadjustment_value_changed            (GtkAdjustment * adjustment,
712                                                   gpointer data);
713 static void hadjustment_value_changed            (GtkAdjustment * adjustment,
714                                                   gpointer data);
715
716
717 static void draw_xor_vline                       (GtkSheet * sheet);
718 static void draw_xor_hline                       (GtkSheet * sheet);
719 static void draw_xor_rectangle                   (GtkSheet *sheet,
720                                                   GtkSheetRange range);
721
722 static guint new_column_width                    (GtkSheet * sheet,
723                                                   gint column,
724                                                   gint * x);
725 static guint new_row_height                      (GtkSheet * sheet,
726                                                   gint row,
727                                                   gint * y);
728 /* Sheet Button */
729
730 static void create_global_button                 (GtkSheet *sheet);
731 static void global_button_clicked                (GtkWidget *widget,
732                                                   gpointer data);
733 /* Sheet Entry */
734
735 static void create_sheet_entry                   (GtkSheet *sheet);
736 static void gtk_sheet_size_allocate_entry        (GtkSheet *sheet);
737 static void gtk_sheet_entry_set_max_size         (GtkSheet *sheet);
738
739 /* Sheet button gadgets */
740
741 static void size_allocate_column_title_buttons   (GtkSheet * sheet);
742 static void size_allocate_row_title_buttons      (GtkSheet * sheet);
743
744
745 static void size_allocate_global_button          (GtkSheet *sheet);
746 static void gtk_sheet_button_size_request        (GtkSheet *sheet,
747                                                   const GtkSheetButton *button,
748                                                   GtkRequisition *requisition);
749
750 /* Attributes routines */
751 static void init_attributes                      (const GtkSheet *sheet, gint col,
752                                                   GtkSheetCellAttr *attributes);
753
754
755 /* Memory allocation routines */
756 static void gtk_sheet_real_range_clear           (GtkSheet *sheet,
757                                                   const GtkSheetRange *range);
758
759 static void gtk_sheet_real_cell_clear            (GtkSheet *sheet,
760                                                   gint row,
761                                                   gint column);
762
763
764 /* Container Functions */
765 static void gtk_sheet_remove                     (GtkContainer *container,
766                                                   GtkWidget *widget);
767 static void gtk_sheet_realize_child              (GtkSheet *sheet,
768                                                   GtkSheetChild *child);
769 static void gtk_sheet_position_child             (GtkSheet *sheet,
770                                                   GtkSheetChild *child);
771 static void gtk_sheet_position_children          (GtkSheet *sheet);
772 static void gtk_sheet_child_show                 (GtkSheetChild *child);
773 static void gtk_sheet_child_hide                 (GtkSheetChild *child);
774 static void gtk_sheet_column_size_request (GtkSheet *sheet,
775                                            gint col,
776                                            guint *requisition);
777 static void gtk_sheet_row_size_request (GtkSheet *sheet,
778                                         gint row,
779                                         guint *requisition);
780
781
782 /* Signals */
783
784 extern void
785 _gtkextra_signal_emit (GtkObject *object, guint signal_id, ...);
786
787 enum
788   {
789     SELECT_ROW,
790     SELECT_COLUMN,
791     DOUBLE_CLICK_ROW,
792     DOUBLE_CLICK_COLUMN,
793     BUTTON_EVENT_ROW,
794     BUTTON_EVENT_COLUMN,
795     SELECT_RANGE,
796     RESIZE_RANGE,
797     MOVE_RANGE,
798     TRAVERSE,
799     DEACTIVATE,
800     ACTIVATE,
801     CHANGED,
802     LAST_SIGNAL
803   };
804
805 static GtkContainerClass *parent_class = NULL;
806 static guint sheet_signals[LAST_SIGNAL] = { 0 };
807
808
809 GType
810 gtk_sheet_get_type ()
811 {
812   static GType sheet_type = 0;
813
814   if (!sheet_type)
815     {
816       static const GTypeInfo sheet_info =
817         {
818           sizeof (GtkSheetClass),
819           NULL,
820           NULL,
821           (GClassInitFunc) gtk_sheet_class_init,
822           NULL,
823           NULL,
824           sizeof (GtkSheet),
825           0,
826           (GInstanceInitFunc) gtk_sheet_init,
827           NULL,
828         };
829       sheet_type =
830         g_type_register_static (GTK_TYPE_CONTAINER, "GtkSheet",
831                                 &sheet_info, 0);
832     }
833   return sheet_type;
834 }
835
836 static GtkSheetRange*
837 gtk_sheet_range_copy (const GtkSheetRange *range)
838 {
839   GtkSheetRange *new_range;
840
841   g_return_val_if_fail (range != NULL, NULL);
842
843   new_range = g_new (GtkSheetRange, 1);
844
845   *new_range = *range;
846
847   return new_range;
848 }
849
850 static void
851 gtk_sheet_range_free (GtkSheetRange *range)
852 {
853   g_return_if_fail (range != NULL);
854
855   g_free (range);
856 }
857
858 GType
859 gtk_sheet_range_get_type (void)
860 {
861   static GType sheet_range_type = 0;
862
863   if (!sheet_range_type)
864     {
865       sheet_range_type =
866         g_boxed_type_register_static ("GtkSheetRange",
867                                       (GBoxedCopyFunc) gtk_sheet_range_copy,
868                                       (GBoxedFreeFunc) gtk_sheet_range_free);
869     }
870
871   return sheet_range_type;
872 }
873
874
875 static void column_titles_changed (GtkWidget *w, gint first, gint n_columns, gpointer data);
876
877 /* Properties */
878 enum
879   {
880     PROP_0,
881     PROP_ROW_GEO,
882     PROP_COL_GEO,
883     PROP_MODEL
884   };
885
886 static void
887 gtk_sheet_set_row_geometry (GtkSheet *sheet, GSheetRow *geo)
888 {
889   if ( sheet->row_geometry ) g_object_unref (sheet->row_geometry);
890
891   sheet->row_geometry = geo;
892
893   if ( sheet->row_geometry ) g_object_ref (sheet->row_geometry);
894 }
895
896 static void
897 gtk_sheet_set_column_geometry (GtkSheet *sheet, GSheetColumn *geo)
898 {
899   if ( sheet->column_geometry ) g_object_unref (sheet->column_geometry);
900
901   sheet->column_geometry = geo;
902
903   if ( sheet->column_geometry ) g_object_ref (sheet->column_geometry);
904 }
905
906
907 static void
908 gtk_sheet_set_property (GObject         *object,
909                         guint            prop_id,
910                         const GValue    *value,
911                         GParamSpec      *pspec)
912
913 {
914   GtkSheet *sheet = GTK_SHEET (object);
915
916   switch (prop_id)
917     {
918     case PROP_ROW_GEO:
919       gtk_sheet_set_row_geometry (sheet, g_value_get_pointer (value));
920       break;
921     case PROP_COL_GEO:
922       gtk_sheet_set_column_geometry (sheet, g_value_get_pointer (value));
923       if ( sheet->column_geometry)
924         g_signal_connect (sheet->column_geometry, "columns_changed",
925                           G_CALLBACK (column_titles_changed), sheet);
926       break;
927     case PROP_MODEL:
928       gtk_sheet_set_model (sheet, g_value_get_pointer (value));
929       break;
930     default:
931       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
932       break;
933     };
934 }
935
936 static void
937 gtk_sheet_get_property (GObject         *object,
938                         guint            prop_id,
939                         GValue          *value,
940                         GParamSpec      *pspec)
941 {
942   GtkSheet *sheet = GTK_SHEET (object);
943
944   switch (prop_id)
945     {
946     case PROP_ROW_GEO:
947       g_value_set_pointer (value, sheet->row_geometry);
948       break;
949     case PROP_COL_GEO:
950       g_value_set_pointer (value, sheet->column_geometry);
951       break;
952     case PROP_MODEL:
953       g_value_set_pointer (value, sheet->model);
954       break;
955     default:
956       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
957       break;
958     };
959 }
960
961
962 static void
963 gtk_sheet_class_init (GtkSheetClass * klass)
964 {
965   GObjectClass *object_class = G_OBJECT_CLASS (klass);
966
967   GParamSpec *row_geo_spec ;
968   GParamSpec *col_geo_spec ;
969   GParamSpec *model_spec ;
970
971   GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
972   GtkContainerClass *container_class = (GtkContainerClass *) klass;
973
974   parent_class = g_type_class_peek_parent (klass);
975
976   /**
977    * GtkSheet::select-row
978    * @sheet: the sheet widget that emitted the signal
979    * @row: the newly selected row index
980    *
981    * A row has been selected.
982    */
983   sheet_signals[SELECT_ROW] =
984     g_signal_new ("select-row",
985                   G_TYPE_FROM_CLASS (object_class),
986                   G_SIGNAL_RUN_LAST,
987                   offsetof (GtkSheetClass, select_row),
988                   NULL, NULL,
989                   g_cclosure_marshal_VOID__INT,
990                   G_TYPE_NONE,
991                   1,
992                   G_TYPE_INT);
993
994
995   /**
996    * GtkSheet::select - column
997    * @sheet: the sheet widget that emitted the signal
998    * @column: the newly selected column index
999    *
1000    * A column has been selected.
1001    */
1002   sheet_signals[SELECT_COLUMN] =
1003     g_signal_new ("select-column",
1004                   G_TYPE_FROM_CLASS (object_class),
1005                   G_SIGNAL_RUN_LAST,
1006                   offsetof (GtkSheetClass, select_column),
1007                   NULL, NULL,
1008                   g_cclosure_marshal_VOID__INT,
1009                   G_TYPE_NONE,
1010                   1,
1011                   G_TYPE_INT);
1012
1013
1014   /**
1015    * GtkSheet::double-click-row
1016    * @sheet: the sheet widget that emitted the signal
1017    * @row: the row that was double clicked.
1018    *
1019    * A row's title button has been double clicked
1020    */
1021   sheet_signals[DOUBLE_CLICK_ROW] =
1022     g_signal_new ("double-click-row",
1023                   G_TYPE_FROM_CLASS (object_class),
1024                   G_SIGNAL_RUN_LAST,
1025                   0,
1026                   NULL, NULL,
1027                   g_cclosure_marshal_VOID__INT,
1028                   G_TYPE_NONE,
1029                   1,
1030                   G_TYPE_INT);
1031
1032
1033   /**
1034    * GtkSheet::double-click-column
1035    * @sheet: the sheet widget that emitted the signal
1036    * @column: the column that was double clicked.
1037    *
1038    * A column's title button has been double clicked
1039    */
1040   sheet_signals[DOUBLE_CLICK_COLUMN] =
1041     g_signal_new ("double-click-column",
1042                   G_TYPE_FROM_CLASS (object_class),
1043                   G_SIGNAL_RUN_LAST,
1044                   0,
1045                   NULL, NULL,
1046                   g_cclosure_marshal_VOID__INT,
1047                   G_TYPE_NONE,
1048                   1,
1049                   G_TYPE_INT);
1050
1051
1052   /**
1053    * GtkSheet::button-event-column
1054    * @sheet: the sheet widget that emitted the signal
1055    * @column: the column on which the event occured.
1056    *
1057    * A button event occured on a column title button
1058    */
1059   sheet_signals[BUTTON_EVENT_COLUMN] =
1060     g_signal_new ("button-event-column",
1061                   G_TYPE_FROM_CLASS (object_class),
1062                   G_SIGNAL_RUN_LAST,
1063                   0,
1064                   NULL, NULL,
1065                   gtkextra_VOID__INT_POINTER,
1066                   G_TYPE_NONE,
1067                   2,
1068                   G_TYPE_INT,
1069                   G_TYPE_POINTER
1070                   );
1071
1072
1073   /**
1074    * GtkSheet::button-event-row
1075    * @sheet: the sheet widget that emitted the signal
1076    * @column: the column on which the event occured.
1077    *
1078    * A button event occured on a row title button
1079    */
1080   sheet_signals[BUTTON_EVENT_ROW] =
1081     g_signal_new ("button-event-row",
1082                   G_TYPE_FROM_CLASS (object_class),
1083                   G_SIGNAL_RUN_LAST,
1084                   0,
1085                   NULL, NULL,
1086                   gtkextra_VOID__INT_POINTER,
1087                   G_TYPE_NONE,
1088                   2,
1089                   G_TYPE_INT,
1090                   G_TYPE_POINTER
1091                   );
1092
1093
1094   sheet_signals[SELECT_RANGE] =
1095     g_signal_new ("select-range",
1096                   G_TYPE_FROM_CLASS (object_class),
1097                   G_SIGNAL_RUN_LAST,
1098                   offsetof (GtkSheetClass, select_range),
1099                   NULL, NULL,
1100                   g_cclosure_marshal_VOID__BOXED,
1101                   G_TYPE_NONE,
1102                   1,
1103                   GTK_TYPE_SHEET_RANGE);
1104
1105
1106   sheet_signals[RESIZE_RANGE] =
1107     g_signal_new ("resize-range",
1108                   G_TYPE_FROM_CLASS (object_class),
1109                   G_SIGNAL_RUN_LAST,
1110                   offsetof (GtkSheetClass, resize_range),
1111                   NULL, NULL,
1112                   gtkextra_VOID__BOXED_BOXED,
1113                   G_TYPE_NONE,
1114                   2,
1115                   GTK_TYPE_SHEET_RANGE, GTK_TYPE_SHEET_RANGE
1116                   );
1117
1118   sheet_signals[MOVE_RANGE] =
1119     g_signal_new ("move-range",
1120                   G_TYPE_FROM_CLASS (object_class),
1121                   G_SIGNAL_RUN_LAST,
1122                   offsetof (GtkSheetClass, move_range),
1123                   NULL, NULL,
1124                   gtkextra_VOID__BOXED_BOXED,
1125                   G_TYPE_NONE,
1126                   2,
1127                   GTK_TYPE_SHEET_RANGE, GTK_TYPE_SHEET_RANGE
1128                   );
1129
1130   sheet_signals[TRAVERSE] =
1131     g_signal_new ("traverse",
1132                   G_TYPE_FROM_CLASS (object_class),
1133                   G_SIGNAL_RUN_LAST,
1134                   offsetof (GtkSheetClass, traverse),
1135                   NULL, NULL,
1136                   gtkextra_BOOLEAN__INT_INT_POINTER_POINTER,
1137                   G_TYPE_BOOLEAN, 4, G_TYPE_INT, G_TYPE_INT,
1138                   G_TYPE_POINTER, G_TYPE_POINTER);
1139
1140
1141   sheet_signals[DEACTIVATE] =
1142     g_signal_new ("deactivate",
1143                   G_TYPE_FROM_CLASS (object_class),
1144                   G_SIGNAL_RUN_LAST,
1145                   offsetof (GtkSheetClass, deactivate),
1146                   NULL, NULL,
1147                   gtkextra_VOID__INT_INT,
1148                   G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
1149
1150   sheet_signals[ACTIVATE] =
1151     g_signal_new ("activate",
1152                   G_TYPE_FROM_CLASS (object_class),
1153                   G_SIGNAL_RUN_LAST,
1154                   offsetof (GtkSheetClass, activate),
1155                   NULL, NULL,
1156                   gtkextra_BOOLEAN__INT_INT,
1157                   G_TYPE_BOOLEAN, 2, G_TYPE_INT, G_TYPE_INT);
1158
1159   sheet_signals[CHANGED] =
1160     g_signal_new ("changed",
1161                   G_TYPE_FROM_CLASS (object_class),
1162                   G_SIGNAL_RUN_LAST,
1163                   offsetof (GtkSheetClass, changed),
1164                   NULL, NULL,
1165                   gtkextra_VOID__INT_INT,
1166                   G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
1167
1168   widget_class->set_scroll_adjustments_signal =
1169     g_signal_new ("set-scroll-adjustments",
1170                   G_TYPE_FROM_CLASS (object_class),
1171                   G_SIGNAL_RUN_LAST,
1172                   offsetof (GtkSheetClass, set_scroll_adjustments),
1173                   NULL, NULL,
1174                   gtkextra_VOID__OBJECT_OBJECT,
1175                   G_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
1176
1177
1178   container_class->add = NULL;
1179   container_class->remove = gtk_sheet_remove;
1180   container_class->forall = gtk_sheet_forall;
1181
1182   object_class->dispose = gtk_sheet_dispose;
1183   object_class->finalize = gtk_sheet_finalize;
1184
1185
1186   row_geo_spec =
1187     g_param_spec_pointer ("row-geometry",
1188                           "Row Geometry",
1189                           "A pointer to the model of the row geometry",
1190                           G_PARAM_READABLE | G_PARAM_WRITABLE );
1191
1192   col_geo_spec =
1193     g_param_spec_pointer ("column-geometry",
1194                           "Column Geometry",
1195                           "A pointer to the model of the column geometry",
1196                           G_PARAM_READABLE | G_PARAM_WRITABLE );
1197
1198   model_spec =
1199     g_param_spec_pointer ("model",
1200                           "Model",
1201                           "A pointer to the data model",
1202                           G_PARAM_READABLE | G_PARAM_WRITABLE );
1203
1204
1205   object_class->set_property = gtk_sheet_set_property;
1206   object_class->get_property = gtk_sheet_get_property;
1207
1208   g_object_class_install_property (object_class,
1209                                    PROP_ROW_GEO,
1210                                    row_geo_spec);
1211
1212   g_object_class_install_property (object_class,
1213                                    PROP_COL_GEO,
1214                                    col_geo_spec);
1215
1216   g_object_class_install_property (object_class,
1217                                    PROP_MODEL,
1218                                    model_spec);
1219
1220
1221   widget_class->realize = gtk_sheet_realize;
1222   widget_class->unrealize = gtk_sheet_unrealize;
1223   widget_class->map = gtk_sheet_map;
1224   widget_class->unmap = gtk_sheet_unmap;
1225   widget_class->style_set = gtk_sheet_style_set;
1226   widget_class->button_press_event = gtk_sheet_button_press;
1227   widget_class->button_release_event = gtk_sheet_button_release;
1228   widget_class->motion_notify_event = gtk_sheet_motion;
1229   widget_class->key_press_event = gtk_sheet_key_press;
1230   widget_class->expose_event = gtk_sheet_expose;
1231   widget_class->size_request = gtk_sheet_size_request;
1232   widget_class->size_allocate = gtk_sheet_size_allocate;
1233   widget_class->focus_in_event = NULL;
1234   widget_class->focus_out_event = NULL;
1235
1236   klass->set_scroll_adjustments = gtk_sheet_set_scroll_adjustments;
1237   klass->select_row = NULL;
1238   klass->select_column = NULL;
1239   klass->select_range = NULL;
1240   klass->resize_range = NULL;
1241   klass->move_range = NULL;
1242   klass->traverse = NULL;
1243   klass->deactivate = NULL;
1244   klass->activate = NULL;
1245   klass->changed = NULL;
1246 }
1247
1248 static void
1249 gtk_sheet_init (GtkSheet *sheet)
1250 {
1251   sheet->model = NULL;
1252   sheet->column_geometry = NULL;
1253   sheet->row_geometry = NULL;
1254
1255   sheet->children = NULL;
1256
1257   sheet->flags = 0;
1258   sheet->selection_mode = GTK_SELECTION_NONE;
1259   sheet->freeze_count = 0;
1260   sheet->state = GTK_SHEET_NORMAL;
1261
1262   GTK_WIDGET_UNSET_FLAGS (sheet, GTK_NO_WINDOW);
1263   GTK_WIDGET_SET_FLAGS (sheet, GTK_CAN_FOCUS);
1264
1265   sheet->column_title_window = NULL;
1266   sheet->column_title_area.x = 0;
1267   sheet->column_title_area.y = 0;
1268   sheet->column_title_area.width = 0;
1269   sheet->column_title_area.height = DEFAULT_ROW_HEIGHT (GTK_WIDGET (sheet));
1270
1271   sheet->row_title_window = NULL;
1272   sheet->row_title_area.x = 0;
1273   sheet->row_title_area.y = 0;
1274   sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
1275   sheet->row_title_area.height = 0;
1276
1277
1278   sheet->active_cell.row = 0;
1279   sheet->active_cell.col = 0;
1280   sheet->selection_cell.row = 0;
1281   sheet->selection_cell.col = 0;
1282
1283   sheet->pixmap = NULL;
1284
1285   sheet->range.row0 = 0;
1286   sheet->range.rowi = 0;
1287   sheet->range.col0 = 0;
1288   sheet->range.coli = 0;
1289
1290   sheet->state = GTK_SHEET_NORMAL;
1291
1292   sheet->sheet_window = NULL;
1293   sheet->sheet_window_width = 0;
1294   sheet->sheet_window_height = 0;
1295   sheet->entry_widget = NULL;
1296   sheet->entry_container = NULL;
1297   sheet->button = NULL;
1298
1299   sheet->hoffset = 0;
1300   sheet->voffset = 0;
1301
1302   sheet->hadjustment = NULL;
1303   sheet->vadjustment = NULL;
1304
1305   sheet->cursor_drag = gdk_cursor_new (GDK_PLUS);
1306   sheet->xor_gc = NULL;
1307   sheet->fg_gc = NULL;
1308   sheet->bg_gc = NULL;
1309   sheet->x_drag = 0;
1310   sheet->y_drag = 0;
1311   gdk_color_parse ("white", &sheet->bg_color);
1312   gdk_colormap_alloc_color (gdk_colormap_get_system (), &sheet->bg_color, FALSE,
1313                              TRUE);
1314   gdk_color_parse ("gray", &sheet->grid_color);
1315   gdk_colormap_alloc_color (gdk_colormap_get_system (), &sheet->grid_color, FALSE,
1316                              TRUE);
1317
1318   sheet->show_grid = TRUE;
1319
1320   sheet->motion_timer = 0;
1321
1322   sheet->columns_resizable = TRUE;
1323   sheet->rows_resizable = TRUE;
1324
1325   sheet->row_titles_visible = TRUE;
1326   sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
1327
1328   sheet->column_titles_visible = TRUE;
1329   sheet->autoscroll = TRUE;
1330   sheet->justify_entry = TRUE;
1331
1332
1333   /* create sheet entry */
1334   sheet->entry_type = 0;
1335   create_sheet_entry (sheet);
1336
1337   /* create global selection button */
1338   create_global_button (sheet);
1339 }
1340
1341
1342 /* Callback which occurs whenever columns are inserted / deleted in the model */
1343 static void
1344 columns_inserted_deleted_callback (GSheetModel *model, gint first_column,
1345                                    gint n_columns,
1346                                    gpointer data)
1347 {
1348   gint i;
1349   GtkSheet *sheet = GTK_SHEET (data);
1350
1351   GtkSheetRange range;
1352   gint model_columns = g_sheet_model_get_column_count (model);
1353
1354
1355   /* Need to update all the columns starting from the first column and onwards.
1356    * Previous column are unchanged, so don't need to be updated.
1357    */
1358   range.col0 = first_column;
1359   range.row0 = 0;
1360   range.coli = xxx_column_count (sheet) - 1;
1361   range.rowi = yyy_row_count (sheet) - 1;
1362
1363   adjust_scrollbars (sheet);
1364
1365   if (sheet->active_cell.col >= model_columns)
1366     gtk_sheet_activate_cell (sheet, sheet->active_cell.row, model_columns - 1);
1367
1368   for (i = first_column; i <= MAX_VISIBLE_COLUMN (sheet); i++)
1369     gtk_sheet_column_title_button_draw (sheet, i);
1370
1371   gtk_sheet_range_draw (sheet, &range);
1372 }
1373
1374
1375 /* Callback which occurs whenever rows are inserted / deleted in the model */
1376 static void
1377 rows_inserted_deleted_callback (GSheetModel *model, gint first_row,
1378                                 gint n_rows, gpointer data)
1379 {
1380   gint i;
1381   GtkSheet *sheet = GTK_SHEET (data);
1382
1383   GtkSheetRange range;
1384
1385   gint model_rows = g_sheet_model_get_row_count (model);
1386
1387   /* Need to update all the rows starting from the first row and onwards.
1388    * Previous rows are unchanged, so don't need to be updated.
1389    */
1390   range.row0 = first_row;
1391   range.col0 = 0;
1392   range.rowi = yyy_row_count (sheet) - 1;
1393   range.coli = xxx_column_count (sheet) - 1;
1394
1395   adjust_scrollbars (sheet);
1396
1397   if (sheet->active_cell.row >= model_rows)
1398     gtk_sheet_activate_cell (sheet, model_rows - 1, sheet->active_cell.col);
1399
1400   for (i = first_row; i <= MAX_VISIBLE_ROW (sheet); i++)
1401     gtk_sheet_row_title_button_draw (sheet, i);
1402
1403   gtk_sheet_range_draw (sheet, &range);
1404 }
1405
1406 /*
1407   If row0 or rowi are negative, then all rows will be updated.
1408   If col0 or coli are negative, then all columns will be updated.
1409 */
1410 static void
1411 range_update_callback (GSheetModel *m, gint row0, gint col0,
1412                        gint rowi, gint coli, gpointer data)
1413 {
1414   GtkSheet *sheet = GTK_SHEET (data);
1415
1416   GtkSheetRange range;
1417
1418   range.row0 = row0;
1419   range.col0 = col0;
1420   range.rowi = rowi;
1421   range.coli = coli;
1422
1423   if ( MAX_VISIBLE_ROW (sheet) >
1424        g_sheet_model_get_row_count (sheet->model)
1425        ||
1426        MAX_VISIBLE_COLUMN (sheet) >
1427        g_sheet_model_get_column_count (sheet->model))
1428     {
1429       gtk_sheet_move_query (sheet, 0, 0);
1430     }
1431
1432   if ( ( row0 < 0 && col0 < 0 ) || ( rowi < 0 && coli < 0 ) )
1433     {
1434       gint i;
1435       gtk_sheet_range_draw (sheet, NULL);
1436       adjust_scrollbars (sheet);
1437
1438       for (i = MIN_VISIBLE_ROW (sheet); i <= MAX_VISIBLE_ROW (sheet); i++)
1439         gtk_sheet_row_title_button_draw (sheet, i);
1440
1441       for (i = MIN_VISIBLE_COLUMN (sheet);
1442            i <= MAX_VISIBLE_COLUMN (sheet); i++)
1443         gtk_sheet_column_title_button_draw (sheet, i);
1444
1445       return;
1446     }
1447   else if ( row0 < 0 || rowi < 0 )
1448     {
1449       range.row0 = MIN_VISIBLE_ROW (sheet);
1450       range.rowi = MAX_VISIBLE_ROW (sheet);
1451     }
1452   else if ( col0 < 0 || coli < 0 )
1453     {
1454       range.col0 = MIN_VISIBLE_COLUMN (sheet);
1455       range.coli = MAX_VISIBLE_COLUMN (sheet);
1456     }
1457
1458   gtk_sheet_range_draw (sheet, &range);
1459 }
1460
1461
1462 /**
1463  * gtk_sheet_new:
1464  * @rows: initial number of rows
1465  * @columns: initial number of columns
1466  * @title: sheet title
1467  * @model: the model to use for the sheet data
1468  *
1469  * Creates a new sheet widget with the given number of rows and columns.
1470  *
1471  * Returns: the new sheet widget
1472  */
1473 GtkWidget *
1474 gtk_sheet_new (GSheetRow *vgeo, GSheetColumn *hgeo, GSheetModel *model)
1475 {
1476   GtkWidget *widget = g_object_new (GTK_TYPE_SHEET,
1477                                     "row-geometry", vgeo,
1478                                     "column-geometry", hgeo,
1479                                     "model", model,
1480                                     NULL);
1481   return widget;
1482 }
1483
1484
1485 /**
1486  * gtk_sheet_set_model
1487  * @sheet: the sheet to set the model for
1488  * @model: the model to use for the sheet data
1489  *
1490  * Sets the model for a GtkSheet
1491  *
1492  */
1493 void
1494 gtk_sheet_set_model (GtkSheet *sheet, GSheetModel *model)
1495 {
1496   g_return_if_fail (GTK_IS_SHEET (sheet));
1497
1498   if (sheet->model ) g_object_unref (sheet->model);
1499
1500   sheet->model = model;
1501
1502   if ( model)
1503     {
1504       g_object_ref (model);
1505
1506       g_signal_connect (model, "range_changed",
1507                         G_CALLBACK (range_update_callback), sheet);
1508
1509       g_signal_connect (model, "rows_inserted",
1510                         G_CALLBACK (rows_inserted_deleted_callback), sheet);
1511
1512       g_signal_connect (model, "rows_deleted",
1513                         G_CALLBACK (rows_inserted_deleted_callback), sheet);
1514
1515       g_signal_connect (model, "columns_inserted",
1516                         G_CALLBACK (columns_inserted_deleted_callback), sheet);
1517
1518       g_signal_connect (model, "columns_deleted",
1519                         G_CALLBACK (columns_inserted_deleted_callback), sheet);
1520     }
1521 }
1522
1523
1524 /* Call back for when the column titles have changed.
1525    FIRST is the first column changed.
1526    N_COLUMNS is the number of columns which have changed, or - 1, which
1527    indicates that the column has changed to its right - most extremity
1528 */
1529 static void
1530 column_titles_changed (GtkWidget *w, gint first, gint n_columns, gpointer data)
1531 {
1532   GtkSheet *sheet = GTK_SHEET (data);
1533   gboolean extremity = FALSE;
1534
1535   if ( n_columns == -1 )
1536     {
1537       extremity = TRUE;
1538       n_columns = xxx_column_count (sheet) - 1 ;
1539     }
1540
1541   if (!GTK_SHEET_IS_FROZEN (sheet))
1542     {
1543       gint i;
1544       for ( i = first ; i <= first + n_columns ; ++i )
1545         {
1546           gtk_sheet_column_title_button_draw (sheet, i);
1547           g_signal_emit (sheet, sheet_signals[CHANGED], 0, -1, i);
1548         }
1549     }
1550
1551   if ( extremity)
1552     gtk_sheet_column_title_button_draw (sheet, -1);
1553
1554 }
1555
1556 void
1557 gtk_sheet_change_entry (GtkSheet *sheet, GtkType entry_type)
1558 {
1559   gint state;
1560
1561   g_return_if_fail (sheet != NULL);
1562   g_return_if_fail (GTK_IS_SHEET (sheet));
1563
1564   state = sheet->state;
1565
1566   if (sheet->state == GTK_SHEET_NORMAL)
1567     gtk_sheet_hide_active_cell (sheet);
1568
1569   sheet->entry_type = entry_type;
1570
1571   create_sheet_entry (sheet);
1572
1573   if (state == GTK_SHEET_NORMAL)
1574     {
1575       gtk_sheet_show_active_cell (sheet);
1576       g_signal_connect (gtk_sheet_get_entry (sheet),
1577                         "changed",
1578                         G_CALLBACK (gtk_sheet_entry_changed),
1579                         sheet);
1580     }
1581 }
1582
1583 void
1584 gtk_sheet_show_grid (GtkSheet *sheet, gboolean show)
1585 {
1586   g_return_if_fail (sheet != NULL);
1587   g_return_if_fail (GTK_IS_SHEET (sheet));
1588
1589   if (show == sheet->show_grid) return;
1590
1591   sheet->show_grid = show;
1592
1593   if (!GTK_SHEET_IS_FROZEN (sheet))
1594     gtk_sheet_range_draw (sheet, NULL);
1595 }
1596
1597 gboolean
1598 gtk_sheet_grid_visible (GtkSheet *sheet)
1599 {
1600   g_return_val_if_fail (sheet != NULL, 0);
1601   g_return_val_if_fail (GTK_IS_SHEET (sheet), 0);
1602
1603   return sheet->show_grid;
1604 }
1605
1606 void
1607 gtk_sheet_set_background (GtkSheet *sheet, GdkColor *color)
1608 {
1609   g_return_if_fail (sheet != NULL);
1610   g_return_if_fail (GTK_IS_SHEET (sheet));
1611
1612   if (!color)
1613     {
1614       gdk_color_parse ("white", &sheet->bg_color);
1615       gdk_colormap_alloc_color (gdk_colormap_get_system (), &sheet->bg_color, FALSE, TRUE);
1616     }
1617   else
1618     sheet->bg_color = *color;
1619
1620   if (!GTK_SHEET_IS_FROZEN (sheet))
1621     gtk_sheet_range_draw (sheet, NULL);
1622 }
1623
1624 void
1625 gtk_sheet_set_grid (GtkSheet *sheet, GdkColor *color)
1626 {
1627   g_return_if_fail (sheet != NULL);
1628   g_return_if_fail (GTK_IS_SHEET (sheet));
1629
1630   if (!color)
1631     {
1632       gdk_color_parse ("black", &sheet->grid_color);
1633       gdk_colormap_alloc_color (gdk_colormap_get_system (), &sheet->grid_color, FALSE, TRUE);
1634     }
1635   else
1636     sheet->grid_color = *color;
1637
1638   if (!GTK_SHEET_IS_FROZEN (sheet))
1639     gtk_sheet_range_draw (sheet, NULL);
1640 }
1641
1642 guint
1643 gtk_sheet_get_columns_count (GtkSheet *sheet)
1644 {
1645   g_return_val_if_fail (sheet != NULL, 0);
1646   g_return_val_if_fail (GTK_IS_SHEET (sheet), 0);
1647
1648   return xxx_column_count (sheet);
1649 }
1650
1651 guint
1652 gtk_sheet_get_rows_count (GtkSheet *sheet)
1653 {
1654   g_return_val_if_fail (sheet != NULL, 0);
1655   g_return_val_if_fail (GTK_IS_SHEET (sheet), 0);
1656
1657   return yyy_row_count (sheet);
1658 }
1659
1660 gint
1661 gtk_sheet_get_state (GtkSheet *sheet)
1662 {
1663   g_return_val_if_fail (sheet != NULL, 0);
1664   g_return_val_if_fail (GTK_IS_SHEET (sheet), 0);
1665
1666   return (sheet->state);
1667 }
1668
1669 void
1670 gtk_sheet_set_selection_mode (GtkSheet *sheet, gint mode)
1671 {
1672   g_return_if_fail (sheet != NULL);
1673   g_return_if_fail (GTK_IS_SHEET (sheet));
1674
1675   if (GTK_WIDGET_REALIZED (sheet))
1676     gtk_sheet_real_unselect_range (sheet, NULL);
1677
1678   sheet->selection_mode = mode;
1679 }
1680
1681 void
1682 gtk_sheet_set_autoresize (GtkSheet *sheet, gboolean autoresize)
1683 {
1684   g_return_if_fail (sheet != NULL);
1685   g_return_if_fail (GTK_IS_SHEET (sheet));
1686
1687   sheet->autoresize = autoresize;
1688 }
1689
1690 gboolean
1691 gtk_sheet_autoresize (GtkSheet *sheet)
1692 {
1693   g_return_val_if_fail (sheet != NULL, FALSE);
1694   g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE);
1695
1696   return sheet->autoresize;
1697 }
1698
1699 static void
1700 gtk_sheet_set_column_width (GtkSheet * sheet,
1701                             gint column,
1702                             guint width);
1703
1704
1705 static void
1706 gtk_sheet_autoresize_column (GtkSheet *sheet, gint column)
1707 {
1708   gint text_width = 0;
1709   gint row;
1710
1711   g_return_if_fail (sheet != NULL);
1712   g_return_if_fail (GTK_IS_SHEET (sheet));
1713   if (column >= xxx_column_count (sheet) || column < 0) return;
1714
1715   for (row = 0; row < yyy_row_count (sheet); row++)
1716     {
1717       gchar *text = gtk_sheet_cell_get_text (sheet, row, column);
1718       if (text && strlen (text) > 0)
1719         {
1720           GtkSheetCellAttr attributes;
1721
1722           gtk_sheet_get_attributes (sheet, row, column, &attributes);
1723           if (attributes.is_visible)
1724             {
1725               gint width = STRING_WIDTH (GTK_WIDGET (sheet),
1726                                          attributes.font_desc,
1727                                          text)
1728                 + 2 * CELLOFFSET + attributes.border.width;
1729               text_width = MAX (text_width, width);
1730             }
1731         }
1732       dispose_string (sheet, text);
1733     }
1734
1735   if (text_width > xxx_column_width (sheet, column) )
1736     {
1737       gtk_sheet_set_column_width (sheet, column, text_width);
1738       GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_REDRAW_PENDING);
1739     }
1740 }
1741
1742
1743 void
1744 gtk_sheet_set_autoscroll (GtkSheet *sheet, gboolean autoscroll)
1745 {
1746   g_return_if_fail (sheet != NULL);
1747   g_return_if_fail (GTK_IS_SHEET (sheet));
1748
1749   sheet->autoscroll = autoscroll;
1750 }
1751
1752 gboolean
1753 gtk_sheet_autoscroll (GtkSheet *sheet)
1754 {
1755   g_return_val_if_fail (sheet != NULL, FALSE);
1756   g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE);
1757
1758   return sheet->autoscroll;
1759 }
1760
1761
1762 void
1763 gtk_sheet_set_justify_entry (GtkSheet *sheet, gboolean justify)
1764 {
1765   g_return_if_fail (sheet != NULL);
1766   g_return_if_fail (GTK_IS_SHEET (sheet));
1767
1768   sheet->justify_entry = justify;
1769 }
1770
1771 gboolean
1772 gtk_sheet_justify_entry (GtkSheet *sheet)
1773 {
1774   g_return_val_if_fail (sheet != NULL, FALSE);
1775   g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE);
1776
1777   return sheet->justify_entry;
1778 }
1779
1780
1781
1782 void
1783 gtk_sheet_freeze (GtkSheet *sheet)
1784 {
1785   g_return_if_fail (sheet != NULL);
1786   g_return_if_fail (GTK_IS_SHEET (sheet));
1787
1788   sheet->freeze_count++;
1789   GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IS_FROZEN);
1790 }
1791
1792 void
1793 gtk_sheet_thaw (GtkSheet *sheet)
1794 {
1795   g_return_if_fail (sheet != NULL);
1796   g_return_if_fail (GTK_IS_SHEET (sheet));
1797
1798   if (sheet->freeze_count == 0) return;
1799
1800   sheet->freeze_count--;
1801   if (sheet->freeze_count > 0) return;
1802
1803   adjust_scrollbars (sheet);
1804
1805   GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IS_FROZEN);
1806
1807   sheet->old_vadjustment = -1.;
1808   sheet->old_hadjustment = -1.;
1809
1810   if (sheet->hadjustment)
1811     g_signal_emit_by_name (sheet->hadjustment,
1812                            "value_changed");
1813   if (sheet->vadjustment)
1814     g_signal_emit_by_name (sheet->vadjustment,
1815                            "value_changed");
1816
1817   if (sheet->state == GTK_STATE_NORMAL)
1818     if (sheet->entry_widget && GTK_WIDGET_MAPPED (sheet->entry_widget))
1819       {
1820         gtk_sheet_activate_cell (sheet, sheet->active_cell.row,
1821                                  sheet->active_cell.col);
1822       }
1823
1824 }
1825
1826 void
1827 gtk_sheet_set_row_titles_width (GtkSheet *sheet, guint width)
1828 {
1829   if (width < COLUMN_MIN_WIDTH) return;
1830
1831   sheet->row_title_area.width = width;
1832
1833   adjust_scrollbars (sheet);
1834
1835   sheet->old_hadjustment = -1.;
1836   if (sheet->hadjustment)
1837     g_signal_emit_by_name (sheet->hadjustment,
1838                            "value_changed");
1839   size_allocate_global_button (sheet);
1840 }
1841
1842 void
1843 gtk_sheet_set_column_titles_height (GtkSheet *sheet, guint height)
1844 {
1845   if (height < DEFAULT_ROW_HEIGHT (GTK_WIDGET (sheet))) return;
1846
1847   sheet->column_title_area.height = height;
1848
1849   adjust_scrollbars (sheet);
1850
1851   sheet->old_vadjustment = -1.;
1852   if (sheet->vadjustment)
1853     g_signal_emit_by_name (sheet->vadjustment,
1854                            "value_changed");
1855   size_allocate_global_button (sheet);
1856 }
1857
1858 void
1859 gtk_sheet_show_column_titles (GtkSheet *sheet)
1860 {
1861   gint col;
1862
1863   if (sheet->column_titles_visible) return;
1864
1865   sheet->column_titles_visible = TRUE;
1866
1867
1868   if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1869     {
1870       gdk_window_show (sheet->column_title_window);
1871       gdk_window_move_resize (sheet->column_title_window,
1872                               sheet->column_title_area.x,
1873                               sheet->column_title_area.y,
1874                               sheet->column_title_area.width,
1875                               sheet->column_title_area.height);
1876
1877       for (col = MIN_VISIBLE_COLUMN (sheet);
1878            col <= MAX_VISIBLE_COLUMN (sheet);
1879            col++)
1880         {
1881           GtkSheetButton *button = xxx_column_button (sheet, col);
1882           GtkSheetChild *child = button->child;
1883           if (child)
1884             gtk_sheet_child_show (child);
1885           gtk_sheet_button_free (button);
1886         }
1887       adjust_scrollbars (sheet);
1888     }
1889
1890   sheet->old_vadjustment = -1.;
1891   if (sheet->vadjustment)
1892     g_signal_emit_by_name (sheet->vadjustment,
1893                            "value_changed");
1894   size_allocate_global_button (sheet);
1895 }
1896
1897
1898 void
1899 gtk_sheet_show_row_titles (GtkSheet *sheet)
1900 {
1901   gint row;
1902
1903   if (sheet->row_titles_visible) return;
1904
1905   sheet->row_titles_visible = TRUE;
1906
1907
1908   if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1909     {
1910       gdk_window_show (sheet->row_title_window);
1911       gdk_window_move_resize (sheet->row_title_window,
1912                               sheet->row_title_area.x,
1913                               sheet->row_title_area.y,
1914                               sheet->row_title_area.width,
1915                               sheet->row_title_area.height);
1916
1917       for (row = MIN_VISIBLE_ROW (sheet);
1918            row <= MAX_VISIBLE_ROW (sheet);
1919            row++)
1920         {
1921           const GtkSheetButton *button = yyy_row_button (sheet, row);
1922           GtkSheetChild *child = button->child;
1923
1924           if (child)
1925             {
1926               gtk_sheet_child_show (child);
1927             }
1928         }
1929       adjust_scrollbars (sheet);
1930     }
1931
1932   sheet->old_hadjustment = -1.;
1933   if (sheet->hadjustment)
1934     g_signal_emit_by_name (sheet->hadjustment,
1935                            "value_changed");
1936   size_allocate_global_button (sheet);
1937 }
1938
1939 void
1940 gtk_sheet_hide_column_titles (GtkSheet *sheet)
1941 {
1942   gint col;
1943
1944   if (!sheet->column_titles_visible) return;
1945
1946   sheet->column_titles_visible = FALSE;
1947
1948   if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1949     {
1950       if (sheet->column_title_window)
1951         gdk_window_hide (sheet->column_title_window);
1952       if (GTK_WIDGET_VISIBLE (sheet->button))
1953         gtk_widget_hide (sheet->button);
1954
1955       for (col = MIN_VISIBLE_COLUMN (sheet);
1956            col <= MAX_VISIBLE_COLUMN (sheet);
1957            col++)
1958         {
1959           GtkSheetButton *button = xxx_column_button (sheet, col);
1960           GtkSheetChild *child = button->child;
1961           if (child)
1962             gtk_sheet_child_hide (child);
1963           gtk_sheet_button_free (button);
1964         }
1965       adjust_scrollbars (sheet);
1966     }
1967
1968   sheet->old_vadjustment = -1.;
1969   if (sheet->vadjustment)
1970     g_signal_emit_by_name (sheet->vadjustment,
1971                            "value_changed");
1972 }
1973
1974 void
1975 gtk_sheet_hide_row_titles (GtkSheet *sheet)
1976 {
1977   gint row;
1978
1979   if (!sheet->row_titles_visible) return;
1980
1981   sheet->row_titles_visible = FALSE;
1982
1983
1984   if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1985     {
1986       if (sheet->row_title_window)
1987         gdk_window_hide (sheet->row_title_window);
1988       if (GTK_WIDGET_VISIBLE (sheet->button))
1989         gtk_widget_hide (sheet->button);
1990       for (row = MIN_VISIBLE_ROW (sheet);
1991            row <= MAX_VISIBLE_ROW (sheet);
1992            row++)
1993         {
1994           const GtkSheetButton *button = yyy_row_button (sheet, row);
1995           GtkSheetChild *child = button->child;
1996
1997           if (child)
1998             gtk_sheet_child_hide (child);
1999         }
2000       adjust_scrollbars (sheet);
2001     }
2002
2003   sheet->old_hadjustment = -1.;
2004   if (sheet->hadjustment)
2005     g_signal_emit_by_name (sheet->hadjustment,
2006                            "value_changed");
2007 }
2008
2009 gboolean
2010 gtk_sheet_column_titles_visible (GtkSheet *sheet)
2011 {
2012   g_return_val_if_fail (sheet != NULL, FALSE);
2013   g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE);
2014   return sheet->column_titles_visible;
2015 }
2016
2017 gboolean
2018 gtk_sheet_row_titles_visible (GtkSheet *sheet)
2019 {
2020   g_return_val_if_fail (sheet != NULL, FALSE);
2021   g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE);
2022   return sheet->row_titles_visible;
2023 }
2024
2025 void
2026 gtk_sheet_moveto (GtkSheet *sheet,
2027                   gint row,
2028                   gint column,
2029                   gfloat row_align,
2030                   gfloat col_align)
2031 {
2032   gint x, y;
2033   guint width, height;
2034   gint adjust;
2035   gint min_row, min_col;
2036
2037   g_return_if_fail (sheet != NULL);
2038   g_return_if_fail (GTK_IS_SHEET (sheet));
2039   g_return_if_fail (sheet->hadjustment != NULL);
2040   g_return_if_fail (sheet->vadjustment != NULL);
2041
2042   if (row < 0 || row >= yyy_row_count (sheet))
2043     return;
2044   if (column < 0 || column >= xxx_column_count (sheet))
2045     return;
2046
2047   height = sheet->sheet_window_height;
2048   width = sheet->sheet_window_width;
2049
2050   /* adjust vertical scrollbar */
2051   if (row >= 0 && row_align >= 0.0)
2052     {
2053       y = ROW_TOP_YPIXEL (sheet, row) - sheet->voffset
2054         - (gint) ( row_align * height + (1.0 - row_align)
2055                    * yyy_row_height (sheet, row));
2056
2057       /* This forces the sheet to scroll when you don't see the entire cell */
2058       min_row = row;
2059       adjust = 0;
2060       if (row_align >= 1.0)
2061         {
2062           while (min_row >= 0 && min_row > MIN_VISIBLE_ROW (sheet))
2063             {
2064               if (yyy_row_is_visible (sheet, min_row))
2065                 adjust += yyy_row_height (sheet, min_row);
2066
2067               if (adjust >= height)
2068                 {
2069                   break;
2070                 }
2071               min_row--;
2072             }
2073           min_row = MAX (min_row, 0);
2074
2075           min_row ++;
2076
2077           y = ROW_TOP_YPIXEL (sheet, min_row) - sheet->voffset +
2078             yyy_row_height (sheet, min_row) - 1;
2079         }
2080
2081       if (y < 0)
2082         sheet->vadjustment->value = 0.0;
2083       else
2084         sheet->vadjustment->value = y;
2085
2086       sheet->old_vadjustment = -1.;
2087       g_signal_emit_by_name (sheet->vadjustment,
2088                              "value_changed");
2089
2090     }
2091
2092   /* adjust horizontal scrollbar */
2093   if (column >= 0 && col_align >= 0.0)
2094     {
2095       x = COLUMN_LEFT_XPIXEL (sheet, column) - sheet->hoffset
2096         - (gint) ( col_align*width + (1.0 - col_align)*
2097                    xxx_column_width (sheet, column));
2098
2099       /* This forces the sheet to scroll when you don't see the entire cell */
2100       min_col = column;
2101       adjust = 0;
2102       if (col_align == 1.0)
2103         {
2104           while (min_col >= 0 && min_col > MIN_VISIBLE_COLUMN (sheet))
2105             {
2106               if (xxx_column_is_visible (sheet, min_col))
2107                 adjust += xxx_column_width (sheet, min_col);
2108
2109               if (adjust >= width)
2110                 {
2111                   break;
2112                 }
2113               min_col--;
2114             }
2115           min_col = MAX (min_col, 0);
2116           x = COLUMN_LEFT_XPIXEL (sheet, min_col) - sheet->hoffset +
2117             xxx_column_width (sheet, min_col) - 1;
2118         }
2119
2120       if (x < 0)
2121         sheet->hadjustment->value = 0.0;
2122       else
2123         sheet->hadjustment->value = x;
2124
2125       sheet->old_vadjustment = -1.;
2126       g_signal_emit_by_name (sheet->hadjustment,
2127                              "value_changed");
2128     }
2129 }
2130
2131
2132 void
2133 gtk_sheet_columns_set_resizable (GtkSheet *sheet, gboolean resizable)
2134 {
2135   g_return_if_fail (sheet != NULL);
2136   g_return_if_fail (GTK_IS_SHEET (sheet));
2137
2138   sheet->columns_resizable = resizable;
2139 }
2140
2141 gboolean
2142 gtk_sheet_columns_resizable (GtkSheet *sheet)
2143 {
2144   g_return_val_if_fail (sheet != NULL, FALSE);
2145   g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE);
2146
2147   return sheet->columns_resizable;
2148 }
2149
2150
2151 void
2152 gtk_sheet_rows_set_resizable (GtkSheet *sheet, gboolean resizable)
2153 {
2154   g_return_if_fail (sheet != NULL);
2155   g_return_if_fail (GTK_IS_SHEET (sheet));
2156
2157   sheet->rows_resizable = resizable;
2158 }
2159
2160 gboolean
2161 gtk_sheet_rows_resizable (GtkSheet *sheet)
2162 {
2163   g_return_val_if_fail (sheet != NULL, FALSE);
2164   g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE);
2165
2166   return sheet->rows_resizable;
2167 }
2168
2169
2170 void
2171 gtk_sheet_select_row (GtkSheet * sheet,
2172                       gint row)
2173 {
2174   g_return_if_fail (sheet != NULL);
2175   g_return_if_fail (GTK_IS_SHEET (sheet));
2176
2177   if (row < 0 || row >= yyy_row_count (sheet))
2178     return;
2179
2180   if (sheet->state != GTK_SHEET_NORMAL)
2181     gtk_sheet_real_unselect_range (sheet, NULL);
2182   else
2183     gtk_sheet_deactivate_cell (sheet);
2184
2185   sheet->state = GTK_SHEET_ROW_SELECTED;
2186   sheet->range.row0 = row;
2187   sheet->range.col0 = 0;
2188   sheet->range.rowi = row;
2189   sheet->range.coli = xxx_column_count (sheet) - 1;
2190   sheet->active_cell.row = row;
2191   sheet->active_cell.col = 0;
2192
2193   g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, row);
2194   gtk_sheet_real_select_range (sheet, NULL);
2195 }
2196
2197
2198 void
2199 gtk_sheet_select_column (GtkSheet * sheet, gint column)
2200 {
2201   g_return_if_fail (sheet != NULL);
2202   g_return_if_fail (GTK_IS_SHEET (sheet));
2203
2204   if (column < 0 || column >= xxx_column_count (sheet))
2205     return;
2206
2207   if (sheet->state != GTK_SHEET_NORMAL)
2208     gtk_sheet_real_unselect_range (sheet, NULL);
2209   else
2210     gtk_sheet_deactivate_cell (sheet);
2211
2212
2213   sheet->state = GTK_SHEET_COLUMN_SELECTED;
2214   sheet->range.row0 = 0;
2215   sheet->range.col0 = column;
2216   sheet->range.rowi = yyy_row_count (sheet) - 1;
2217   sheet->range.coli = column;
2218   sheet->active_cell.row = 0;
2219   sheet->active_cell.col = column;
2220
2221   g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, column);
2222   gtk_sheet_real_select_range (sheet, NULL);
2223 }
2224
2225
2226
2227
2228 static gboolean
2229 gtk_sheet_range_isvisible (const GtkSheet * sheet,
2230                            GtkSheetRange range)
2231 {
2232   g_return_val_if_fail (sheet != NULL, FALSE);
2233
2234   if (range.row0 < 0 || range.row0 >= yyy_row_count (sheet))
2235     return FALSE;
2236
2237   if (range.rowi < 0 || range.rowi >= yyy_row_count (sheet))
2238     return FALSE;
2239
2240   if (range.col0 < 0 || range.col0 >= xxx_column_count (sheet))
2241     return FALSE;
2242
2243   if (range.coli < 0 || range.coli >= xxx_column_count (sheet))
2244     return FALSE;
2245
2246   if (range.rowi < MIN_VISIBLE_ROW (sheet))
2247     return FALSE;
2248
2249   if (range.row0 > MAX_VISIBLE_ROW (sheet))
2250     return FALSE;
2251
2252   if (range.coli < MIN_VISIBLE_COLUMN (sheet))
2253     return FALSE;
2254
2255   if (range.col0 > MAX_VISIBLE_COLUMN (sheet))
2256     return FALSE;
2257
2258   return TRUE;
2259 }
2260
2261 static gboolean
2262 gtk_sheet_cell_isvisible (GtkSheet * sheet,
2263                           gint row, gint column)
2264 {
2265   GtkSheetRange range;
2266
2267   range.row0 = row;
2268   range.col0 = column;
2269   range.rowi = row;
2270   range.coli = column;
2271
2272   return gtk_sheet_range_isvisible (sheet, range);
2273 }
2274
2275 void
2276 gtk_sheet_get_visible_range (GtkSheet *sheet, GtkSheetRange *range)
2277 {
2278   g_return_if_fail (sheet != NULL);
2279   g_return_if_fail (GTK_IS_SHEET (sheet)) ;
2280   g_return_if_fail (range != NULL);
2281
2282   range->row0 = MIN_VISIBLE_ROW (sheet);
2283   range->col0 = MIN_VISIBLE_COLUMN (sheet);
2284   range->rowi = MAX_VISIBLE_ROW (sheet);
2285   range->coli = MAX_VISIBLE_COLUMN (sheet);
2286 }
2287
2288 GtkAdjustment *
2289 gtk_sheet_get_vadjustment (GtkSheet * sheet)
2290 {
2291   g_return_val_if_fail (sheet != NULL, NULL);
2292   g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);
2293
2294   return sheet->vadjustment;
2295 }
2296
2297 GtkAdjustment *
2298 gtk_sheet_get_hadjustment (GtkSheet * sheet)
2299 {
2300   g_return_val_if_fail (sheet != NULL, NULL);
2301   g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);
2302
2303   return sheet->hadjustment;
2304 }
2305
2306 void
2307 gtk_sheet_set_vadjustment (GtkSheet *sheet,
2308                            GtkAdjustment *adjustment)
2309 {
2310   GtkAdjustment *old_adjustment;
2311
2312   g_return_if_fail (sheet != NULL);
2313   g_return_if_fail (GTK_IS_SHEET (sheet));
2314   if (adjustment)
2315     g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
2316
2317   if (sheet->vadjustment == adjustment)
2318     return;
2319
2320   old_adjustment = sheet->vadjustment;
2321
2322   if (sheet->vadjustment)
2323     {
2324       g_signal_handlers_disconnect_matched (sheet->vadjustment,
2325                                             G_SIGNAL_MATCH_DATA,
2326                                             0, 0, 0, 0,
2327                                             sheet);
2328       g_object_unref (sheet->vadjustment);
2329     }
2330
2331   sheet->vadjustment = adjustment;
2332
2333   if (sheet->vadjustment)
2334     {
2335       g_object_ref (sheet->vadjustment);
2336       g_object_ref_sink (sheet->vadjustment);
2337
2338       g_signal_connect (sheet->vadjustment, "value_changed",
2339                         G_CALLBACK (vadjustment_value_changed),
2340                         sheet);
2341     }
2342
2343   if (!sheet->vadjustment || !old_adjustment)
2344     {
2345       gtk_widget_queue_resize (GTK_WIDGET (sheet));
2346       return;
2347     }
2348
2349   sheet->old_vadjustment = sheet->vadjustment->value;
2350 }
2351
2352 void
2353 gtk_sheet_set_hadjustment (GtkSheet *sheet,
2354                            GtkAdjustment *adjustment)
2355 {
2356   GtkAdjustment *old_adjustment;
2357
2358   g_return_if_fail (sheet != NULL);
2359   g_return_if_fail (GTK_IS_SHEET (sheet));
2360   if (adjustment)
2361     g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
2362
2363   if (sheet->hadjustment == adjustment)
2364     return;
2365
2366   old_adjustment = sheet->hadjustment;
2367
2368   if (sheet->hadjustment)
2369     {
2370       g_signal_handlers_disconnect_matched (sheet->hadjustment,
2371                                             G_SIGNAL_MATCH_DATA,
2372                                             0, 0, 0, 0,
2373                                             sheet);
2374       g_object_unref (sheet->hadjustment);
2375     }
2376
2377   sheet->hadjustment = adjustment;
2378
2379   if (sheet->hadjustment)
2380     {
2381       g_object_ref (sheet->hadjustment);
2382       g_object_ref_sink (sheet->hadjustment);
2383
2384       g_signal_connect (sheet->hadjustment, "value_changed",
2385                         G_CALLBACK (hadjustment_value_changed),
2386                         sheet);
2387     }
2388
2389   if (!sheet->hadjustment || !old_adjustment)
2390     {
2391       gtk_widget_queue_resize (GTK_WIDGET (sheet));
2392       return;
2393     }
2394
2395   sheet->old_hadjustment = sheet->hadjustment->value;
2396 }
2397
2398 static void
2399 gtk_sheet_set_scroll_adjustments (GtkSheet *sheet,
2400                                   GtkAdjustment *hadjustment,
2401                                   GtkAdjustment *vadjustment)
2402 {
2403   if (sheet->hadjustment != hadjustment)
2404     gtk_sheet_set_hadjustment (sheet, hadjustment);
2405
2406   if (sheet->vadjustment != vadjustment)
2407     gtk_sheet_set_vadjustment (sheet, vadjustment);
2408 }
2409
2410 static void
2411 gtk_sheet_finalize (GObject * object)
2412 {
2413   GtkSheet *sheet;
2414
2415   g_return_if_fail (object != NULL);
2416   g_return_if_fail (GTK_IS_SHEET (object));
2417
2418   sheet = GTK_SHEET (object);
2419
2420   if (G_OBJECT_CLASS (parent_class)->finalize)
2421     (*G_OBJECT_CLASS (parent_class)->finalize) (object);
2422 }
2423
2424 static void
2425 gtk_sheet_dispose  (GObject *object)
2426 {
2427   GtkSheet *sheet = GTK_SHEET (object);
2428   GList *children;
2429
2430   g_return_if_fail (object != NULL);
2431   g_return_if_fail (GTK_IS_SHEET (object));
2432
2433   if ( sheet->dispose_has_run )
2434     return ;
2435
2436   sheet->dispose_has_run = TRUE;
2437
2438   if (sheet->model) g_object_unref (sheet->model);
2439   if (sheet->row_geometry) g_object_unref (sheet->row_geometry);
2440   if (sheet->column_geometry) g_object_unref (sheet->column_geometry);
2441
2442   g_object_unref (sheet->entry_container);
2443   g_object_unref (sheet->button);
2444
2445   /* unref adjustments */
2446   if (sheet->hadjustment)
2447     {
2448       g_signal_handlers_disconnect_matched (sheet->hadjustment,
2449                                             G_SIGNAL_MATCH_DATA,
2450                                             0, 0, 0, 0,
2451                                             sheet);
2452
2453       g_object_unref (sheet->hadjustment);
2454       sheet->hadjustment = NULL;
2455     }
2456
2457   if (sheet->vadjustment)
2458     {
2459       g_signal_handlers_disconnect_matched (sheet->vadjustment,
2460                                             G_SIGNAL_MATCH_DATA,
2461                                             0, 0, 0, 0,
2462                                             sheet);
2463
2464       g_object_unref (sheet->vadjustment);
2465
2466       sheet->vadjustment = NULL;
2467     }
2468
2469   children = sheet->children;
2470   while (children)
2471     {
2472       GtkSheetChild *child = (GtkSheetChild *)children->data;
2473       if (child && child->widget)
2474         gtk_sheet_remove (GTK_CONTAINER (sheet), child->widget);
2475       children = sheet->children;
2476     }
2477   sheet->children = NULL;
2478
2479   if (G_OBJECT_CLASS (parent_class)->dispose)
2480     (*G_OBJECT_CLASS (parent_class)->dispose) (object);
2481 }
2482
2483 static void
2484 gtk_sheet_style_set (GtkWidget *widget,
2485                      GtkStyle *previous_style)
2486 {
2487   GtkSheet *sheet;
2488
2489   g_return_if_fail (widget != NULL);
2490   g_return_if_fail (GTK_IS_SHEET (widget));
2491
2492   if (GTK_WIDGET_CLASS (parent_class)->style_set)
2493     (*GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous_style);
2494
2495   sheet = GTK_SHEET (widget);
2496
2497   if (GTK_WIDGET_REALIZED (widget))
2498     {
2499       gtk_style_set_background (widget->style, widget->window, widget->state);
2500     }
2501
2502 }
2503
2504 static void
2505 gtk_sheet_realize (GtkWidget * widget)
2506 {
2507   GtkSheet *sheet;
2508   GdkWindowAttr attributes;
2509   gint attributes_mask;
2510   GdkGCValues values, auxvalues;
2511   GdkColormap *colormap;
2512   GtkSheetChild *child;
2513   GList *children;
2514
2515   g_return_if_fail (widget != NULL);
2516   g_return_if_fail (GTK_IS_SHEET (widget));
2517
2518   sheet = GTK_SHEET (widget);
2519
2520   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
2521
2522   attributes.window_type = GDK_WINDOW_CHILD;
2523   attributes.x = widget->allocation.x;
2524   attributes.y = widget->allocation.y;
2525   attributes.width = widget->allocation.width;
2526   attributes.height = widget->allocation.height;
2527   attributes.wclass = GDK_INPUT_OUTPUT;
2528
2529   attributes.visual = gtk_widget_get_visual (widget);
2530   attributes.colormap = gtk_widget_get_colormap (widget);
2531
2532   attributes.event_mask = gtk_widget_get_events (widget);
2533   attributes.event_mask |= (GDK_EXPOSURE_MASK |
2534                             GDK_BUTTON_PRESS_MASK |
2535                             GDK_BUTTON_RELEASE_MASK |
2536                             GDK_KEY_PRESS_MASK |
2537                             GDK_POINTER_MOTION_MASK |
2538                             GDK_POINTER_MOTION_HINT_MASK);
2539   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP |
2540     GDK_WA_CURSOR;
2541
2542   attributes.cursor = gdk_cursor_new (GDK_TOP_LEFT_ARROW);
2543
2544   /* main window */
2545   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
2546
2547   gdk_window_set_user_data (widget->window, sheet);
2548
2549   widget->style = gtk_style_attach (widget->style, widget->window);
2550
2551   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
2552
2553   attributes.x = 0;
2554   if (sheet->row_titles_visible)
2555     attributes.x = sheet->row_title_area.width;
2556   attributes.y = 0;
2557   attributes.width = sheet->column_title_area.width;
2558   attributes.height = sheet->column_title_area.height;
2559
2560   /* column - title window */
2561   sheet->column_title_window = gdk_window_new (widget->window, &attributes, attributes_mask);
2562   gdk_window_set_user_data (sheet->column_title_window, sheet);
2563   gtk_style_set_background (widget->style, sheet->column_title_window, GTK_STATE_NORMAL);
2564
2565   attributes.x = 0;
2566   attributes.y = 0;
2567   if (sheet->column_titles_visible)
2568     attributes.y = sheet->column_title_area.height;
2569   attributes.width = sheet->row_title_area.width;
2570   attributes.height = sheet->row_title_area.height;
2571
2572   /* row - title window */
2573   sheet->row_title_window = gdk_window_new (widget->window, &attributes, attributes_mask);
2574   gdk_window_set_user_data (sheet->row_title_window, sheet);
2575   gtk_style_set_background (widget->style, sheet->row_title_window, GTK_STATE_NORMAL);
2576
2577   /* sheet - window */
2578   attributes.cursor = gdk_cursor_new (GDK_PLUS);
2579
2580   attributes.x = 0;
2581   attributes.y = 0;
2582   attributes.width = sheet->sheet_window_width,
2583     attributes.height = sheet->sheet_window_height;
2584
2585   sheet->sheet_window = gdk_window_new (widget->window, &attributes, attributes_mask);
2586   gdk_window_set_user_data (sheet->sheet_window, sheet);
2587
2588   gdk_cursor_unref (attributes.cursor);
2589
2590   gdk_window_set_background (sheet->sheet_window, &widget->style->white);
2591   gdk_window_show (sheet->sheet_window);
2592
2593   /* backing_pixmap */
2594   gtk_sheet_make_backing_pixmap (sheet, 0, 0);
2595
2596   /* GCs */
2597   if (sheet->fg_gc)
2598     g_object_unref (sheet->fg_gc);
2599   if (sheet->bg_gc)
2600     g_object_unref (sheet->bg_gc);
2601   sheet->fg_gc = gdk_gc_new (widget->window);
2602   sheet->bg_gc = gdk_gc_new (widget->window);
2603
2604   colormap = gtk_widget_get_colormap (widget);
2605
2606   gdk_gc_get_values (sheet->fg_gc, &auxvalues);
2607
2608   values.foreground = widget->style->white;
2609   values.function = GDK_INVERT;
2610   values.subwindow_mode = GDK_INCLUDE_INFERIORS;
2611   if (sheet->xor_gc)
2612     g_object_unref (sheet->xor_gc);
2613   sheet->xor_gc = gdk_gc_new_with_values (widget->window,
2614                                           &values,
2615                                           GDK_GC_FOREGROUND |
2616                                           GDK_GC_FUNCTION |
2617                                           GDK_GC_SUBWINDOW);
2618
2619
2620   gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
2621   gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
2622
2623   gtk_widget_set_parent_window (sheet->button, sheet->sheet_window);
2624   gtk_widget_set_parent (sheet->button, GTK_WIDGET (sheet));
2625
2626
2627   gdk_cursor_unref (sheet->cursor_drag);
2628   sheet->cursor_drag = gdk_cursor_new (GDK_PLUS);
2629
2630   if (sheet->column_titles_visible)
2631     gdk_window_show (sheet->column_title_window);
2632   if (sheet->row_titles_visible)
2633     gdk_window_show (sheet->row_title_window);
2634
2635   size_allocate_row_title_buttons (sheet);
2636   size_allocate_column_title_buttons (sheet);
2637
2638   children = sheet->children;
2639   while (children)
2640     {
2641       child = children->data;
2642       children = children->next;
2643
2644       gtk_sheet_realize_child (sheet, child);
2645     }
2646
2647   gtk_sheet_update_primary_selection (sheet);
2648 }
2649
2650 static void
2651 create_global_button (GtkSheet *sheet)
2652 {
2653   sheet->button = gtk_button_new_with_label (" ");
2654
2655   g_object_ref_sink (sheet->button);
2656
2657   g_signal_connect (sheet->button,
2658                     "pressed",
2659                     G_CALLBACK (global_button_clicked),
2660                     sheet);
2661 }
2662
2663 static void
2664 size_allocate_global_button (GtkSheet *sheet)
2665 {
2666   GtkAllocation allocation;
2667
2668   if (!sheet->column_titles_visible) return;
2669   if (!sheet->row_titles_visible) return;
2670
2671   gtk_widget_size_request (sheet->button, NULL);
2672
2673   allocation.x = 0;
2674   allocation.y = 0;
2675   allocation.width = sheet->row_title_area.width;
2676   allocation.height = sheet->column_title_area.height;
2677
2678   gtk_widget_size_allocate (sheet->button, &allocation);
2679   gtk_widget_show (sheet->button);
2680 }
2681
2682 static void
2683 global_button_clicked (GtkWidget *widget, gpointer data)
2684 {
2685   gboolean veto;
2686
2687   gtk_sheet_click_cell (GTK_SHEET (data), - 1, - 1, &veto);
2688   gtk_widget_grab_focus (GTK_WIDGET (data));
2689 }
2690
2691
2692 static void
2693 gtk_sheet_unrealize (GtkWidget * widget)
2694 {
2695   GtkSheet *sheet;
2696
2697   g_return_if_fail (widget != NULL);
2698   g_return_if_fail (GTK_IS_SHEET (widget));
2699
2700   sheet = GTK_SHEET (widget);
2701
2702   gdk_cursor_unref (sheet->cursor_drag);
2703
2704   g_object_unref (sheet->xor_gc);
2705   g_object_unref (sheet->fg_gc);
2706   g_object_unref (sheet->bg_gc);
2707
2708   gdk_window_destroy (sheet->sheet_window);
2709   gdk_window_destroy (sheet->column_title_window);
2710   gdk_window_destroy (sheet->row_title_window);
2711
2712   if (sheet->pixmap)
2713     {
2714       g_object_unref (sheet->pixmap);
2715       sheet->pixmap = NULL;
2716     }
2717
2718   sheet->column_title_window = NULL;
2719   sheet->sheet_window = NULL;
2720   sheet->xor_gc = NULL;
2721   sheet->fg_gc = NULL;
2722   sheet->bg_gc = NULL;
2723
2724   gtk_widget_unparent (sheet->entry_widget);
2725   gtk_widget_unparent (sheet->button);
2726
2727   if (GTK_WIDGET_CLASS (parent_class)->unrealize)
2728     (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
2729 }
2730
2731 static void
2732 gtk_sheet_map (GtkWidget * widget)
2733 {
2734   GtkSheet *sheet = GTK_SHEET (widget);
2735   GtkSheetChild *child;
2736   GList *children;
2737
2738   g_return_if_fail (widget != NULL);
2739   g_return_if_fail (GTK_IS_SHEET (widget));
2740
2741   if (!GTK_WIDGET_MAPPED (widget))
2742     {
2743       GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
2744
2745       gdk_window_show (widget->window);
2746       gdk_window_show (sheet->sheet_window);
2747
2748       if (sheet->column_titles_visible)
2749         {
2750           size_allocate_column_title_buttons (sheet);
2751           gdk_window_show (sheet->column_title_window);
2752         }
2753       if (sheet->row_titles_visible)
2754         {
2755           size_allocate_row_title_buttons (sheet);
2756           gdk_window_show (sheet->row_title_window);
2757         }
2758
2759       if (!GTK_WIDGET_MAPPED (sheet->entry_widget)
2760           && sheet->active_cell.row >= 0
2761           && sheet->active_cell.col >= 0 )
2762         {
2763           gtk_widget_show (sheet->entry_widget);
2764           gtk_widget_map (sheet->entry_widget);
2765         }
2766
2767       if (GTK_WIDGET_VISIBLE (sheet->button) &&
2768           !GTK_WIDGET_MAPPED (sheet->button))
2769         {
2770           gtk_widget_show (sheet->button);
2771           gtk_widget_map (sheet->button);
2772         }
2773
2774       if (GTK_BIN (sheet->button)->child)
2775         if (GTK_WIDGET_VISIBLE (GTK_BIN (sheet->button)->child) &&
2776             !GTK_WIDGET_MAPPED (GTK_BIN (sheet->button)->child))
2777           gtk_widget_map (GTK_BIN (sheet->button)->child);
2778
2779       gtk_sheet_range_draw (sheet, NULL);
2780       gtk_sheet_activate_cell (sheet,
2781                                sheet->active_cell.row,
2782                                sheet->active_cell.col);
2783
2784       children = sheet->children;
2785       while (children)
2786         {
2787           child = children->data;
2788           children = children->next;
2789
2790           if (GTK_WIDGET_VISIBLE (child->widget) &&
2791               !GTK_WIDGET_MAPPED (child->widget))
2792             {
2793               gtk_widget_map (child->widget);
2794               gtk_sheet_position_child (sheet, child);
2795             }
2796         }
2797
2798     }
2799 }
2800
2801 static void
2802 gtk_sheet_unmap (GtkWidget * widget)
2803 {
2804   GtkSheet *sheet;
2805   GtkSheetChild *child;
2806   GList *children;
2807
2808   g_return_if_fail (widget != NULL);
2809   g_return_if_fail (GTK_IS_SHEET (widget));
2810
2811   sheet = GTK_SHEET (widget);
2812
2813   if (GTK_WIDGET_MAPPED (widget))
2814     {
2815       GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
2816
2817       gdk_window_hide (sheet->sheet_window);
2818       if (sheet->column_titles_visible)
2819         gdk_window_hide (sheet->column_title_window);
2820       if (sheet->row_titles_visible)
2821         gdk_window_hide (sheet->row_title_window);
2822       gdk_window_hide (widget->window);
2823
2824       if (GTK_WIDGET_MAPPED (sheet->entry_widget))
2825         gtk_widget_unmap (sheet->entry_widget);
2826
2827       if (GTK_WIDGET_MAPPED (sheet->button))
2828         gtk_widget_unmap (sheet->button);
2829
2830       children = sheet->children;
2831       while (children)
2832         {
2833           child = children->data;
2834           children = children->next;
2835
2836           if (GTK_WIDGET_VISIBLE (child->widget) &&
2837               GTK_WIDGET_MAPPED (child->widget))
2838             {
2839               gtk_widget_unmap (child->widget);
2840             }
2841         }
2842
2843     }
2844 }
2845
2846
2847 static void
2848 gtk_sheet_cell_draw_default (GtkSheet *sheet, gint row, gint col)
2849 {
2850   GtkWidget *widget;
2851   GdkGC *fg_gc, *bg_gc;
2852   GtkSheetCellAttr attributes;
2853   GdkRectangle area;
2854
2855   g_return_if_fail (sheet != NULL);
2856
2857   /* bail now if we arn't drawable yet */
2858   if (!GTK_WIDGET_DRAWABLE (sheet)) return;
2859
2860   if (row < 0 || row >= yyy_row_count (sheet)) return;
2861   if (col < 0 || col >= xxx_column_count (sheet)) return;
2862   if (! xxx_column_is_visible (sheet, col)) return;
2863   if (! yyy_row_is_visible (sheet, row)) return;
2864
2865   widget = GTK_WIDGET (sheet);
2866
2867   gtk_sheet_get_attributes (sheet, row, col, &attributes);
2868
2869   /* select GC for background rectangle */
2870   gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground);
2871   gdk_gc_set_foreground (sheet->bg_gc, &attributes.background);
2872
2873   fg_gc = sheet->fg_gc;
2874   bg_gc = sheet->bg_gc;
2875
2876   area.x = COLUMN_LEFT_XPIXEL (sheet, col);
2877   area.y = ROW_TOP_YPIXEL (sheet, row);
2878   area.width= xxx_column_width (sheet, col);
2879   area.height = yyy_row_height (sheet, row);
2880
2881   gdk_draw_rectangle (sheet->pixmap,
2882                       bg_gc,
2883                       TRUE,
2884                       area.x,
2885                       area.y,
2886                       area.width,
2887                       area.height);
2888
2889   gdk_gc_set_line_attributes (sheet->fg_gc, 1, 0, 0, 0);
2890
2891   if (sheet->show_grid)
2892     {
2893       gdk_gc_set_foreground (sheet->bg_gc, &sheet->grid_color);
2894
2895       gdk_draw_rectangle (sheet->pixmap,
2896                           sheet->bg_gc,
2897                           FALSE,
2898                           area.x, area.y,
2899                           area.width, area.height);
2900     }
2901 }
2902
2903 static void
2904 gtk_sheet_cell_draw_label (GtkSheet *sheet, gint row, gint col)
2905 {
2906   GtkWidget *widget;
2907   GdkRectangle area;
2908   gint i;
2909   gint text_width, text_height, y;
2910   gint xoffset = 0;
2911   gint size, sizel, sizer;
2912   GdkGC *fg_gc, *bg_gc;
2913   GtkSheetCellAttr attributes;
2914   PangoLayout *layout;
2915   PangoRectangle rect;
2916   PangoRectangle logical_rect;
2917   PangoLayoutLine *line;
2918   PangoFontMetrics *metrics;
2919   PangoContext *context = gtk_widget_get_pango_context (GTK_WIDGET (sheet));
2920   gint ascent, descent, y_pos;
2921
2922   gchar *label;
2923
2924   g_return_if_fail (sheet != NULL);
2925
2926   /* bail now if we aren't drawable yet */
2927   if (!GTK_WIDGET_DRAWABLE (sheet))
2928     return;
2929
2930   label = gtk_sheet_cell_get_text (sheet, row, col);
2931   if (!label)
2932     return;
2933
2934   if (row < 0 || row >= yyy_row_count (sheet)) return;
2935   if (col < 0 || col >= xxx_column_count (sheet)) return;
2936   if (! xxx_column_is_visible (sheet, col)) return;
2937   if (!yyy_row_is_visible (sheet, row)) return;
2938
2939
2940   widget = GTK_WIDGET (sheet);
2941
2942   gtk_sheet_get_attributes (sheet, row, col, &attributes);
2943
2944   /* select GC for background rectangle */
2945   gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground);
2946   gdk_gc_set_foreground (sheet->bg_gc, &attributes.background);
2947
2948   fg_gc = sheet->fg_gc;
2949   bg_gc = sheet->bg_gc;
2950
2951   area.x = COLUMN_LEFT_XPIXEL (sheet, col);
2952   area.y = ROW_TOP_YPIXEL (sheet, row);
2953   area.width = xxx_column_width (sheet, col);
2954   area.height = yyy_row_height (sheet, row);
2955
2956
2957   layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), label);
2958   dispose_string (sheet, label);
2959   pango_layout_set_font_description (layout, attributes.font_desc);
2960
2961   pango_layout_get_pixel_extents (layout, NULL, &rect);
2962
2963   line = pango_layout_get_lines (layout)->data;
2964   pango_layout_line_get_extents (line, NULL, &logical_rect);
2965
2966   metrics = pango_context_get_metrics (context,
2967                                        attributes.font_desc,
2968                                        pango_context_get_language (context));
2969
2970   ascent = pango_font_metrics_get_ascent (metrics) / PANGO_SCALE;
2971   descent = pango_font_metrics_get_descent (metrics) / PANGO_SCALE;
2972
2973   pango_font_metrics_unref (metrics);
2974
2975   /* Align primarily for locale's ascent / descent */
2976
2977   logical_rect.height /= PANGO_SCALE;
2978   logical_rect.y /= PANGO_SCALE;
2979   y_pos = area.height - logical_rect.height;
2980
2981   if (logical_rect.height > area.height)
2982     y_pos = (logical_rect.height - area.height - 2 * CELLOFFSET) / 2;
2983   else if (y_pos < 0)
2984     y_pos = 0;
2985   else if (y_pos + logical_rect.height > area.height)
2986     y_pos = area.height - logical_rect.height;
2987
2988   text_width = rect.width;
2989   text_height = rect.height;
2990   y = area.y + y_pos - CELLOFFSET;
2991
2992   switch (attributes.justification)
2993     {
2994     case GTK_JUSTIFY_RIGHT:
2995       size = area.width;
2996       area.x +=area.width;
2997       {
2998         for (i = col - 1; i >= MIN_VISIBLE_COLUMN (sheet); i--)
2999           {
3000             if ( !gtk_sheet_cell_empty (sheet, row, i)) break;
3001             if (size >= text_width + CELLOFFSET) break;
3002             size +=xxx_column_width (sheet, i);
3003             xxx_column_set_right_column (sheet, i,
3004                                          MAX (col,
3005                                               xxx_column_right_column (sheet, i)));
3006           }
3007         area.width = size;
3008       }
3009       area.x -= size;
3010       xoffset += area.width - text_width - 2 * CELLOFFSET -
3011         attributes.border.width / 2;
3012       break;
3013     case GTK_JUSTIFY_CENTER:
3014       sizel = area.width / 2;
3015       sizer = area.width / 2;
3016       area.x += area.width / 2;
3017       {
3018         for (i = col + 1; i <= MAX_VISIBLE_COLUMN (sheet); i++)
3019           {
3020             if ( ! gtk_sheet_cell_empty (sheet, row, i)) break;
3021             if (sizer >= text_width / 2) break;
3022             sizer += xxx_column_width (sheet, i);
3023             xxx_column_set_left_column (sheet, i,
3024                                         MIN (
3025                                              col,
3026                                              xxx_column_left_column (sheet, i)));
3027           }
3028         for (i = col - 1; i >= MIN_VISIBLE_COLUMN (sheet); i--)
3029           {
3030             if ( ! gtk_sheet_cell_empty (sheet, row, i)) break;
3031             if (sizel >= text_width / 2) break;
3032             sizel +=xxx_column_width (sheet, i);
3033             xxx_column_set_right_column (sheet, i,
3034                                          MAX (col,
3035                                               xxx_column_right_column (sheet, i)));
3036           }
3037         size = MIN (sizel, sizer);
3038       }
3039       area.x -= sizel;
3040       xoffset += sizel - text_width / 2 - CELLOFFSET;
3041       area.width = sizel + sizer;
3042       break;
3043     case GTK_JUSTIFY_LEFT:
3044     default:
3045       size = area.width;
3046       {
3047         for (i = col + 1; i <= MAX_VISIBLE_COLUMN (sheet); i++)
3048           {
3049             if (! gtk_sheet_cell_empty (sheet, row, i)) break;
3050             if (size >= text_width + CELLOFFSET) break;
3051             size +=xxx_column_width (sheet, i);
3052             xxx_column_set_left_column (sheet, i,
3053                                         MIN (
3054                                              col,
3055                                              xxx_column_left_column (sheet, i)));
3056
3057           }
3058         area.width = size;
3059       }
3060       xoffset += attributes.border.width / 2;
3061       break;
3062     }
3063
3064   gdk_gc_set_clip_rectangle (fg_gc, &area);
3065
3066
3067   gdk_draw_layout (sheet->pixmap, fg_gc,
3068                    area.x + xoffset + CELLOFFSET,
3069                    y,
3070                    layout);
3071
3072   gdk_gc_set_clip_rectangle (fg_gc, NULL);
3073   g_object_unref (layout);
3074
3075   gdk_draw_drawable (sheet->sheet_window,
3076                    GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
3077                    sheet->pixmap,
3078                    area.x,
3079                    area.y,
3080                    area.x,
3081                    area.y,
3082                    area.width,
3083                    area.height);
3084
3085 }
3086
3087 static void
3088 gtk_sheet_range_draw (GtkSheet *sheet, const GtkSheetRange *range)
3089 {
3090   gint i, j;
3091   GtkSheetRange drawing_range;
3092   GdkRectangle area;
3093
3094   g_return_if_fail (sheet != NULL);
3095   g_return_if_fail (GTK_SHEET (sheet));
3096
3097   if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (sheet))) return;
3098   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
3099   if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
3100
3101   if (range == NULL)
3102     {
3103       drawing_range.row0 = MIN_VISIBLE_ROW (sheet);
3104       drawing_range.col0 = MIN_VISIBLE_COLUMN (sheet);
3105       drawing_range.rowi = MIN (MAX_VISIBLE_ROW (sheet),
3106                                 yyy_row_count (sheet) - 1);
3107       drawing_range.coli = MAX_VISIBLE_COLUMN (sheet);
3108
3109
3110       gdk_draw_rectangle (sheet->pixmap,
3111                           GTK_WIDGET (sheet)->style->white_gc,
3112                           TRUE,
3113                           0, 0,
3114                           sheet->sheet_window_width,
3115                           sheet->sheet_window_height);
3116     }
3117   else
3118     {
3119       drawing_range.row0 = MAX (range->row0, MIN_VISIBLE_ROW (sheet));
3120       drawing_range.col0 = MAX (range->col0, MIN_VISIBLE_COLUMN (sheet));
3121       drawing_range.rowi = MIN (range->rowi, MAX_VISIBLE_ROW (sheet));
3122       drawing_range.coli = MIN (range->coli, MAX_VISIBLE_COLUMN (sheet));
3123     }
3124
3125   if (drawing_range.coli == xxx_column_count (sheet) - 1)
3126     {
3127       area.x = COLUMN_LEFT_XPIXEL (sheet,
3128                                    xxx_column_count (sheet) - 1) +
3129         xxx_column_width (sheet, xxx_column_count (sheet) - 1) + 1;
3130
3131       area.y = 0;
3132
3133       gdk_gc_set_foreground (sheet->fg_gc, &sheet->bg_color);
3134
3135       gdk_draw_rectangle (sheet->pixmap,
3136                           sheet->fg_gc,
3137                           TRUE,
3138                           area.x, area.y,
3139                           sheet->sheet_window_width - area.x,
3140                           sheet->sheet_window_height);
3141
3142       gdk_draw_drawable (sheet->sheet_window,
3143                        GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
3144                        sheet->pixmap,
3145                        area.x,
3146                        area.y,
3147                        area.x,
3148                        area.y,
3149                        sheet->sheet_window_width - area.x,
3150                        sheet->sheet_window_height);
3151     }
3152
3153   if (drawing_range.rowi == yyy_row_count (sheet) - 1)
3154     {
3155       area.x = 0;
3156       area.y = ROW_TOP_YPIXEL (sheet,
3157                                yyy_row_count (sheet) - 1) +
3158         yyy_row_height (sheet, yyy_row_count (sheet) - 1) + 1;
3159
3160       gdk_gc_set_foreground (sheet->fg_gc, &sheet->bg_color);
3161
3162       gdk_draw_rectangle (sheet->pixmap,
3163                           sheet->fg_gc,
3164                           TRUE,
3165                           area.x, area.y,
3166                           sheet->sheet_window_width,
3167                           sheet->sheet_window_height - area.y);
3168
3169       gdk_draw_drawable (sheet->sheet_window,
3170                        GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
3171                        sheet->pixmap,
3172                        area.x,
3173                        area.y,
3174                        area.x,
3175                        area.y,
3176                        sheet->sheet_window_width,
3177                        sheet->sheet_window_height - area.y);
3178     }
3179
3180   for (i = drawing_range.row0; i <= drawing_range.rowi; i++)
3181     for (j = drawing_range.col0; j <= drawing_range.coli; j++)
3182       {
3183         gtk_sheet_cell_draw_default (sheet, i, j);
3184         gtk_sheet_cell_draw_label (sheet, i, j);
3185       }
3186
3187   gtk_sheet_draw_backing_pixmap (sheet, drawing_range);
3188
3189   if (sheet->state != GTK_SHEET_NORMAL &&
3190       gtk_sheet_range_isvisible (sheet, sheet->range))
3191     gtk_sheet_range_draw_selection (sheet, drawing_range);
3192
3193   if (sheet->state == GTK_STATE_NORMAL &&
3194       sheet->active_cell.row >= drawing_range.row0 &&
3195       sheet->active_cell.row <= drawing_range.rowi &&
3196       sheet->active_cell.col >= drawing_range.col0 &&
3197       sheet->active_cell.col <= drawing_range.coli)
3198     gtk_sheet_show_active_cell (sheet);
3199 }
3200
3201 static void
3202 gtk_sheet_range_draw_selection (GtkSheet *sheet, GtkSheetRange range)
3203 {
3204   GdkRectangle area;
3205   gint i, j;
3206   GtkSheetRange aux;
3207
3208   if (range.col0 > sheet->range.coli || range.coli < sheet->range.col0 ||
3209       range.row0 > sheet->range.rowi || range.rowi < sheet->range.row0)
3210     return;
3211
3212   if (!gtk_sheet_range_isvisible (sheet, range)) return;
3213   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
3214
3215   aux = range;
3216
3217   range.col0 = MAX (sheet->range.col0, range.col0);
3218   range.coli = MIN (sheet->range.coli, range.coli);
3219   range.row0 = MAX (sheet->range.row0, range.row0);
3220   range.rowi = MIN (sheet->range.rowi, range.rowi);
3221
3222   range.col0 = MAX (range.col0, MIN_VISIBLE_COLUMN (sheet));
3223   range.coli = MIN (range.coli, MAX_VISIBLE_COLUMN (sheet));
3224   range.row0 = MAX (range.row0, MIN_VISIBLE_ROW (sheet));
3225   range.rowi = MIN (range.rowi, MAX_VISIBLE_ROW (sheet));
3226
3227   for (i = range.row0; i <= range.rowi; i++)
3228     {
3229       for (j = range.col0; j <= range.coli; j++)
3230         {
3231
3232           if (gtk_sheet_cell_get_state (sheet, i, j) == GTK_STATE_SELECTED &&
3233               xxx_column_is_visible (sheet, j) && yyy_row_is_visible (sheet, i))
3234             {
3235
3236               area.x = COLUMN_LEFT_XPIXEL (sheet, j);
3237               area.y = ROW_TOP_YPIXEL (sheet, i);
3238               area.width= xxx_column_width (sheet, j);
3239               area.height = yyy_row_height (sheet, i);
3240
3241               if (i == sheet->range.row0)
3242                 {
3243                   area.y = area.y + 2;
3244                   area.height = area.height - 2;
3245                 }
3246               if (i == sheet->range.rowi) area.height = area.height - 3;
3247               if (j == sheet->range.col0)
3248                 {
3249                   area.x = area.x + 2;
3250                   area.width = area.width - 2;
3251                 }
3252               if (j == sheet->range.coli) area.width = area.width - 3;
3253
3254               if (i != sheet->active_cell.row || j != sheet->active_cell.col)
3255                 {
3256                   gdk_draw_rectangle (sheet->sheet_window,
3257                                       sheet->xor_gc,
3258                                       TRUE,
3259                                       area.x + 1, area.y + 1,
3260                                       area.width, area.height);
3261                 }
3262             }
3263
3264         }
3265     }
3266
3267   gtk_sheet_draw_border (sheet, sheet->range);
3268 }
3269
3270 static void
3271 gtk_sheet_draw_backing_pixmap (GtkSheet *sheet, GtkSheetRange range)
3272 {
3273   gint x, y, width, height;
3274
3275   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
3276
3277   x = COLUMN_LEFT_XPIXEL (sheet, range.col0);
3278   y = ROW_TOP_YPIXEL (sheet, range.row0);
3279   width = COLUMN_LEFT_XPIXEL (sheet, range.coli) - x +
3280     xxx_column_width (sheet, range.coli);
3281
3282   height = ROW_TOP_YPIXEL (sheet, range.rowi)- y + yyy_row_height (sheet, range.rowi);
3283
3284   if (range.row0 == sheet->range.row0)
3285     {
3286       y = y - 5;
3287       height = height + 5;
3288     }
3289   if (range.rowi == sheet->range.rowi) height = height + 5;
3290   if (range.col0 == sheet->range.col0)
3291     {
3292       x = x - 5;
3293       width = width + 5;
3294     }
3295   if (range.coli == sheet->range.coli) width = width + 5;
3296
3297   width = MIN (width, sheet->sheet_window_width - x);
3298   height = MIN (height, sheet->sheet_window_height - y);
3299
3300   x--;
3301   y--;
3302   width +=2;
3303   height +=2;
3304
3305   x = (sheet->row_titles_visible)
3306     ? MAX (x, sheet->row_title_area.width) : MAX (x, 0);
3307   y = (sheet->column_titles_visible)
3308     ? MAX (y, sheet->column_title_area.height) : MAX (y, 0);
3309
3310   if (range.coli == xxx_column_count (sheet) - 1)
3311     width = sheet->sheet_window_width - x;
3312   if (range.rowi == yyy_row_count (sheet) - 1)
3313     height = sheet->sheet_window_height - y;
3314
3315   gdk_draw_drawable (sheet->sheet_window,
3316                    GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
3317                    sheet->pixmap,
3318                    x,
3319                    y,
3320                    x,
3321                    y,
3322                    width + 1,
3323                    height + 1);
3324 }
3325
3326
3327 void
3328 gtk_sheet_set_cell_text (GtkSheet *sheet, gint row, gint col, const gchar *text)
3329 {
3330   GtkSheetCellAttr attributes;
3331
3332   g_return_if_fail (sheet != NULL);
3333   g_return_if_fail (GTK_IS_SHEET (sheet));
3334   if (col >= xxx_column_count (sheet) || row >= yyy_row_count (sheet)) return;
3335   if (col < 0 || row < 0) return;
3336
3337   gtk_sheet_get_attributes (sheet, row, col, &attributes);
3338   gtk_sheet_set_cell (sheet, row, col, attributes.justification, text);
3339 }
3340
3341 static inline gint
3342 safe_strcmp (const gchar *s1, const gchar *s2)
3343 {
3344   if ( !s1 && !s2) return 0;
3345   if ( !s1) return - 1;
3346   if ( !s2) return +1;
3347   return strcmp (s1, s2);
3348 }
3349
3350 void
3351 gtk_sheet_set_cell (GtkSheet *sheet, gint row, gint col,
3352                     GtkJustification justification,
3353                     const gchar *text)
3354 {
3355   GSheetModel *model ;
3356   gboolean changed ;
3357   gchar *old_text ;
3358
3359   GtkSheetRange range;
3360   gint text_width;
3361   GtkSheetCellAttr attributes;
3362
3363   g_return_if_fail (sheet != NULL);
3364   g_return_if_fail (GTK_IS_SHEET (sheet));
3365   if (col >= xxx_column_count (sheet) || row >= yyy_row_count (sheet)) return;
3366   if (col < 0 || row < 0) return;
3367
3368   gtk_sheet_get_attributes (sheet, row, col, &attributes);
3369
3370   attributes.justification = justification;
3371
3372   model = gtk_sheet_get_model (sheet);
3373
3374   old_text = g_sheet_model_get_string (model, row, col);
3375
3376   changed = FALSE;
3377
3378   if (0 != safe_strcmp (old_text, text))
3379     changed = g_sheet_model_set_string (model, text, row, col);
3380
3381   if ( g_sheet_model_free_strings (model))
3382     g_free (old_text);
3383
3384
3385   if (changed && attributes.is_visible)
3386     {
3387       gchar *s = gtk_sheet_cell_get_text (sheet, row, col);
3388       text_width = 0;
3389       if (s && strlen (s) > 0)
3390         {
3391           text_width = STRING_WIDTH (GTK_WIDGET (sheet),
3392                                      attributes.font_desc, text);
3393         }
3394       dispose_string (sheet, s);
3395
3396       range.row0 = row;
3397       range.rowi = row;
3398       range.col0 = MIN_VISIBLE_COLUMN (sheet);
3399       range.coli = MAX_VISIBLE_COLUMN (sheet);
3400
3401       if (gtk_sheet_autoresize (sheet) &&
3402           text_width > xxx_column_width (sheet, col) -
3403           2 * CELLOFFSET- attributes.border.width)
3404         {
3405           gtk_sheet_set_column_width (sheet, col, text_width + 2 * CELLOFFSET
3406                                       + attributes.border.width);
3407           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_REDRAW_PENDING);
3408         }
3409       else
3410         if (!GTK_SHEET_IS_FROZEN (sheet))
3411           gtk_sheet_range_draw (sheet, &range);
3412     }
3413
3414   if ( changed )
3415     g_signal_emit (sheet, sheet_signals[CHANGED], 0, row, col);
3416
3417 }
3418
3419
3420 void
3421 gtk_sheet_cell_clear (GtkSheet *sheet, gint row, gint column)
3422 {
3423   GtkSheetRange range;
3424
3425   g_return_if_fail (sheet != NULL);
3426   g_return_if_fail (GTK_IS_SHEET (sheet));
3427   if (column >= xxx_column_count (sheet) ||
3428       row >= yyy_row_count (sheet)) return;
3429
3430   if (column < 0 || row < 0) return;
3431
3432   range.row0 = row;
3433   range.rowi = row;
3434   range.col0 = MIN_VISIBLE_COLUMN (sheet);
3435   range.coli = MAX_VISIBLE_COLUMN (sheet);
3436
3437   gtk_sheet_real_cell_clear (sheet, row, column);
3438
3439   if (!GTK_SHEET_IS_FROZEN (sheet))
3440     {
3441       gtk_sheet_range_draw (sheet, &range);
3442     }
3443 }
3444
3445 static void
3446 gtk_sheet_real_cell_clear (GtkSheet *sheet, gint row, gint column)
3447 {
3448   GSheetModel *model = gtk_sheet_get_model (sheet);
3449
3450   gchar *old_text = gtk_sheet_cell_get_text (sheet, row, column);
3451
3452   if (old_text && strlen (old_text) > 0 )
3453     {
3454       g_sheet_model_datum_clear (model, row, column);
3455     }
3456
3457   dispose_string (sheet, old_text);
3458 }
3459
3460 void
3461 gtk_sheet_range_clear (GtkSheet *sheet, const GtkSheetRange *range)
3462 {
3463   g_return_if_fail (sheet != NULL);
3464   g_return_if_fail (GTK_IS_SHEET (sheet));
3465
3466   gtk_sheet_real_range_clear (sheet, range);
3467 }
3468
3469 static void
3470 gtk_sheet_real_range_clear (GtkSheet *sheet, const GtkSheetRange *range)
3471 {
3472   gint i, j;
3473   GtkSheetRange clear;
3474
3475   if (!range)
3476     {
3477       clear.row0 = 0;
3478       clear.rowi = yyy_row_count (sheet) - 1;
3479       clear.col0 = 0;
3480       clear.coli = xxx_column_count (sheet) - 1;
3481     }
3482   else
3483     clear=*range;
3484
3485   clear.row0 = MAX (clear.row0, 0);
3486   clear.col0 = MAX (clear.col0, 0);
3487   clear.rowi = MIN (clear.rowi, yyy_row_count (sheet) - 1 );
3488   clear.coli = MIN (clear.coli, xxx_column_count (sheet) - 1 );
3489
3490   for (i = clear.row0; i <= clear.rowi; i++)
3491     for (j = clear.col0; j <= clear.coli; j++)
3492       {
3493         gtk_sheet_real_cell_clear (sheet, i, j);
3494       }
3495
3496   gtk_sheet_range_draw (sheet, NULL);
3497 }
3498
3499
3500 static gboolean
3501 gtk_sheet_cell_empty (const GtkSheet *sheet, gint row, gint col)
3502 {
3503   gboolean empty;
3504   char *text = gtk_sheet_cell_get_text (sheet, row, col);
3505   empty = (text == NULL );
3506
3507   dispose_string (sheet, text);
3508
3509   return empty;
3510 }
3511
3512
3513 gchar *
3514 gtk_sheet_cell_get_text (const GtkSheet *sheet, gint row, gint col)
3515 {
3516   GSheetModel *model;
3517   g_return_val_if_fail (sheet != NULL, NULL);
3518   g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);
3519
3520   if (col >= xxx_column_count (sheet) || row >= yyy_row_count (sheet))
3521     return NULL;
3522   if (col < 0 || row < 0) return NULL;
3523
3524   model = gtk_sheet_get_model (sheet);
3525
3526   if ( !model )
3527     return NULL;
3528
3529   return g_sheet_model_get_string (model, row, col);
3530 }
3531
3532
3533 GtkStateType
3534 gtk_sheet_cell_get_state (GtkSheet *sheet, gint row, gint col)
3535 {
3536   gint state;
3537   GtkSheetRange *range;
3538
3539   g_return_val_if_fail (sheet != NULL, 0);
3540   g_return_val_if_fail (GTK_IS_SHEET (sheet), 0);
3541   if (col >= xxx_column_count (sheet) || row >= yyy_row_count (sheet)) return 0;
3542   if (col < 0 || row < 0) return 0;
3543
3544   state = sheet->state;
3545   range = &sheet->range;
3546
3547   switch (state)
3548     {
3549     case GTK_SHEET_NORMAL:
3550       return GTK_STATE_NORMAL;
3551       break;
3552     case GTK_SHEET_ROW_SELECTED:
3553       if (row >= range->row0 && row <= range->rowi)
3554         return GTK_STATE_SELECTED;
3555       break;
3556     case GTK_SHEET_COLUMN_SELECTED:
3557       if (col >= range->col0 && col <= range->coli)
3558         return GTK_STATE_SELECTED;
3559       break;
3560     case GTK_SHEET_RANGE_SELECTED:
3561       if (row >= range->row0 && row <= range->rowi && \
3562           col >= range->col0 && col <= range->coli)
3563         return GTK_STATE_SELECTED;
3564       break;
3565     }
3566   return GTK_STATE_NORMAL;
3567 }
3568
3569 /* Convert X, Y (in pixels) to *ROW, *COLUMN (in cell coords)
3570    -1 indicates the title buttons.
3571    If the function returns FALSE, then the results will be unreliable.
3572 */
3573 gboolean
3574 gtk_sheet_get_pixel_info (GtkSheet *sheet,
3575                           gint x,
3576                           gint y,
3577                           gint *row,
3578                           gint *column)
3579 {
3580   gint trow, tcol;
3581   *row = -G_MAXINT;
3582   *column = -G_MAXINT;
3583
3584   g_return_val_if_fail (sheet != NULL, 0);
3585   g_return_val_if_fail (GTK_IS_SHEET (sheet), 0);
3586
3587   /* bounds checking, return false if the user clicked
3588      on a blank area */
3589   if (y < 0)
3590     return FALSE;
3591
3592   if (x < 0)
3593     return FALSE;
3594
3595   if ( y < sheet->column_title_area.height + sheet->column_title_area.y)
3596     *row = -1;
3597
3598   else
3599     {
3600       trow = ROW_FROM_YPIXEL (sheet, y);
3601       if (trow > yyy_row_count (sheet))
3602         return FALSE;
3603
3604       *row = trow;
3605     }
3606
3607   if ( x < sheet->row_title_area.width + sheet->row_title_area.x)
3608     *column = -1;
3609   else
3610     {
3611       tcol = COLUMN_FROM_XPIXEL (sheet, x);
3612       if (tcol > xxx_column_count (sheet))
3613         return FALSE;
3614
3615       *column = tcol;
3616     }
3617
3618   return TRUE;
3619 }
3620
3621 gboolean
3622 gtk_sheet_get_cell_area (GtkSheet * sheet,
3623                          gint row,
3624                          gint column,
3625                          GdkRectangle *area)
3626 {
3627   g_return_val_if_fail (sheet != NULL, 0);
3628   g_return_val_if_fail (GTK_IS_SHEET (sheet), 0);
3629
3630   if (row >= yyy_row_count (sheet) || column >= xxx_column_count (sheet))
3631     return FALSE;
3632
3633   area->x = (column == -1) ? 0 : (COLUMN_LEFT_XPIXEL (sheet, column) -
3634                                   (sheet->row_titles_visible
3635                                    ? sheet->row_title_area.width
3636                                    : 0));
3637   area->y = (row == -1) ? 0 : (ROW_TOP_YPIXEL (sheet, row) -
3638                                (sheet->column_titles_visible
3639                                 ? sheet->column_title_area.height
3640                                 : 0));
3641   area->width= (column == -1) ? sheet->row_title_area.width
3642     : xxx_column_width (sheet, column);
3643
3644   area->height= (row == -1) ? sheet->column_title_area.height
3645     : yyy_row_height (sheet, row);
3646
3647   return TRUE;
3648 }
3649
3650 gboolean
3651 gtk_sheet_set_active_cell (GtkSheet *sheet, gint row, gint column)
3652 {
3653   g_return_val_if_fail (sheet != NULL, 0);
3654   g_return_val_if_fail (GTK_IS_SHEET (sheet), 0);
3655
3656   if (row < - 1 || column < - 1) return FALSE;
3657   if (row >= yyy_row_count (sheet) || column >= xxx_column_count (sheet))
3658     return FALSE;
3659
3660   if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
3661     gtk_sheet_deactivate_cell (sheet);
3662
3663   sheet->active_cell.row = row;
3664   sheet->active_cell.col = column;
3665
3666   if ( row == -1 || column == -1)
3667     {
3668       gtk_sheet_hide_active_cell (sheet);
3669       return TRUE;
3670     }
3671
3672   if (!gtk_sheet_activate_cell (sheet, row, column)) return FALSE;
3673
3674   if (gtk_sheet_autoscroll (sheet))
3675     gtk_sheet_move_query (sheet, row, column);
3676
3677   return TRUE;
3678 }
3679
3680 void
3681 gtk_sheet_get_active_cell (GtkSheet *sheet, gint *row, gint *column)
3682 {
3683   g_return_if_fail (sheet != NULL);
3684   g_return_if_fail (GTK_IS_SHEET (sheet));
3685
3686   if ( row ) *row = sheet->active_cell.row;
3687   if (column) *column = sheet->active_cell.col;
3688 }
3689
3690 static void
3691 gtk_sheet_entry_changed (GtkWidget *widget, gpointer data)
3692 {
3693   GtkSheet *sheet;
3694   gint row, col;
3695   const char *text;
3696   GtkJustification justification;
3697   GtkSheetCellAttr attributes;
3698
3699   g_return_if_fail (data != NULL);
3700   g_return_if_fail (GTK_IS_SHEET (data));
3701
3702   sheet = GTK_SHEET (data);
3703
3704   if (!GTK_WIDGET_VISIBLE (widget)) return;
3705   if (sheet->state != GTK_STATE_NORMAL) return;
3706
3707   row = sheet->active_cell.row;
3708   col = sheet->active_cell.col;
3709
3710   if (row < 0 || col < 0) return;
3711
3712   sheet->active_cell.row = -1;
3713   sheet->active_cell.col = -1;
3714
3715   text = gtk_entry_get_text (GTK_ENTRY (gtk_sheet_get_entry (sheet)));
3716
3717   GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IS_FROZEN);
3718
3719   if (text && strlen (text) > 0)
3720     {
3721       gtk_sheet_get_attributes (sheet, row, col, &attributes);
3722       justification = attributes.justification;
3723       gtk_sheet_set_cell (sheet, row, col, justification, text);
3724     }
3725
3726   if (sheet->freeze_count == 0)
3727     GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IS_FROZEN);
3728
3729   sheet->active_cell.row = row;;
3730   sheet->active_cell.col = col;
3731 }
3732
3733
3734 static void
3735 gtk_sheet_deactivate_cell (GtkSheet *sheet)
3736 {
3737   g_return_if_fail (sheet != NULL);
3738   g_return_if_fail (GTK_IS_SHEET (sheet));
3739
3740   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return ;
3741   if (sheet->state != GTK_SHEET_NORMAL) return ;
3742
3743   if ( sheet->active_cell.row == -1 || sheet->active_cell.col == -1 )
3744     return ;
3745
3746   g_signal_emit (sheet, sheet_signals[DEACTIVATE], 0,
3747                  sheet->active_cell.row,
3748                  sheet->active_cell.col);
3749
3750
3751   g_signal_handlers_disconnect_by_func (gtk_sheet_get_entry (sheet),
3752                                         G_CALLBACK (gtk_sheet_entry_changed),
3753                                         sheet);
3754
3755   gtk_sheet_hide_active_cell (sheet);
3756   sheet->active_cell.row = -1;
3757   sheet->active_cell.col = -1;
3758
3759   if (GTK_SHEET_REDRAW_PENDING (sheet))
3760     {
3761       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_REDRAW_PENDING);
3762       gtk_sheet_range_draw (sheet, NULL);
3763     }
3764 }
3765
3766 static void
3767 gtk_sheet_hide_active_cell (GtkSheet *sheet)
3768 {
3769   const char *text;
3770   gint row, col;
3771   GtkJustification justification;
3772   GtkSheetCellAttr attributes;
3773
3774   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
3775
3776   row = sheet->active_cell.row;
3777   col = sheet->active_cell.col;
3778
3779   if (row < 0 || col < 0) return;
3780
3781   if (sheet->freeze_count == 0)
3782     GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IS_FROZEN);
3783
3784   text = gtk_entry_get_text (GTK_ENTRY (gtk_sheet_get_entry (sheet)));
3785
3786   gtk_sheet_get_attributes (sheet, row, col, &attributes);
3787   justification = attributes.justification;
3788
3789   row = sheet->active_cell.row;
3790   col = sheet->active_cell.col;
3791
3792   gtk_widget_hide (sheet->entry_widget);
3793   gtk_widget_unmap (sheet->entry_widget);
3794
3795   if (row != -1 && col != -1)
3796     gdk_draw_drawable (sheet->sheet_window,
3797                      GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
3798                      sheet->pixmap,
3799                      COLUMN_LEFT_XPIXEL (sheet, col)- 1,
3800                      ROW_TOP_YPIXEL (sheet, row)- 1,
3801                      COLUMN_LEFT_XPIXEL (sheet, col)- 1,
3802                      ROW_TOP_YPIXEL (sheet, row)- 1,
3803                      xxx_column_width (sheet, col) + 4,
3804                      yyy_row_height (sheet, row)+4);
3805
3806   gtk_widget_grab_focus (GTK_WIDGET (sheet));
3807
3808   GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
3809
3810 }
3811
3812 static gboolean
3813 gtk_sheet_activate_cell (GtkSheet *sheet, gint row, gint col)
3814 {
3815   gboolean veto = TRUE;
3816
3817   g_return_val_if_fail (sheet != NULL, FALSE);
3818   g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE);
3819
3820   if (row < 0 || col < 0) return FALSE;
3821
3822   if ( row > yyy_row_count (sheet) || col > xxx_column_count (sheet))
3823     return FALSE;
3824
3825   if (!veto) return FALSE;
3826   if (sheet->state != GTK_SHEET_NORMAL)
3827     {
3828       sheet->state = GTK_SHEET_NORMAL;
3829       gtk_sheet_real_unselect_range (sheet, NULL);
3830     }
3831
3832   sheet->range.row0 = row;
3833   sheet->range.col0 = col;
3834   sheet->range.rowi = row;
3835   sheet->range.coli = col;
3836   sheet->active_cell.row = row;
3837   sheet->active_cell.col = col;
3838   sheet->selection_cell.row = row;
3839   sheet->selection_cell.col = col;
3840
3841   GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
3842
3843   gtk_sheet_show_active_cell (sheet);
3844
3845   g_signal_connect (gtk_sheet_get_entry (sheet),
3846                     "changed",
3847                     G_CALLBACK (gtk_sheet_entry_changed),
3848                     sheet);
3849
3850   _gtkextra_signal_emit (GTK_OBJECT (sheet), sheet_signals [ACTIVATE], row, col, &veto);
3851
3852   return TRUE;
3853 }
3854
3855 static void
3856 gtk_sheet_show_active_cell (GtkSheet *sheet)
3857 {
3858   GtkEntry *sheet_entry;
3859   GtkSheetCellAttr attributes;
3860   gchar *text = NULL;
3861   const gchar *old_text;
3862   GtkJustification justification;
3863   gint row, col;
3864
3865   g_return_if_fail (sheet != NULL);
3866   g_return_if_fail (GTK_IS_SHEET (sheet));
3867
3868   row = sheet->active_cell.row;
3869   col = sheet->active_cell.col;
3870
3871   /* Don't show the active cell, if there is no active cell: */
3872   if (! (row >= 0 && col >= 0)) /* e.g row or coll == -1. */
3873     return;
3874
3875   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
3876   if (sheet->state != GTK_SHEET_NORMAL) return;
3877   if (GTK_SHEET_IN_SELECTION (sheet)) return;
3878
3879   GTK_WIDGET_SET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
3880
3881   sheet_entry = GTK_ENTRY (gtk_sheet_get_entry (sheet));
3882
3883   gtk_sheet_get_attributes (sheet, row, col, &attributes);
3884
3885   justification = GTK_JUSTIFY_LEFT;
3886
3887   if (gtk_sheet_justify_entry (sheet))
3888     justification = attributes.justification;
3889
3890   text = gtk_sheet_cell_get_text (sheet, row, col);
3891   if ( ! text )
3892     text = g_strdup ("");
3893
3894   gtk_entry_set_visibility (GTK_ENTRY (sheet_entry), attributes.is_visible);
3895
3896
3897   /*** Added by John Gotts. Mar 25, 2005 *********/
3898   old_text = gtk_entry_get_text (GTK_ENTRY (sheet_entry));
3899   if (strcmp (old_text, text) != 0)
3900     {
3901       if (!GTK_IS_ITEM_ENTRY (sheet_entry))
3902         gtk_entry_set_text (GTK_ENTRY (sheet_entry), text);
3903       else
3904         gtk_item_entry_set_text (GTK_ITEM_ENTRY (sheet_entry), text, justification);
3905     }
3906
3907   gtk_sheet_entry_set_max_size (sheet);
3908   gtk_sheet_size_allocate_entry (sheet);
3909
3910   gtk_widget_map (sheet->entry_widget);
3911
3912   gtk_widget_grab_focus (GTK_WIDGET (sheet_entry));
3913
3914   dispose_string (sheet, text);
3915 }
3916
3917 static void
3918 gtk_sheet_draw_active_cell (GtkSheet *sheet)
3919 {
3920   gint row, col;
3921   GtkSheetRange range;
3922
3923   if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (sheet))) return;
3924   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
3925
3926   row = sheet->active_cell.row;
3927   col = sheet->active_cell.col;
3928
3929   if (row < 0 || col < 0) return;
3930
3931   if (!gtk_sheet_cell_isvisible (sheet, row, col)) return;
3932
3933   range.col0 = range.coli = col;
3934   range.row0 = range.rowi = row;
3935
3936   gtk_sheet_draw_border (sheet, range);
3937 }
3938
3939
3940 static void
3941 gtk_sheet_make_backing_pixmap (GtkSheet *sheet, guint width, guint height)
3942 {
3943   gint pixmap_width, pixmap_height;
3944
3945   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
3946
3947   if (width == 0 && height == 0)
3948     {
3949       width = sheet->sheet_window_width + 80;
3950       height = sheet->sheet_window_height + 80;
3951     }
3952
3953   if (!sheet->pixmap)
3954     {
3955       /* allocate */
3956       sheet->pixmap = gdk_pixmap_new (sheet->sheet_window,
3957                                       width, height,
3958                                       - 1);
3959       if (!GTK_SHEET_IS_FROZEN (sheet)) gtk_sheet_range_draw (sheet, NULL);
3960     }
3961   else
3962     {
3963       /* reallocate if sizes don't match */
3964       gdk_drawable_get_size (sheet->pixmap,
3965                              &pixmap_width, &pixmap_height);
3966       if ( (pixmap_width != width) || (pixmap_height != height))
3967         {
3968           g_object_unref (sheet->pixmap);
3969           sheet->pixmap = gdk_pixmap_new (sheet->sheet_window,
3970                                           width, height,
3971                                           - 1);
3972           if (!GTK_SHEET_IS_FROZEN (sheet)) gtk_sheet_range_draw (sheet, NULL);
3973         }
3974     }
3975 }
3976
3977 static void
3978 gtk_sheet_new_selection (GtkSheet *sheet, GtkSheetRange *range)
3979 {
3980   gint i, j, mask1, mask2;
3981   gint state, selected;
3982   gint x, y, width, height;
3983   GtkSheetRange new_range, aux_range;
3984
3985   g_return_if_fail (sheet != NULL);
3986
3987   if (range == NULL) range=&sheet->range;
3988
3989   new_range=*range;
3990
3991   range->row0 = MIN (range->row0, sheet->range.row0);
3992   range->rowi = MAX (range->rowi, sheet->range.rowi);
3993   range->col0 = MIN (range->col0, sheet->range.col0);
3994   range->coli = MAX (range->coli, sheet->range.coli);
3995
3996   range->row0 = MAX (range->row0, MIN_VISIBLE_ROW (sheet));
3997   range->rowi = MIN (range->rowi, MAX_VISIBLE_ROW (sheet));
3998   range->col0 = MAX (range->col0, MIN_VISIBLE_COLUMN (sheet));
3999   range->coli = MIN (range->coli, MAX_VISIBLE_COLUMN (sheet));
4000
4001   aux_range.row0 = MAX (new_range.row0, MIN_VISIBLE_ROW (sheet));
4002   aux_range.rowi = MIN (new_range.rowi, MAX_VISIBLE_ROW (sheet));
4003   aux_range.col0 = MAX (new_range.col0, MIN_VISIBLE_COLUMN (sheet));
4004   aux_range.coli = MIN (new_range.coli, MAX_VISIBLE_COLUMN (sheet));
4005
4006   for (i = range->row0; i <= range->rowi; i++)
4007     {
4008       for (j = range->col0; j <= range->coli; j++)
4009         {
4010
4011           state = gtk_sheet_cell_get_state (sheet, i, j);
4012           selected= (i <= new_range.rowi && i >= new_range.row0 &&
4013                      j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
4014
4015           if (state == GTK_STATE_SELECTED && selected &&
4016               xxx_column_is_visible (sheet, j) && yyy_row_is_visible (sheet, i) &&
4017               (i == sheet->range.row0 || i == sheet->range.rowi ||
4018                j == sheet->range.col0 || j == sheet->range.coli ||
4019                i == new_range.row0 || i == new_range.rowi ||
4020                j == new_range.col0 || j == new_range.coli))
4021             {
4022
4023               mask1 = i == sheet->range.row0 ? 1 : 0;
4024               mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1;
4025               mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1;
4026               mask1 = j == sheet->range.coli ? mask1 + 8 : mask1;
4027
4028               mask2 = i == new_range.row0 ? 1 : 0;
4029               mask2 = i == new_range.rowi ? mask2 + 2 : mask2;
4030               mask2 = j == new_range.col0 ? mask2 + 4 : mask2;
4031               mask2 = j == new_range.coli ? mask2 + 8 : mask2;
4032
4033               if (mask1 != mask2)
4034                 {
4035                   x = COLUMN_LEFT_XPIXEL (sheet, j);
4036                   y = ROW_TOP_YPIXEL (sheet, i);
4037                   width = COLUMN_LEFT_XPIXEL (sheet, j)- x+
4038                     xxx_column_width (sheet, j);
4039                   height = ROW_TOP_YPIXEL (sheet, i)- y + yyy_row_height (sheet, i);
4040
4041                   if (i == sheet->range.row0)
4042                     {
4043                       y = y - 3;
4044                       height = height + 3;
4045                     }
4046                   if (i == sheet->range.rowi) height = height + 3;
4047                   if (j == sheet->range.col0)
4048                     {
4049                       x = x - 3;
4050                       width = width + 3;
4051                     }
4052                   if (j == sheet->range.coli) width = width + 3;
4053
4054                   gdk_draw_drawable (sheet->sheet_window,
4055                                    GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
4056                                    sheet->pixmap,
4057                                    x + 1,
4058                                    y + 1,
4059                                    x + 1,
4060                                    y + 1,
4061                                    width,
4062                                    height);
4063
4064                   if (i != sheet->active_cell.row || j != sheet->active_cell.col)
4065                     {
4066                       x = COLUMN_LEFT_XPIXEL (sheet, j);
4067                       y = ROW_TOP_YPIXEL (sheet, i);
4068                       width = COLUMN_LEFT_XPIXEL (sheet, j)- x+
4069                         xxx_column_width (sheet, j);
4070
4071                       height = ROW_TOP_YPIXEL (sheet, i)- y + yyy_row_height (sheet, i);
4072
4073                       if (i == new_range.row0)
4074                         {
4075                           y = y+2;
4076                           height = height - 2;
4077                         }
4078                       if (i == new_range.rowi) height = height - 3;
4079                       if (j == new_range.col0)
4080                         {
4081                           x = x+2;
4082                           width = width - 2;
4083                         }
4084                       if (j == new_range.coli) width = width - 3;
4085
4086                       gdk_draw_rectangle (sheet->sheet_window,
4087                                           sheet->xor_gc,
4088                                           TRUE,
4089                                           x + 1, y + 1,
4090                                           width, height);
4091                     }
4092                 }
4093             }
4094         }
4095     }
4096
4097   for (i = range->row0; i <= range->rowi; i++)
4098     {
4099       for (j = range->col0; j <= range->coli; j++)
4100         {
4101
4102           state = gtk_sheet_cell_get_state (sheet, i, j);
4103           selected= (i <= new_range.rowi && i >= new_range.row0 &&
4104                      j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
4105
4106           if (state == GTK_STATE_SELECTED && !selected &&
4107               xxx_column_is_visible (sheet, j) && yyy_row_is_visible (sheet, i))
4108             {
4109
4110               x = COLUMN_LEFT_XPIXEL (sheet, j);
4111               y = ROW_TOP_YPIXEL (sheet, i);
4112               width = COLUMN_LEFT_XPIXEL (sheet, j)- x+ xxx_column_width (sheet, j);
4113               height = ROW_TOP_YPIXEL (sheet, i)- y + yyy_row_height (sheet, i);
4114
4115               if (i == sheet->range.row0)
4116                 {
4117                   y = y - 3;
4118                   height = height + 3;
4119                 }
4120               if (i == sheet->range.rowi) height = height + 3;
4121               if (j == sheet->range.col0)
4122                 {
4123                   x = x - 3;
4124                   width = width + 3;
4125                 }
4126               if (j == sheet->range.coli) width = width + 3;
4127
4128               gdk_draw_drawable (sheet->sheet_window,
4129                                GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
4130                                sheet->pixmap,
4131                                x + 1,
4132                                y + 1,
4133                                x + 1,
4134                                y + 1,
4135                                width,
4136                                height);
4137             }
4138         }
4139     }
4140
4141   for (i = range->row0; i <= range->rowi; i++)
4142     {
4143       for (j = range->col0; j <= range->coli; j++)
4144         {
4145
4146           state = gtk_sheet_cell_get_state (sheet, i, j);
4147           selected= (i <= new_range.rowi && i >= new_range.row0 &&
4148                      j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
4149
4150           if (state != GTK_STATE_SELECTED && selected &&
4151               xxx_column_is_visible (sheet, j) && yyy_row_is_visible (sheet, i) &&
4152               (i != sheet->active_cell.row || j != sheet->active_cell.col))
4153             {
4154
4155               x = COLUMN_LEFT_XPIXEL (sheet, j);
4156               y = ROW_TOP_YPIXEL (sheet, i);
4157               width = COLUMN_LEFT_XPIXEL (sheet, j)- x+ xxx_column_width (sheet, j);
4158               height = ROW_TOP_YPIXEL (sheet, i)- y + yyy_row_height (sheet, i);
4159
4160               if (i == new_range.row0)
4161                 {
4162                   y = y+2;
4163                   height = height - 2;
4164                 }
4165               if (i == new_range.rowi) height = height - 3;
4166               if (j == new_range.col0)
4167                 {
4168                   x = x+2;
4169                   width = width - 2;
4170                 }
4171               if (j == new_range.coli) width = width - 3;
4172
4173               gdk_draw_rectangle (sheet->sheet_window,
4174                                   sheet->xor_gc,
4175                                   TRUE,
4176                                   x + 1, y + 1,
4177                                   width, height);
4178
4179             }
4180
4181         }
4182     }
4183
4184   for (i = aux_range.row0; i <= aux_range.rowi; i++)
4185     {
4186       for (j = aux_range.col0; j <= aux_range.coli; j++)
4187         {
4188
4189           if (xxx_column_is_visible (sheet, j) && yyy_row_is_visible (sheet, i))
4190             {
4191
4192               state = gtk_sheet_cell_get_state (sheet, i, j);
4193
4194               mask1 = i == sheet->range.row0 ? 1 : 0;
4195               mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1;
4196               mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1;
4197               mask1 = j == sheet->range.coli ? mask1 + 8 : mask1;
4198
4199               mask2 = i == new_range.row0 ? 1 : 0;
4200               mask2 = i == new_range.rowi ? mask2 + 2 : mask2;
4201               mask2 = j == new_range.col0 ? mask2 + 4 : mask2;
4202               mask2 = j == new_range.coli ? mask2 + 8 : mask2;
4203               if (mask2 != mask1 || (mask2 == mask1 && state != GTK_STATE_SELECTED))
4204                 {
4205                   x = COLUMN_LEFT_XPIXEL (sheet, j);
4206                   y = ROW_TOP_YPIXEL (sheet, i);
4207                   width = xxx_column_width (sheet, j);
4208                   height = yyy_row_height (sheet, i);
4209                   if (mask2 & 1)
4210                     gdk_draw_rectangle (sheet->sheet_window,
4211                                         sheet->xor_gc,
4212                                         TRUE,
4213                                         x + 1, y - 1,
4214                                         width, 3);
4215
4216
4217                   if (mask2 & 2)
4218                     gdk_draw_rectangle (sheet->sheet_window,
4219                                         sheet->xor_gc,
4220                                         TRUE,
4221                                         x + 1, y + height - 1,
4222                                         width, 3);
4223
4224                   if (mask2 & 4)
4225                     gdk_draw_rectangle (sheet->sheet_window,
4226                                         sheet->xor_gc,
4227                                         TRUE,
4228                                         x - 1, y + 1,
4229                                         3, height);
4230
4231
4232                   if (mask2 & 8)
4233                     gdk_draw_rectangle (sheet->sheet_window,
4234                                         sheet->xor_gc,
4235                                         TRUE,
4236                                         x + width - 1, y + 1,
4237                                         3, height);
4238
4239
4240
4241                 }
4242
4243             }
4244
4245         }
4246     }
4247
4248
4249   *range = new_range;
4250   gtk_sheet_draw_corners (sheet, new_range);
4251
4252 }
4253
4254 static void
4255 gtk_sheet_draw_border (GtkSheet *sheet, GtkSheetRange new_range)
4256 {
4257   GtkWidget *widget;
4258   GdkRectangle area;
4259   gint i;
4260   gint x, y, width, height;
4261
4262   widget = GTK_WIDGET (sheet);
4263
4264   x = COLUMN_LEFT_XPIXEL (sheet, new_range.col0);
4265   y = ROW_TOP_YPIXEL (sheet, new_range.row0);
4266   width = COLUMN_LEFT_XPIXEL (sheet, new_range.coli) - x +
4267     xxx_column_width (sheet, new_range.coli);
4268
4269   height = ROW_TOP_YPIXEL (sheet, new_range.rowi) - y +
4270     yyy_row_height (sheet, new_range.rowi);
4271
4272   area.x = COLUMN_LEFT_XPIXEL (sheet, MIN_VISIBLE_COLUMN (sheet));
4273   area.y = ROW_TOP_YPIXEL (sheet, MIN_VISIBLE_ROW (sheet));
4274   area.width = sheet->sheet_window_width;
4275   area.height = sheet->sheet_window_height;
4276
4277   if (x < 0)
4278     {
4279       width = width + x;
4280       x = 0;
4281     }
4282   if (width > area.width) width = area.width + 10;
4283   if (y < 0)
4284     {
4285       height = height + y;
4286       y = 0;
4287     }
4288   if (height > area.height) height = area.height + 10;
4289
4290   gdk_gc_set_clip_rectangle (sheet->xor_gc, &area);
4291
4292   for (i = -1; i <= 1; ++i)
4293     gdk_draw_rectangle (sheet->sheet_window,
4294                         sheet->xor_gc,
4295                         FALSE,
4296                         x + i,
4297                         y + i,
4298                         width - 2 * i,
4299                         height - 2 * i);
4300
4301   gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
4302
4303
4304   gtk_sheet_draw_corners (sheet, new_range);
4305 }
4306
4307 static void
4308 gtk_sheet_draw_corners (GtkSheet *sheet, GtkSheetRange range)
4309 {
4310   gint x, y;
4311   guint width = 1;
4312
4313   if (gtk_sheet_cell_isvisible (sheet, range.row0, range.col0))
4314     {
4315       x = COLUMN_LEFT_XPIXEL (sheet, range.col0);
4316       y = ROW_TOP_YPIXEL (sheet, range.row0);
4317       gdk_draw_drawable (sheet->sheet_window,
4318                        GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
4319                        sheet->pixmap,
4320                        x - 1,
4321                        y - 1,
4322                        x - 1,
4323                        y - 1,
4324                        3,
4325                        3);
4326       gdk_draw_rectangle (sheet->sheet_window,
4327                           sheet->xor_gc,
4328                           TRUE,
4329                           x - 1, y - 1,
4330                           3, 3);
4331     }
4332
4333   if (gtk_sheet_cell_isvisible (sheet, range.row0, range.coli) ||
4334       sheet->state == GTK_SHEET_COLUMN_SELECTED)
4335     {
4336       x = COLUMN_LEFT_XPIXEL (sheet, range.coli)+
4337         xxx_column_width (sheet, range.coli);
4338       y = ROW_TOP_YPIXEL (sheet, range.row0);
4339       width = 1;
4340       if (sheet->state == GTK_SHEET_COLUMN_SELECTED)
4341         {
4342           y = ROW_TOP_YPIXEL (sheet, MIN_VISIBLE_ROW (sheet))+3;
4343           width = 3;
4344         }
4345       gdk_draw_drawable (sheet->sheet_window,
4346                        GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
4347                        sheet->pixmap,
4348                        x - width,
4349                        y - width,
4350                        x - width,
4351                        y - width,
4352                        2 * width + 1,
4353                        2 * width + 1);
4354       gdk_draw_rectangle (sheet->sheet_window,
4355                           sheet->xor_gc,
4356                           TRUE,
4357                           x - width + width / 2, y - width + width / 2,
4358                           2 + width, 2 + width);
4359     }
4360
4361   if (gtk_sheet_cell_isvisible (sheet, range.rowi, range.col0) ||
4362       sheet->state == GTK_SHEET_ROW_SELECTED)
4363     {
4364       x = COLUMN_LEFT_XPIXEL (sheet, range.col0);
4365       y = ROW_TOP_YPIXEL (sheet, range.rowi)+
4366         yyy_row_height (sheet, range.rowi);
4367       width = 1;
4368       if (sheet->state == GTK_SHEET_ROW_SELECTED)
4369         {
4370           x = COLUMN_LEFT_XPIXEL (sheet, MIN_VISIBLE_COLUMN (sheet))+3;
4371           width = 3;
4372         }
4373       gdk_draw_drawable (sheet->sheet_window,
4374                        GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
4375                        sheet->pixmap,
4376                        x - width,
4377                        y - width,
4378                        x - width,
4379                        y - width,
4380                        2 * width + 1,
4381                        2 * width + 1);
4382       gdk_draw_rectangle (sheet->sheet_window,
4383                           sheet->xor_gc,
4384                           TRUE,
4385                           x - width + width / 2, y - width + width / 2,
4386                           2 + width, 2 + width);
4387     }
4388
4389   if (gtk_sheet_cell_isvisible (sheet, range.rowi, range.coli))
4390     {
4391       x = COLUMN_LEFT_XPIXEL (sheet, range.coli)+
4392         xxx_column_width (sheet, range.coli);
4393       y = ROW_TOP_YPIXEL (sheet, range.rowi)+
4394         yyy_row_height (sheet, range.rowi);
4395       width = 1;
4396       if (sheet->state == GTK_SHEET_RANGE_SELECTED) width = 3;
4397       if (sheet->state == GTK_SHEET_NORMAL) width = 3;
4398       gdk_draw_drawable (sheet->sheet_window,
4399                        GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
4400                        sheet->pixmap,
4401                        x - width,
4402                        y - width,
4403                        x - width,
4404                        y - width,
4405                        2 * width + 1,
4406                        2 * width + 1);
4407       gdk_draw_rectangle (sheet->sheet_window,
4408                           sheet->xor_gc,
4409                           TRUE,
4410                           x - width + width / 2, y - width + width / 2,
4411                           2 + width, 2 + width);
4412
4413     }
4414
4415 }
4416
4417
4418 static void
4419 gtk_sheet_real_select_range (GtkSheet * sheet,
4420                              const GtkSheetRange * range)
4421 {
4422   gint state;
4423
4424   g_return_if_fail (sheet != NULL);
4425
4426   if (range == NULL) range = &sheet->range;
4427
4428   memcpy (&sheet->range, range, sizeof (*range));
4429
4430   if (range->row0 < 0 || range->rowi < 0) return;
4431   if (range->col0 < 0 || range->coli < 0) return;
4432
4433   state = sheet->state;
4434
4435   if (range->coli != sheet->range.coli || range->col0 != sheet->range.col0 ||
4436       range->rowi != sheet->range.rowi || range->row0 != sheet->range.row0)
4437     {
4438       gtk_sheet_new_selection (sheet, &sheet->range);
4439     }
4440   else
4441     {
4442       gtk_sheet_draw_backing_pixmap (sheet, sheet->range);
4443       gtk_sheet_range_draw_selection (sheet, sheet->range);
4444     }
4445
4446   gtk_sheet_update_primary_selection (sheet);
4447
4448   g_signal_emit (sheet, sheet_signals[SELECT_RANGE], 0, &sheet->range);
4449 }
4450
4451
4452 void
4453 gtk_sheet_get_selected_range            (GtkSheet *sheet,
4454                                          GtkSheetRange *range)
4455 {
4456   g_return_if_fail (sheet != NULL);
4457   *range = sheet->range;
4458 }
4459
4460
4461 void
4462 gtk_sheet_select_range (GtkSheet * sheet, const GtkSheetRange *range)
4463 {
4464   g_return_if_fail (sheet != NULL);
4465
4466   if (range == NULL) range=&sheet->range;
4467
4468   if (range->row0 < 0 || range->rowi < 0) return;
4469   if (range->col0 < 0 || range->coli < 0) return;
4470
4471
4472   if (sheet->state != GTK_SHEET_NORMAL)
4473     gtk_sheet_real_unselect_range (sheet, NULL);
4474   else
4475     gtk_sheet_deactivate_cell (sheet);
4476
4477   sheet->range.row0 = range->row0;
4478   sheet->range.rowi = range->rowi;
4479   sheet->range.col0 = range->col0;
4480   sheet->range.coli = range->coli;
4481   sheet->active_cell.row = range->row0;
4482   sheet->active_cell.col = range->col0;
4483   sheet->selection_cell.row = range->rowi;
4484   sheet->selection_cell.col = range->coli;
4485
4486   sheet->state = GTK_SHEET_RANGE_SELECTED;
4487   gtk_sheet_real_select_range (sheet, NULL);
4488
4489 }
4490
4491 void
4492 gtk_sheet_unselect_range (GtkSheet * sheet)
4493 {
4494   if (! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4495     return;
4496
4497   gtk_sheet_real_unselect_range (sheet, NULL);
4498   sheet->state = GTK_STATE_NORMAL;
4499
4500   gtk_sheet_activate_cell (sheet,
4501                            sheet->active_cell.row, sheet->active_cell.col);
4502 }
4503
4504
4505 static void
4506 gtk_sheet_real_unselect_range (GtkSheet * sheet,
4507                                const GtkSheetRange *range)
4508 {
4509   g_return_if_fail (sheet != NULL);
4510   g_return_if_fail (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)));
4511
4512   if ( range == NULL)
4513     range = &sheet->range;
4514
4515   if (range->row0 < 0 || range->rowi < 0) return;
4516   if (range->col0 < 0 || range->coli < 0) return;
4517
4518   g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, -1);
4519   g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, -1);
4520
4521   if (gtk_sheet_range_isvisible (sheet, *range))
4522     gtk_sheet_draw_backing_pixmap (sheet, *range);
4523
4524   sheet->range.row0 = -1;
4525   sheet->range.rowi = -1;
4526   sheet->range.col0 = -1;
4527   sheet->range.coli = -1;
4528
4529   gtk_sheet_position_children (sheet);
4530 }
4531
4532
4533 static gint
4534 gtk_sheet_expose (GtkWidget * widget,
4535                   GdkEventExpose * event)
4536 {
4537   GtkSheet *sheet;
4538   GtkSheetRange range;
4539
4540   g_return_val_if_fail (widget != NULL, FALSE);
4541   g_return_val_if_fail (GTK_IS_SHEET (widget), FALSE);
4542   g_return_val_if_fail (event != NULL, FALSE);
4543
4544
4545   sheet = GTK_SHEET (widget);
4546
4547   if (GTK_WIDGET_DRAWABLE (widget))
4548     {
4549       range.row0 = ROW_FROM_YPIXEL (sheet, event->area.y);
4550       range.col0 = COLUMN_FROM_XPIXEL (sheet, event->area.x);
4551       range.rowi = ROW_FROM_YPIXEL (sheet, event->area.y + event->area.height);
4552       range.coli = COLUMN_FROM_XPIXEL (sheet, event->area.x + event->area.width);
4553
4554       /* exposure events on the sheet */
4555       if (event->window == sheet->row_title_window &&
4556           sheet->row_titles_visible)
4557         {
4558           gint i;
4559           for (i = MIN_VISIBLE_ROW (sheet); i <= MAX_VISIBLE_ROW (sheet); i++)
4560             gtk_sheet_row_title_button_draw (sheet, i);
4561         }
4562
4563       if (event->window == sheet->column_title_window &&
4564           sheet->column_titles_visible)
4565         {
4566           gint i;
4567           for (i = MIN_VISIBLE_COLUMN (sheet); i <= MAX_VISIBLE_COLUMN (sheet); i++)
4568             gtk_sheet_column_title_button_draw (sheet, i);
4569         }
4570
4571       if (event->window == sheet->sheet_window)
4572         {
4573           gtk_sheet_draw_backing_pixmap (sheet, range);
4574
4575           if (sheet->state != GTK_SHEET_NORMAL)
4576             {
4577               if (gtk_sheet_range_isvisible (sheet, sheet->range))
4578                 gtk_sheet_draw_backing_pixmap (sheet, sheet->range);
4579               if (GTK_SHEET_IN_RESIZE (sheet) || GTK_SHEET_IN_DRAG (sheet))
4580                 gtk_sheet_draw_backing_pixmap (sheet, sheet->drag_range);
4581
4582               if (gtk_sheet_range_isvisible (sheet, sheet->range))
4583                 gtk_sheet_range_draw_selection (sheet, sheet->range);
4584               if (GTK_SHEET_IN_RESIZE (sheet) || GTK_SHEET_IN_DRAG (sheet))
4585                 draw_xor_rectangle (sheet, sheet->drag_range);
4586             }
4587
4588           if ((!GTK_SHEET_IN_XDRAG (sheet)) && (!GTK_SHEET_IN_YDRAG (sheet)))
4589             {
4590               if (sheet->state == GTK_SHEET_NORMAL)
4591                 gtk_sheet_draw_active_cell (sheet);
4592             }
4593         }
4594     }
4595
4596   if (sheet->state != GTK_SHEET_NORMAL && GTK_SHEET_IN_SELECTION (sheet))
4597     gtk_widget_grab_focus (GTK_WIDGET (sheet));
4598
4599   (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
4600
4601   return FALSE;
4602 }
4603
4604
4605 static gboolean
4606 gtk_sheet_button_press (GtkWidget * widget,
4607                         GdkEventButton * event)
4608 {
4609   GtkSheet *sheet;
4610   GdkModifierType mods;
4611   gint x, y, row, column;
4612   gboolean veto;
4613
4614   g_return_val_if_fail (widget != NULL, FALSE);
4615   g_return_val_if_fail (GTK_IS_SHEET (widget), FALSE);
4616   g_return_val_if_fail (event != NULL, FALSE);
4617
4618   sheet = GTK_SHEET (widget);
4619
4620   /* Cancel any pending tooltips */
4621   if (sheet->motion_timer)
4622     {
4623       g_source_remove (sheet->motion_timer);
4624       sheet->motion_timer = 0;
4625     }
4626
4627   gtk_widget_get_pointer (widget, &x, &y);
4628   gtk_sheet_get_pixel_info (sheet, x, y, &row, &column);
4629
4630
4631   if (event->window == sheet->column_title_window)
4632     {
4633       g_signal_emit (sheet,
4634                      sheet_signals[BUTTON_EVENT_COLUMN], 0,
4635                      column, event);
4636
4637       if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
4638         g_signal_emit (sheet,
4639                        sheet_signals[DOUBLE_CLICK_COLUMN], 0, column);
4640
4641     }
4642   else if (event->window == sheet->row_title_window)
4643     {
4644       g_signal_emit (sheet,
4645                      sheet_signals[BUTTON_EVENT_ROW], 0,
4646                      row, event);
4647
4648       if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
4649         g_signal_emit (sheet,
4650                        sheet_signals[DOUBLE_CLICK_ROW], 0, row);
4651     }
4652
4653
4654   gdk_window_get_pointer (widget->window, NULL, NULL, &mods);
4655
4656   if (! (mods & GDK_BUTTON1_MASK)) return TRUE;
4657
4658
4659   /* press on resize windows */
4660   if (event->window == sheet->column_title_window &&
4661       gtk_sheet_columns_resizable (sheet))
4662     {
4663       gtk_widget_get_pointer (widget, &sheet->x_drag, NULL);
4664       if (POSSIBLE_XDRAG (sheet, sheet->x_drag, &sheet->drag_cell.col))
4665         {
4666           guint req;
4667           if (event->type == GDK_2BUTTON_PRESS)
4668             {
4669               gtk_sheet_autoresize_column (sheet, sheet->drag_cell.col);
4670               GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_XDRAG);
4671               return TRUE;
4672             }
4673           gtk_sheet_column_size_request (sheet, sheet->drag_cell.col, &req);
4674           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_XDRAG);
4675           gdk_pointer_grab (sheet->column_title_window, FALSE,
4676                             GDK_POINTER_MOTION_HINT_MASK |
4677                             GDK_BUTTON1_MOTION_MASK |
4678                             GDK_BUTTON_RELEASE_MASK,
4679                             NULL, NULL, event->time);
4680
4681           draw_xor_vline (sheet);
4682           return TRUE;
4683         }
4684     }
4685
4686   if (event->window == sheet->row_title_window && gtk_sheet_rows_resizable (sheet))
4687     {
4688       gtk_widget_get_pointer (widget, NULL, &sheet->y_drag);
4689
4690       if (POSSIBLE_YDRAG (sheet, sheet->y_drag, &sheet->drag_cell.row))
4691         {
4692           guint req;
4693           gtk_sheet_row_size_request (sheet, sheet->drag_cell.row, &req);
4694           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_YDRAG);
4695           gdk_pointer_grab (sheet->row_title_window, FALSE,
4696                             GDK_POINTER_MOTION_HINT_MASK |
4697                             GDK_BUTTON1_MOTION_MASK |
4698                             GDK_BUTTON_RELEASE_MASK,
4699                             NULL, NULL, event->time);
4700
4701           draw_xor_hline (sheet);
4702           return TRUE;
4703         }
4704     }
4705
4706   /* the sheet itself does not handle other than single click events */
4707   if (event->type != GDK_BUTTON_PRESS) return FALSE;
4708
4709   /* selections on the sheet */
4710   if (event->window == sheet->sheet_window)
4711     {
4712       gtk_widget_get_pointer (widget, &x, &y);
4713       gtk_sheet_get_pixel_info (sheet, x, y, &row, &column);
4714       gdk_pointer_grab (sheet->sheet_window, FALSE,
4715                         GDK_POINTER_MOTION_HINT_MASK |
4716                         GDK_BUTTON1_MOTION_MASK |
4717                         GDK_BUTTON_RELEASE_MASK,
4718                         NULL, NULL, event->time);
4719       gtk_grab_add (GTK_WIDGET (sheet));
4720
4721       /* This seems to be a kludge to work around a problem where the sheet
4722          scrolls to another position.  The timeout scrolls it back to its
4723          original posn.          JMD 3 July 2007
4724       */
4725       gtk_widget_grab_focus (GTK_WIDGET (sheet));
4726
4727       if (sheet->selection_mode != GTK_SELECTION_SINGLE &&
4728           sheet->selection_mode != GTK_SELECTION_NONE &&
4729           sheet->cursor_drag->type == GDK_SIZING &&
4730           !GTK_SHEET_IN_SELECTION (sheet) && !GTK_SHEET_IN_RESIZE (sheet))
4731         {
4732           if (sheet->state == GTK_STATE_NORMAL)
4733             {
4734               row = sheet->active_cell.row;
4735               column = sheet->active_cell.col;
4736               gtk_sheet_deactivate_cell (sheet);
4737               sheet->active_cell.row = row;
4738               sheet->active_cell.col = column;
4739               sheet->drag_range = sheet->range;
4740               sheet->state = GTK_SHEET_RANGE_SELECTED;
4741               gtk_sheet_select_range (sheet, &sheet->drag_range);
4742             }
4743           sheet->x_drag = x;
4744           sheet->y_drag = y;
4745           if (row > sheet->range.rowi) row--;
4746           if (column > sheet->range.coli) column--;
4747           sheet->drag_cell.row = row;
4748           sheet->drag_cell.col = column;
4749           sheet->drag_range = sheet->range;
4750           draw_xor_rectangle (sheet, sheet->drag_range);
4751           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_RESIZE);
4752         }
4753       else if (sheet->cursor_drag->type == GDK_TOP_LEFT_ARROW &&
4754                !GTK_SHEET_IN_SELECTION (sheet)
4755                && ! GTK_SHEET_IN_DRAG (sheet)
4756                && sheet->active_cell.row >= 0
4757                && sheet->active_cell.col >= 0
4758                )
4759         {
4760           if (sheet->state == GTK_STATE_NORMAL)
4761             {
4762               row = sheet->active_cell.row;
4763               column = sheet->active_cell.col;
4764               gtk_sheet_deactivate_cell (sheet);
4765               sheet->active_cell.row = row;
4766               sheet->active_cell.col = column;
4767               sheet->drag_range = sheet->range;
4768               sheet->state = GTK_SHEET_RANGE_SELECTED;
4769               gtk_sheet_select_range (sheet, &sheet->drag_range);
4770             }
4771           sheet->x_drag = x;
4772           sheet->y_drag = y;
4773           if (row < sheet->range.row0) row++;
4774           if (row > sheet->range.rowi) row--;
4775           if (column < sheet->range.col0) column++;
4776           if (column > sheet->range.coli) column--;
4777           sheet->drag_cell.row = row;
4778           sheet->drag_cell.col = column;
4779           sheet->drag_range = sheet->range;
4780           draw_xor_rectangle (sheet, sheet->drag_range);
4781           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_DRAG);
4782         }
4783       else
4784         {
4785           gtk_sheet_click_cell (sheet, row, column, &veto);
4786           if (veto) GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
4787         }
4788     }
4789
4790   if (event->window == sheet->column_title_window)
4791     {
4792       gtk_widget_get_pointer (widget, &x, &y);
4793       column = COLUMN_FROM_XPIXEL (sheet, x);
4794
4795       if (xxx_column_is_sensitive (sheet, column))
4796         {
4797           gtk_sheet_click_cell (sheet, - 1, column, &veto);
4798           gtk_grab_add (GTK_WIDGET (sheet));
4799           gtk_widget_grab_focus (GTK_WIDGET (sheet));
4800           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
4801         }
4802     }
4803
4804   if (event->window == sheet->row_title_window)
4805     {
4806       gtk_widget_get_pointer (widget, &x, &y);
4807       row = ROW_FROM_YPIXEL (sheet, y);
4808       if (yyy_row_is_sensitive (sheet, row))
4809         {
4810           gtk_sheet_click_cell (sheet, row, - 1, &veto);
4811           gtk_grab_add (GTK_WIDGET (sheet));
4812           gtk_widget_grab_focus (GTK_WIDGET (sheet));
4813           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
4814         }
4815     }
4816
4817   return TRUE;
4818 }
4819
4820 static void
4821 gtk_sheet_click_cell (GtkSheet *sheet, gint row, gint column, gboolean *veto)
4822 {
4823   *veto = TRUE;
4824
4825   if (row >= yyy_row_count (sheet) || column >= xxx_column_count (sheet))
4826     {
4827       *veto = FALSE;
4828       return;
4829     }
4830
4831   if (column >= 0 && row >= 0)
4832     if (! xxx_column_is_visible (sheet, column) || !yyy_row_is_visible (sheet, row))
4833       {
4834         *veto = FALSE;
4835         return;
4836       }
4837
4838   _gtkextra_signal_emit (GTK_OBJECT (sheet), sheet_signals[TRAVERSE],
4839                          sheet->active_cell.row, sheet->active_cell.col,
4840                          &row, &column, veto);
4841
4842   if (!*veto)
4843     {
4844       if (sheet->state == GTK_STATE_NORMAL) return;
4845
4846       row = sheet->active_cell.row;
4847       column = sheet->active_cell.col;
4848
4849       gtk_sheet_activate_cell (sheet, row, column);
4850       return;
4851     }
4852
4853   if (row == -1 && column >= 0)
4854     {
4855       if (gtk_sheet_autoscroll (sheet))
4856         gtk_sheet_move_query (sheet, row, column);
4857       gtk_sheet_select_column (sheet, column);
4858       return;
4859     }
4860   if (column == -1 && row >= 0)
4861     {
4862       if (gtk_sheet_autoscroll (sheet))
4863         gtk_sheet_move_query (sheet, row, column);
4864       gtk_sheet_select_row (sheet, row);
4865       return;
4866     }
4867
4868   if (row == - 1 && column == - 1)
4869     {
4870       sheet->range.row0 = 0;
4871       sheet->range.col0 = 0;
4872       sheet->range.rowi = yyy_row_count (sheet) - 1;
4873       sheet->range.coli = xxx_column_count (sheet) - 1;
4874       sheet->active_cell.row = 0;
4875       sheet->active_cell.col = 0;
4876       gtk_sheet_select_range (sheet, NULL);
4877       return;
4878     }
4879
4880   if (row != -1 && column != -1)
4881     {
4882       if (sheet->state != GTK_SHEET_NORMAL)
4883         {
4884           sheet->state = GTK_SHEET_NORMAL;
4885           gtk_sheet_real_unselect_range (sheet, NULL);
4886         }
4887       else
4888         {
4889           gtk_sheet_deactivate_cell (sheet);
4890           gtk_sheet_activate_cell (sheet, row, column);
4891         }
4892
4893       if (gtk_sheet_autoscroll (sheet))
4894         gtk_sheet_move_query (sheet, row, column);
4895       sheet->active_cell.row = row;
4896       sheet->active_cell.col = column;
4897       sheet->selection_cell.row = row;
4898       sheet->selection_cell.col = column;
4899       sheet->range.row0 = row;
4900       sheet->range.col0 = column;
4901       sheet->range.rowi = row;
4902       sheet->range.coli = column;
4903       sheet->state = GTK_SHEET_NORMAL;
4904       GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
4905       gtk_sheet_draw_active_cell (sheet);
4906       return;
4907     }
4908
4909   g_assert_not_reached ();
4910   gtk_sheet_activate_cell (sheet, sheet->active_cell.row,
4911                            sheet->active_cell.col);
4912 }
4913
4914 static gint
4915 gtk_sheet_button_release (GtkWidget * widget,
4916                           GdkEventButton * event)
4917 {
4918   GtkSheet *sheet;
4919   gint x, y;
4920
4921   sheet = GTK_SHEET (widget);
4922
4923   /* release on resize windows */
4924   if (GTK_SHEET_IN_XDRAG (sheet))
4925     {
4926       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_XDRAG);
4927       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
4928       gtk_widget_get_pointer (widget, &x, NULL);
4929       gdk_pointer_ungrab (event->time);
4930       draw_xor_vline (sheet);
4931
4932       gtk_sheet_set_column_width (sheet, sheet->drag_cell.col,
4933                                   new_column_width (sheet, sheet->drag_cell.col, &x));
4934       sheet->old_hadjustment = -1.;
4935       g_signal_emit_by_name (sheet->hadjustment, "value_changed");
4936       return TRUE;
4937     }
4938
4939   if (GTK_SHEET_IN_YDRAG (sheet))
4940     {
4941       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_YDRAG);
4942       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
4943       gtk_widget_get_pointer (widget, NULL, &y);
4944       gdk_pointer_ungrab (event->time);
4945       draw_xor_hline (sheet);
4946
4947       gtk_sheet_set_row_height (sheet, sheet->drag_cell.row, new_row_height (sheet, sheet->drag_cell.row, &y));
4948       sheet->old_vadjustment = -1.;
4949       g_signal_emit_by_name (sheet->vadjustment, "value_changed");
4950       return TRUE;
4951     }
4952
4953
4954   if (GTK_SHEET_IN_DRAG (sheet))
4955     {
4956       GtkSheetRange old_range;
4957       draw_xor_rectangle (sheet, sheet->drag_range);
4958       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_DRAG);
4959       gdk_pointer_ungrab (event->time);
4960
4961       gtk_sheet_real_unselect_range (sheet, NULL);
4962
4963       sheet->active_cell.row = sheet->active_cell.row +
4964         (sheet->drag_range.row0 - sheet->range.row0);
4965       sheet->active_cell.col = sheet->active_cell.col +
4966         (sheet->drag_range.col0 - sheet->range.col0);
4967       sheet->selection_cell.row = sheet->selection_cell.row +
4968         (sheet->drag_range.row0 - sheet->range.row0);
4969       sheet->selection_cell.col = sheet->selection_cell.col +
4970         (sheet->drag_range.col0 - sheet->range.col0);
4971       old_range = sheet->range;
4972       sheet->range = sheet->drag_range;
4973       sheet->drag_range = old_range;
4974       g_signal_emit (sheet, sheet_signals[MOVE_RANGE], 0,
4975                      &sheet->drag_range, &sheet->range);
4976       gtk_sheet_select_range (sheet, &sheet->range);
4977     }
4978
4979   if (GTK_SHEET_IN_RESIZE (sheet))
4980     {
4981       GtkSheetRange old_range;
4982       draw_xor_rectangle (sheet, sheet->drag_range);
4983       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_RESIZE);
4984       gdk_pointer_ungrab (event->time);
4985
4986       gtk_sheet_real_unselect_range (sheet, NULL);
4987
4988       sheet->active_cell.row = sheet->active_cell.row +
4989         (sheet->drag_range.row0 - sheet->range.row0);
4990       sheet->active_cell.col = sheet->active_cell.col +
4991         (sheet->drag_range.col0 - sheet->range.col0);
4992       if (sheet->drag_range.row0 < sheet->range.row0)
4993         sheet->selection_cell.row = sheet->drag_range.row0;
4994       if (sheet->drag_range.rowi >= sheet->range.rowi)
4995         sheet->selection_cell.row = sheet->drag_range.rowi;
4996       if (sheet->drag_range.col0 < sheet->range.col0)
4997         sheet->selection_cell.col = sheet->drag_range.col0;
4998       if (sheet->drag_range.coli >= sheet->range.coli)
4999         sheet->selection_cell.col = sheet->drag_range.coli;
5000       old_range = sheet->range;
5001       sheet->range = sheet->drag_range;
5002       sheet->drag_range = old_range;
5003
5004       if (sheet->state == GTK_STATE_NORMAL) sheet->state = GTK_SHEET_RANGE_SELECTED;
5005       g_signal_emit (sheet, sheet_signals[RESIZE_RANGE], 0,
5006                      &sheet->drag_range, &sheet->range);
5007       gtk_sheet_select_range (sheet, &sheet->range);
5008     }
5009
5010   if (sheet->state == GTK_SHEET_NORMAL && GTK_SHEET_IN_SELECTION (sheet))
5011     {
5012       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
5013       gdk_pointer_ungrab (event->time);
5014       gtk_sheet_activate_cell (sheet, sheet->active_cell.row,
5015                                sheet->active_cell.col);
5016     }
5017
5018   if (GTK_SHEET_IN_SELECTION)
5019     gdk_pointer_ungrab (event->time);
5020   gtk_grab_remove (GTK_WIDGET (sheet));
5021
5022   GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
5023
5024   return TRUE;
5025 }
5026
5027 /* Shamelessly lifted from gtktooltips */
5028 static gboolean
5029 gtk_sheet_subtitle_paint_window (GtkWidget *tip_window)
5030 {
5031   GtkRequisition req;
5032
5033   gtk_widget_size_request (tip_window, &req);
5034   gtk_paint_flat_box (tip_window->style, tip_window->window,
5035                       GTK_STATE_NORMAL, GTK_SHADOW_OUT,
5036                       NULL, GTK_WIDGET(tip_window), "tooltip",
5037                       0, 0, req.width, req.height);
5038
5039   return FALSE;
5040 }
5041
5042 static GtkSheetHoverTitle *
5043 create_hover_window (void)
5044 {
5045   GtkSheetHoverTitle *hw = malloc (sizeof (*hw));
5046
5047   hw->window = gtk_window_new (GTK_WINDOW_POPUP);
5048
5049 #if GTK_CHECK_VERSION (2, 9, 0)
5050   gtk_window_set_type_hint (GTK_WINDOW (hw->window),
5051                             GDK_WINDOW_TYPE_HINT_TOOLTIP);
5052 #endif
5053
5054   gtk_widget_set_app_paintable (hw->window, TRUE);
5055   gtk_window_set_resizable (GTK_WINDOW (hw->window), FALSE);
5056   gtk_widget_set_name (hw->window, "gtk-tooltips");
5057   gtk_container_set_border_width (GTK_CONTAINER (hw->window), 4);
5058
5059   g_signal_connect (hw->window,
5060                     "expose_event",
5061                     G_CALLBACK (gtk_sheet_subtitle_paint_window),
5062                     NULL);
5063
5064   hw->label = gtk_label_new (NULL);
5065
5066
5067   gtk_label_set_line_wrap (GTK_LABEL (hw->label), TRUE);
5068   gtk_misc_set_alignment (GTK_MISC (hw->label), 0.5, 0.5);
5069
5070   gtk_container_add (GTK_CONTAINER (hw->window), hw->label);
5071
5072   gtk_widget_show (hw->label);
5073
5074   g_signal_connect (hw->window,
5075                     "destroy",
5076                     G_CALLBACK (gtk_widget_destroyed),
5077                     &hw->window);
5078
5079   return hw;
5080 }
5081
5082 #define HOVER_WINDOW_Y_OFFSET 2
5083
5084 static void
5085 show_subtitle (GtkSheet *sheet, gint row, gint column, const gchar *subtitle)
5086 {
5087   gint x, y;
5088   gint px, py;
5089   gint width;
5090
5091   if ( ! subtitle )
5092     return;
5093
5094   if ( ! sheet->hover_window)
5095     {
5096       sheet->hover_window = create_hover_window ();
5097       gtk_widget_add_events (GTK_WIDGET (sheet), GDK_LEAVE_NOTIFY_MASK);
5098
5099       g_signal_connect_swapped (sheet, "leave-notify-event",
5100                                 G_CALLBACK (gtk_widget_hide),
5101                                 sheet->hover_window->window);
5102     }
5103
5104   gtk_label_set_text (GTK_LABEL (sheet->hover_window->label),
5105                       subtitle);
5106
5107
5108   sheet->hover_window->row = row;
5109   sheet->hover_window->column = column;
5110
5111   gdk_window_get_origin (GTK_WIDGET (sheet)->window, &x, &y);
5112
5113   gtk_widget_get_pointer (GTK_WIDGET (sheet), &px, &py);
5114
5115   gtk_widget_show (sheet->hover_window->window);
5116
5117   width = GTK_WIDGET (sheet->hover_window->label)->allocation.width;
5118
5119   if (row == -1 )
5120     {
5121       x += px;
5122       x -= width / 2;
5123       y += sheet->column_title_area.y;
5124       y += sheet->column_title_area.height;
5125       y += HOVER_WINDOW_Y_OFFSET;
5126     }
5127
5128   if ( column == -1 )
5129     {
5130       y += py;
5131       x += sheet->row_title_area.x;
5132       x += sheet->row_title_area.width * 2 / 3.0;
5133     }
5134
5135   gtk_window_move (GTK_WINDOW (sheet->hover_window->window),
5136                    x, y);
5137 }
5138
5139 static gboolean
5140 motion_timeout_callback (gpointer data)
5141 {
5142   GtkSheet *sheet = GTK_SHEET (data);
5143   gint x, y;
5144   gint row, column;
5145   gtk_widget_get_pointer (GTK_WIDGET (sheet), &x, &y);
5146
5147   if ( gtk_sheet_get_pixel_info (sheet, x, y, &row, &column) )
5148     {
5149       if ( column == -1 && row == -1 )
5150         return FALSE;
5151
5152       if ( column == -1)
5153         {
5154           GSheetRow *row_geo = sheet->row_geometry;
5155           gchar *text;
5156
5157           text = g_sheet_row_get_subtitle (row_geo, row);
5158
5159           show_subtitle (sheet, row, column, text);
5160           g_free (text);
5161         }
5162
5163       if ( row == -1)
5164         {
5165           GSheetColumn *col_geo = sheet->column_geometry;
5166           gchar *text;
5167
5168           text = g_sheet_column_get_subtitle (col_geo, column);
5169
5170           show_subtitle (sheet, row, column, text );
5171
5172           g_free (text);
5173         }
5174     }
5175
5176   return FALSE;
5177 }
5178
5179 static gint
5180 gtk_sheet_motion (GtkWidget * widget,
5181                   GdkEventMotion * event)
5182 {
5183   GtkSheet *sheet;
5184   GdkModifierType mods;
5185   GdkCursorType new_cursor;
5186   gint x, y;
5187   gint row, column;
5188
5189   g_return_val_if_fail (widget != NULL, FALSE);
5190   g_return_val_if_fail (GTK_IS_SHEET (widget), FALSE);
5191   g_return_val_if_fail (event != NULL, FALSE);
5192
5193   sheet = GTK_SHEET (widget);
5194
5195   /* selections on the sheet */
5196   x = event->x;
5197   y = event->y;
5198
5199   if (!sheet->hover_window || ! GTK_WIDGET_VISIBLE (sheet->hover_window->window))
5200     {
5201       if ( sheet->motion_timer > 0 )
5202         g_source_remove (sheet->motion_timer);
5203       sheet->motion_timer = g_timeout_add (TIMEOUT_HOVER, motion_timeout_callback, sheet);
5204     }
5205   else
5206     {
5207       gint row, column;
5208       gint wx, wy;
5209       gtk_widget_get_pointer (widget, &wx, &wy);
5210
5211       if ( gtk_sheet_get_pixel_info (sheet, wx, wy, &row, &column) )
5212         {
5213           if ( row != sheet->hover_window->row || column != sheet->hover_window->column)
5214             {
5215               gtk_widget_hide (sheet->hover_window->window);
5216             }
5217         }
5218     }
5219
5220   if (event->window == sheet->column_title_window &&
5221       gtk_sheet_columns_resizable (sheet))
5222     {
5223       gtk_widget_get_pointer (widget, &x, &y);
5224       if (!GTK_SHEET_IN_SELECTION (sheet) &&
5225           POSSIBLE_XDRAG (sheet, x, &column))
5226         {
5227           new_cursor = GDK_SB_H_DOUBLE_ARROW;
5228           if (new_cursor != sheet->cursor_drag->type)
5229             {
5230               gdk_cursor_unref (sheet->cursor_drag);
5231               sheet->cursor_drag = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW);
5232               gdk_window_set_cursor (sheet->column_title_window,
5233                                      sheet->cursor_drag);
5234             }
5235         }
5236       else
5237         {
5238           new_cursor = GDK_TOP_LEFT_ARROW;
5239           if (!GTK_SHEET_IN_XDRAG (sheet) &&
5240               new_cursor != sheet->cursor_drag->type)
5241             {
5242               gdk_cursor_unref (sheet->cursor_drag);
5243               sheet->cursor_drag = gdk_cursor_new (GDK_TOP_LEFT_ARROW);
5244               gdk_window_set_cursor (sheet->column_title_window,
5245                                      sheet->cursor_drag);
5246             }
5247         }
5248     }
5249
5250   if (event->window == sheet->row_title_window &&
5251       gtk_sheet_rows_resizable (sheet))
5252     {
5253       gtk_widget_get_pointer (widget, &x, &y);
5254       if (!GTK_SHEET_IN_SELECTION (sheet) && POSSIBLE_YDRAG (sheet, y, &column))
5255         {
5256           new_cursor = GDK_SB_V_DOUBLE_ARROW;
5257           if (new_cursor != sheet->cursor_drag->type)
5258             {
5259               gdk_cursor_unref (sheet->cursor_drag);
5260               sheet->cursor_drag = gdk_cursor_new (GDK_SB_V_DOUBLE_ARROW);
5261               gdk_window_set_cursor (sheet->row_title_window, sheet->cursor_drag);
5262             }
5263         }
5264       else
5265         {
5266           new_cursor = GDK_TOP_LEFT_ARROW;
5267           if (!GTK_SHEET_IN_YDRAG (sheet) &&
5268               new_cursor != sheet->cursor_drag->type)
5269             {
5270               gdk_cursor_unref (sheet->cursor_drag);
5271               sheet->cursor_drag = gdk_cursor_new (GDK_TOP_LEFT_ARROW);
5272               gdk_window_set_cursor (sheet->row_title_window, sheet->cursor_drag);
5273             }
5274         }
5275     }
5276
5277   new_cursor = GDK_PLUS;
5278   if ( event->window == sheet->sheet_window &&
5279        !POSSIBLE_DRAG (sheet, x, y, &row, &column) &&
5280        !GTK_SHEET_IN_DRAG (sheet) &&
5281        !POSSIBLE_RESIZE (sheet, x, y, &row, &column) &&
5282        !GTK_SHEET_IN_RESIZE (sheet) &&
5283        new_cursor != sheet->cursor_drag->type)
5284     {
5285       gdk_cursor_unref (sheet->cursor_drag);
5286       sheet->cursor_drag = gdk_cursor_new (GDK_PLUS);
5287       gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
5288     }
5289
5290   new_cursor = GDK_TOP_LEFT_ARROW;
5291   if ( event->window == sheet->sheet_window &&
5292        ! (POSSIBLE_RESIZE (sheet, x, y, &row, &column) || GTK_SHEET_IN_RESIZE (sheet)) && (POSSIBLE_DRAG (sheet, x, y, &row, &column) || GTK_SHEET_IN_DRAG (sheet)) &&
5293
5294        new_cursor != sheet->cursor_drag->type)
5295     {
5296       gdk_cursor_unref (sheet->cursor_drag);
5297       sheet->cursor_drag = gdk_cursor_new (GDK_TOP_LEFT_ARROW);
5298       gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
5299     }
5300
5301   new_cursor = GDK_SIZING;
5302   if ( event->window == sheet->sheet_window &&
5303        sheet->selection_mode != GTK_SELECTION_NONE &&
5304        !GTK_SHEET_IN_DRAG (sheet) &&
5305        (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
5306         GTK_SHEET_IN_RESIZE (sheet)) &&
5307        new_cursor != sheet->cursor_drag->type)
5308     {
5309       gdk_cursor_unref (sheet->cursor_drag);
5310       sheet->cursor_drag = gdk_cursor_new (GDK_SIZING);
5311       gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
5312     }
5313
5314
5315   gdk_window_get_pointer (widget->window, &x, &y, &mods);
5316   if (! (mods & GDK_BUTTON1_MASK)) return FALSE;
5317
5318   if (GTK_SHEET_IN_XDRAG (sheet))
5319     {
5320       if (event->is_hint || event->window != widget->window)
5321         gtk_widget_get_pointer (widget, &x, NULL);
5322       else
5323         x = event->x;
5324
5325       new_column_width (sheet, sheet->drag_cell.col, &x);
5326       if (x != sheet->x_drag)
5327         {
5328           draw_xor_vline (sheet);
5329           sheet->x_drag = x;
5330           draw_xor_vline (sheet);
5331         }
5332       return TRUE;
5333     }
5334
5335   if (GTK_SHEET_IN_YDRAG (sheet))
5336     {
5337       if (event->is_hint || event->window != widget->window)
5338         gtk_widget_get_pointer (widget, NULL, &y);
5339       else
5340         y = event->y;
5341
5342       new_row_height (sheet, sheet->drag_cell.row, &y);
5343       if (y != sheet->y_drag)
5344         {
5345           draw_xor_hline (sheet);
5346           sheet->y_drag = y;
5347           draw_xor_hline (sheet);
5348         }
5349       return TRUE;
5350     }
5351
5352   if (GTK_SHEET_IN_DRAG (sheet))
5353     {
5354       GtkSheetRange aux;
5355       column = COLUMN_FROM_XPIXEL (sheet, x)- sheet->drag_cell.col;
5356       row = ROW_FROM_YPIXEL (sheet, y)- sheet->drag_cell.row;
5357       if (sheet->state == GTK_SHEET_COLUMN_SELECTED) row = 0;
5358       if (sheet->state == GTK_SHEET_ROW_SELECTED) column = 0;
5359       sheet->x_drag = x;
5360       sheet->y_drag = y;
5361       aux = sheet->range;
5362       if (aux.row0 + row >= 0 && aux.rowi + row < yyy_row_count (sheet) &&
5363           aux.col0 + column >= 0 && aux.coli + column < xxx_column_count (sheet))
5364         {
5365           aux = sheet->drag_range;
5366           sheet->drag_range.row0 = sheet->range.row0 + row;
5367           sheet->drag_range.col0 = sheet->range.col0 + column;
5368           sheet->drag_range.rowi = sheet->range.rowi + row;
5369           sheet->drag_range.coli = sheet->range.coli + column;
5370           if (aux.row0 != sheet->drag_range.row0 ||
5371               aux.col0 != sheet->drag_range.col0)
5372             {
5373               draw_xor_rectangle (sheet, aux);
5374               draw_xor_rectangle (sheet, sheet->drag_range);
5375             }
5376         }
5377       return TRUE;
5378     }
5379
5380   if (GTK_SHEET_IN_RESIZE (sheet))
5381     {
5382       GtkSheetRange aux;
5383       gint v_h, current_col, current_row, col_threshold, row_threshold;
5384       v_h = 1;
5385
5386       if (abs (x - COLUMN_LEFT_XPIXEL (sheet, sheet->drag_cell.col)) >
5387           abs (y - ROW_TOP_YPIXEL (sheet, sheet->drag_cell.row))) v_h = 2;
5388
5389       current_col = COLUMN_FROM_XPIXEL (sheet, x);
5390       current_row = ROW_FROM_YPIXEL (sheet, y);
5391       column = current_col - sheet->drag_cell.col;
5392       row = current_row - sheet->drag_cell.row;
5393
5394       /*use half of column width resp. row height as threshold to
5395         expand selection*/
5396       col_threshold = COLUMN_LEFT_XPIXEL (sheet, current_col) +
5397         xxx_column_width (sheet, current_col) / 2;
5398       if (column > 0)
5399         {
5400           if (x < col_threshold)
5401             column -= 1;
5402         }
5403       else if (column < 0)
5404         {
5405           if (x > col_threshold)
5406             column +=1;
5407         }
5408       row_threshold = ROW_TOP_YPIXEL (sheet, current_row) +
5409         yyy_row_height (sheet, current_row)/2;
5410       if (row > 0)
5411         {
5412           if (y < row_threshold)
5413             row -= 1;
5414         }
5415       else if (row < 0)
5416         {
5417           if (y > row_threshold)
5418             row +=1;
5419         }
5420
5421       if (sheet->state == GTK_SHEET_COLUMN_SELECTED) row = 0;
5422       if (sheet->state == GTK_SHEET_ROW_SELECTED) column = 0;
5423       sheet->x_drag = x;
5424       sheet->y_drag = y;
5425       aux = sheet->range;
5426
5427       if (v_h == 1)
5428         column = 0;
5429       else
5430         row = 0;
5431
5432       if (aux.row0 + row >= 0 && aux.rowi + row < yyy_row_count (sheet) &&
5433           aux.col0 + column >= 0 && aux.coli + column < xxx_column_count (sheet))
5434         {
5435           aux = sheet->drag_range;
5436           sheet->drag_range = sheet->range;
5437
5438           if (row < 0) sheet->drag_range.row0 = sheet->range.row0 + row;
5439           if (row > 0) sheet->drag_range.rowi = sheet->range.rowi + row;
5440           if (column < 0) sheet->drag_range.col0 = sheet->range.col0 + column;
5441           if (column > 0) sheet->drag_range.coli = sheet->range.coli + column;
5442
5443           if (aux.row0 != sheet->drag_range.row0 ||
5444               aux.rowi != sheet->drag_range.rowi ||
5445               aux.col0 != sheet->drag_range.col0 ||
5446               aux.coli != sheet->drag_range.coli)
5447             {
5448               draw_xor_rectangle (sheet, aux);
5449               draw_xor_rectangle (sheet, sheet->drag_range);
5450             }
5451         }
5452       return TRUE;
5453     }
5454
5455   gtk_sheet_get_pixel_info (sheet, x, y, &row, &column);
5456
5457   if (sheet->state == GTK_SHEET_NORMAL && row == sheet->active_cell.row &&
5458       column == sheet->active_cell.col) return TRUE;
5459
5460   if (GTK_SHEET_IN_SELECTION (sheet) && mods&GDK_BUTTON1_MASK)
5461     gtk_sheet_extend_selection (sheet, row, column);
5462
5463   return TRUE;
5464 }
5465
5466 static gboolean
5467 gtk_sheet_move_query (GtkSheet *sheet, gint row, gint column)
5468 {
5469   gint row_move, column_move;
5470   gfloat row_align, col_align;
5471   guint height, width;
5472   gint new_row = row;
5473   gint new_col = column;
5474
5475   row_move = FALSE;
5476   column_move = FALSE;
5477   row_align = -1.;
5478   col_align = -1.;
5479
5480   height = sheet->sheet_window_height;
5481   width = sheet->sheet_window_width;
5482
5483   if (row >= MAX_VISIBLE_ROW (sheet) && sheet->state != GTK_SHEET_COLUMN_SELECTED)
5484     {
5485       row_align = 1.;
5486       new_row = MIN (yyy_row_count (sheet) - 1, row + 1);
5487       row_move = TRUE;
5488       if (MAX_VISIBLE_ROW (sheet) == yyy_row_count (sheet) - 1 &&
5489           ROW_TOP_YPIXEL (sheet, yyy_row_count (sheet)- 1) +
5490           yyy_row_height (sheet, yyy_row_count (sheet)- 1) < height)
5491         {
5492           row_move = FALSE;
5493           row_align = -1.;
5494         }
5495     }
5496   if (row < MIN_VISIBLE_ROW (sheet) && sheet->state != GTK_SHEET_COLUMN_SELECTED)
5497     {
5498       row_align= 0.;
5499       row_move = TRUE;
5500     }
5501   if (column >= MAX_VISIBLE_COLUMN (sheet) && sheet->state != GTK_SHEET_ROW_SELECTED)
5502     {
5503       col_align = 1.;
5504       new_col = MIN (xxx_column_count (sheet) - 1, column + 1);
5505       column_move = TRUE;
5506       if (MAX_VISIBLE_COLUMN (sheet) == (xxx_column_count (sheet) - 1) &&
5507           COLUMN_LEFT_XPIXEL (sheet, xxx_column_count (sheet) - 1) +
5508           xxx_column_width (sheet, xxx_column_count (sheet) - 1) < width)
5509         {
5510           column_move = FALSE;
5511           col_align = -1.;
5512         }
5513     }
5514   if (column < MIN_VISIBLE_COLUMN (sheet) && sheet->state != GTK_SHEET_ROW_SELECTED)
5515     {
5516       col_align = 0.;
5517       column_move = TRUE;
5518     }
5519
5520   if (row_move || column_move)
5521     {
5522       gtk_sheet_moveto (sheet, new_row, new_col, row_align, col_align);
5523     }
5524
5525   return (row_move || column_move);
5526 }
5527
5528 static void
5529 gtk_sheet_extend_selection (GtkSheet *sheet, gint row, gint column)
5530 {
5531   GtkSheetRange range;
5532   gint state;
5533   gint r, c;
5534
5535   if (row == sheet->selection_cell.row && column == sheet->selection_cell.col)
5536     return;
5537
5538   if (sheet->selection_mode == GTK_SELECTION_SINGLE) return;
5539
5540   gtk_sheet_move_query (sheet, row, column);
5541   gtk_widget_grab_focus (GTK_WIDGET (sheet));
5542
5543   if (GTK_SHEET_IN_DRAG (sheet)) return;
5544
5545   state = sheet->state;
5546
5547   switch (sheet->state)
5548     {
5549     case GTK_SHEET_ROW_SELECTED:
5550       column = xxx_column_count (sheet) - 1;
5551       break;
5552     case GTK_SHEET_COLUMN_SELECTED:
5553       row = yyy_row_count (sheet) - 1;
5554       break;
5555     case GTK_SHEET_NORMAL:
5556       sheet->state = GTK_SHEET_RANGE_SELECTED;
5557       r = sheet->active_cell.row;
5558       c = sheet->active_cell.col;
5559       sheet->range.col0 = c;
5560       sheet->range.row0 = r;
5561       sheet->range.coli = c;
5562       sheet->range.rowi = r;
5563       gdk_draw_drawable (sheet->sheet_window,
5564                        GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
5565                        sheet->pixmap,
5566                        COLUMN_LEFT_XPIXEL (sheet, c)- 1,
5567                        ROW_TOP_YPIXEL (sheet, r)- 1,
5568                        COLUMN_LEFT_XPIXEL (sheet, c)- 1,
5569                        ROW_TOP_YPIXEL (sheet, r)- 1,
5570                        xxx_column_width (sheet, c)+4,
5571                        yyy_row_height (sheet, r)+4);
5572       gtk_sheet_range_draw_selection (sheet, sheet->range);
5573     case GTK_SHEET_RANGE_SELECTED:
5574       sheet->state = GTK_SHEET_RANGE_SELECTED;
5575     }
5576
5577   sheet->selection_cell.row = row;
5578   sheet->selection_cell.col = column;
5579
5580   range.col0 = MIN (column, sheet->active_cell.col);
5581   range.coli = MAX (column, sheet->active_cell.col);
5582   range.row0 = MIN (row, sheet->active_cell.row);
5583   range.rowi = MAX (row, sheet->active_cell.row);
5584
5585   if (range.row0 != sheet->range.row0 || range.rowi != sheet->range.rowi ||
5586       range.col0 != sheet->range.col0 || range.coli != sheet->range.coli ||
5587       state == GTK_SHEET_NORMAL)
5588     gtk_sheet_real_select_range (sheet, &range);
5589
5590 }
5591
5592 static gint
5593 gtk_sheet_entry_key_press (GtkWidget *widget,
5594                            GdkEventKey *key)
5595 {
5596   gboolean focus;
5597   g_signal_emit_by_name (widget, "key_press_event", key, &focus);
5598   return focus;
5599 }
5600
5601 static gint
5602 gtk_sheet_key_press (GtkWidget *widget,
5603                      GdkEventKey *key)
5604 {
5605   GtkSheet *sheet;
5606   gint row, col;
5607   gint state;
5608   gboolean extend_selection = FALSE;
5609   gboolean force_move = FALSE;
5610   gboolean in_selection = FALSE;
5611   gboolean veto = TRUE;
5612   gint scroll = 1;
5613
5614   sheet = GTK_SHEET (widget);
5615
5616   if (key->state & GDK_CONTROL_MASK || key->keyval == GDK_Control_L ||
5617       key->keyval == GDK_Control_R) return FALSE;
5618
5619   extend_selection = (key->state & GDK_SHIFT_MASK) || key->keyval == GDK_Shift_L
5620     || key->keyval == GDK_Shift_R;
5621
5622   state = sheet->state;
5623   in_selection = GTK_SHEET_IN_SELECTION (sheet);
5624   GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
5625
5626   switch (key->keyval)
5627     {
5628     case GDK_Return: case GDK_KP_Enter:
5629       if (sheet->state == GTK_SHEET_NORMAL &&
5630           !GTK_SHEET_IN_SELECTION (sheet))
5631         g_signal_stop_emission_by_name (gtk_sheet_get_entry (sheet),
5632                                         "key-press-event");
5633       row = sheet->active_cell.row;
5634       col = sheet->active_cell.col;
5635       if (sheet->state == GTK_SHEET_COLUMN_SELECTED)
5636         row = MIN_VISIBLE_ROW (sheet)- 1;
5637       if (sheet->state == GTK_SHEET_ROW_SELECTED)
5638         col = MIN_VISIBLE_COLUMN (sheet);
5639       if (row < yyy_row_count (sheet) - 1)
5640         {
5641           row = row + scroll;
5642           while (!yyy_row_is_visible (sheet, row) && row < yyy_row_count (sheet)- 1)
5643             row++;
5644         }
5645       gtk_sheet_click_cell (sheet, row, col, &veto);
5646       extend_selection = FALSE;
5647       break;
5648     case GDK_ISO_Left_Tab:
5649       row = sheet->active_cell.row;
5650       col = sheet->active_cell.col;
5651       if (sheet->state == GTK_SHEET_ROW_SELECTED)
5652         col = MIN_VISIBLE_COLUMN (sheet)- 1;
5653       if (sheet->state == GTK_SHEET_COLUMN_SELECTED)
5654         row = MIN_VISIBLE_ROW (sheet);
5655       if (col > 0)
5656         {
5657           col = col - scroll;
5658           while (! xxx_column_is_visible (sheet, col) && col > 0) col--;
5659           col = MAX (0, col);
5660         }
5661       gtk_sheet_click_cell (sheet, row, col, &veto);
5662       extend_selection = FALSE;
5663       break;
5664     case GDK_Tab:
5665       row = sheet->active_cell.row;
5666       col = sheet->active_cell.col;
5667       if (sheet->state == GTK_SHEET_ROW_SELECTED)
5668         col = MIN_VISIBLE_COLUMN (sheet)- 1;
5669       if (sheet->state == GTK_SHEET_COLUMN_SELECTED)
5670         row = MIN_VISIBLE_ROW (sheet);
5671       if (col < xxx_column_count (sheet) - 1)
5672         {
5673           col = col + scroll;
5674           while (! xxx_column_is_visible (sheet, col) &&
5675                  col < xxx_column_count (sheet) - 1)
5676             col++;
5677         }
5678       gtk_sheet_click_cell (sheet, row, col, &veto);
5679       extend_selection = FALSE;
5680       break;
5681     case GDK_Page_Up:
5682       scroll = MAX_VISIBLE_ROW (sheet)- MIN_VISIBLE_ROW (sheet)+1;
5683     case GDK_Up:
5684       if (extend_selection)
5685         {
5686           if (state == GTK_STATE_NORMAL)
5687             {
5688               row = sheet->active_cell.row;
5689               col = sheet->active_cell.col;
5690               gtk_sheet_click_cell (sheet, row, col, &veto);
5691               if (!veto) break;
5692             }
5693           if (sheet->selection_cell.row > 0)
5694             {
5695               row = sheet->selection_cell.row - scroll;
5696               while (!yyy_row_is_visible (sheet, row) && row > 0) row--;
5697               row = MAX (0, row);
5698               gtk_sheet_extend_selection (sheet, row, sheet->selection_cell.col);
5699             }
5700           return TRUE;
5701         }
5702       col = sheet->active_cell.col;
5703       row = sheet->active_cell.row;
5704       if (state == GTK_SHEET_COLUMN_SELECTED)
5705         row = MIN_VISIBLE_ROW (sheet);
5706       if (state == GTK_SHEET_ROW_SELECTED)
5707         col = MIN_VISIBLE_COLUMN (sheet);
5708       row = row - scroll;
5709       while (!yyy_row_is_visible (sheet, row) && row > 0) row--;
5710       row = MAX (0, row);
5711       gtk_sheet_click_cell (sheet, row, col, &veto);
5712       extend_selection = FALSE;
5713       break;
5714     case GDK_Page_Down:
5715       scroll = MAX_VISIBLE_ROW (sheet)- MIN_VISIBLE_ROW (sheet)+1;
5716     case GDK_Down:
5717       if (extend_selection)
5718         {
5719           if (state == GTK_STATE_NORMAL)
5720             {
5721               row = sheet->active_cell.row;
5722               col = sheet->active_cell.col;
5723               gtk_sheet_click_cell (sheet, row, col, &veto);
5724               if (!veto) break;
5725             }
5726           if (sheet->selection_cell.row < yyy_row_count (sheet)- 1)
5727             {
5728               row = sheet->selection_cell.row + scroll;
5729               while (!yyy_row_is_visible (sheet, row) && row < yyy_row_count (sheet)- 1) row++;
5730               row = MIN (yyy_row_count (sheet)- 1, row);
5731               gtk_sheet_extend_selection (sheet, row, sheet->selection_cell.col);
5732             }
5733           return TRUE;
5734         }
5735       col = sheet->active_cell.col;
5736       row = sheet->active_cell.row;
5737       if (sheet->active_cell.row < yyy_row_count (sheet)- 1)
5738         {
5739           if (state == GTK_SHEET_COLUMN_SELECTED)
5740             row = MIN_VISIBLE_ROW (sheet)- 1;
5741           if (state == GTK_SHEET_ROW_SELECTED)
5742             col = MIN_VISIBLE_COLUMN (sheet);
5743           row = row + scroll;
5744           while (!yyy_row_is_visible (sheet, row) && row < yyy_row_count (sheet)- 1) row++;
5745           row = MIN (yyy_row_count (sheet)- 1, row);
5746         }
5747       gtk_sheet_click_cell (sheet, row, col, &veto);
5748       extend_selection = FALSE;
5749       break;
5750     case GDK_Right:
5751       if (extend_selection)
5752         {
5753           if (state == GTK_STATE_NORMAL)
5754             {
5755               row = sheet->active_cell.row;
5756               col = sheet->active_cell.col;
5757               gtk_sheet_click_cell (sheet, row, col, &veto);
5758               if (!veto) break;
5759             }
5760           if (sheet->selection_cell.col < xxx_column_count (sheet) - 1)
5761             {
5762               col = sheet->selection_cell.col + 1;
5763               while (! xxx_column_is_visible (sheet, col) && col < xxx_column_count (sheet) - 1)
5764                 col++;
5765               gtk_sheet_extend_selection (sheet, sheet->selection_cell.row, col);
5766             }
5767           return TRUE;
5768         }
5769       col = sheet->active_cell.col;
5770       row = sheet->active_cell.row;
5771       if (sheet->active_cell.col < xxx_column_count (sheet) - 1)
5772         {
5773           col ++;
5774           if (state == GTK_SHEET_ROW_SELECTED)
5775             col = MIN_VISIBLE_COLUMN (sheet)- 1;
5776           if (state == GTK_SHEET_COLUMN_SELECTED)
5777             row = MIN_VISIBLE_ROW (sheet);
5778           while (! xxx_column_is_visible (sheet, col) && col < xxx_column_count (sheet) - 1) col++;
5779           if (strlen (gtk_entry_get_text (GTK_ENTRY (gtk_sheet_get_entry (sheet)))) == 0
5780               || force_move)
5781             {
5782               gtk_sheet_click_cell (sheet, row, col, &veto);
5783             }
5784           else
5785             return FALSE;
5786         }
5787       extend_selection = FALSE;
5788       break;
5789     case GDK_Left:
5790       if (extend_selection)
5791         {
5792           if (state == GTK_STATE_NORMAL)
5793             {
5794               row = sheet->active_cell.row;
5795               col = sheet->active_cell.col;
5796               gtk_sheet_click_cell (sheet, row, col, &veto);
5797               if (!veto) break;
5798             }
5799           if (sheet->selection_cell.col > 0)
5800             {
5801               col = sheet->selection_cell.col - 1;
5802               while (! xxx_column_is_visible (sheet, col) && col > 0) col--;
5803               gtk_sheet_extend_selection (sheet, sheet->selection_cell.row, col);
5804             }
5805           return TRUE;
5806         }
5807       col = sheet->active_cell.col - 1;
5808       row = sheet->active_cell.row;
5809       if (state == GTK_SHEET_ROW_SELECTED)
5810         col = MIN_VISIBLE_COLUMN (sheet)- 1;
5811       if (state == GTK_SHEET_COLUMN_SELECTED)
5812         row = MIN_VISIBLE_ROW (sheet);
5813       while (! xxx_column_is_visible (sheet, col) && col > 0) col--;
5814       col = MAX (0, col);
5815
5816       if (strlen (gtk_entry_get_text (GTK_ENTRY (gtk_sheet_get_entry (sheet)))) == 0
5817           || force_move)
5818         {
5819           gtk_sheet_click_cell (sheet, row, col, &veto);
5820         }
5821       else
5822         return FALSE;
5823       extend_selection = FALSE;
5824       break;
5825     case GDK_Home:
5826       row = 0;
5827       while (!yyy_row_is_visible (sheet, row) && row < yyy_row_count (sheet) - 1) row++;
5828       gtk_sheet_click_cell (sheet, row, sheet->active_cell.col, &veto);
5829       extend_selection = FALSE;
5830       break;
5831     case GDK_End:
5832       row = yyy_row_count (sheet) - 1;
5833       while (!yyy_row_is_visible (sheet, row) && row > 0) row--;
5834       gtk_sheet_click_cell (sheet, row, sheet->active_cell.col, &veto);
5835       extend_selection = FALSE;
5836       break;
5837     default:
5838       if (in_selection)
5839         {
5840           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
5841           if (extend_selection) return TRUE;
5842         }
5843       if (state == GTK_SHEET_ROW_SELECTED)
5844         sheet->active_cell.col = MIN_VISIBLE_COLUMN (sheet);
5845       if (state == GTK_SHEET_COLUMN_SELECTED)
5846         sheet->active_cell.row = MIN_VISIBLE_ROW (sheet);
5847       return FALSE;
5848     }
5849
5850   if (extend_selection) return TRUE;
5851
5852   gtk_sheet_activate_cell (sheet, sheet->active_cell.row,
5853                            sheet->active_cell.col);
5854
5855   return TRUE;
5856 }
5857
5858 static void
5859 gtk_sheet_size_request (GtkWidget * widget,
5860                         GtkRequisition * requisition)
5861 {
5862   GtkSheet *sheet;
5863   GList *children;
5864   GtkSheetChild *child;
5865   GtkRequisition child_requisition;
5866
5867   g_return_if_fail (widget != NULL);
5868   g_return_if_fail (GTK_IS_SHEET (widget));
5869   g_return_if_fail (requisition != NULL);
5870
5871   sheet = GTK_SHEET (widget);
5872
5873   requisition->width = 3*DEFAULT_COLUMN_WIDTH;
5874   requisition->height = 3*DEFAULT_ROW_HEIGHT (widget);
5875
5876   /* compute the size of the column title area */
5877   if (sheet->column_titles_visible)
5878     requisition->height += sheet->column_title_area.height;
5879
5880   /* compute the size of the row title area */
5881   if (sheet->row_titles_visible)
5882     requisition->width += sheet->row_title_area.width;
5883
5884   children = sheet->children;
5885   while (children)
5886     {
5887       child = children->data;
5888       children = children->next;
5889
5890       gtk_widget_size_request (child->widget, &child_requisition);
5891     }
5892 }
5893
5894
5895 static void
5896 gtk_sheet_size_allocate (GtkWidget * widget,
5897                          GtkAllocation * allocation)
5898 {
5899   GtkSheet *sheet;
5900   GtkAllocation sheet_allocation;
5901   gint border_width;
5902
5903   g_return_if_fail (widget != NULL);
5904   g_return_if_fail (GTK_IS_SHEET (widget));
5905   g_return_if_fail (allocation != NULL);
5906
5907   sheet = GTK_SHEET (widget);
5908   widget->allocation = *allocation;
5909   border_width = GTK_CONTAINER (widget)->border_width;
5910
5911   if (GTK_WIDGET_REALIZED (widget))
5912     gdk_window_move_resize (widget->window,
5913                             allocation->x + border_width,
5914                             allocation->y + border_width,
5915                             allocation->width - 2 * border_width,
5916                             allocation->height - 2 * border_width);
5917
5918   /* use internal allocation structure for all the math
5919    * because it's easier than always subtracting the container
5920    * border width */
5921   sheet->internal_allocation.x = 0;
5922   sheet->internal_allocation.y = 0;
5923   sheet->internal_allocation.width = allocation->width - 2 * border_width;
5924   sheet->internal_allocation.height = allocation->height - 2 * border_width;
5925
5926   sheet_allocation.x = 0;
5927   sheet_allocation.y = 0;
5928   sheet_allocation.width = allocation->width - 2 * border_width;
5929   sheet_allocation.height = allocation->height - 2 * border_width;
5930
5931   sheet->sheet_window_width = sheet_allocation.width;
5932   sheet->sheet_window_height = sheet_allocation.height;
5933
5934   if (GTK_WIDGET_REALIZED (widget))
5935     gdk_window_move_resize (sheet->sheet_window,
5936                             sheet_allocation.x,
5937                             sheet_allocation.y,
5938                             sheet_allocation.width,
5939                             sheet_allocation.height);
5940
5941   /* position the window which holds the column title buttons */
5942   sheet->column_title_area.x = 0;
5943   sheet->column_title_area.y = 0;
5944   if (sheet->row_titles_visible)
5945     sheet->column_title_area.x = sheet->row_title_area.width;
5946   sheet->column_title_area.width = sheet_allocation.width -
5947     sheet->column_title_area.x;
5948   if (GTK_WIDGET_REALIZED (widget) && sheet->column_titles_visible)
5949     gdk_window_move_resize (sheet->column_title_window,
5950                             sheet->column_title_area.x,
5951                             sheet->column_title_area.y,
5952                             sheet->column_title_area.width,
5953                             sheet->column_title_area.height);
5954
5955   sheet->sheet_window_width = sheet_allocation.width;
5956   sheet->sheet_window_height = sheet_allocation.height;
5957
5958   /* column button allocation */
5959   size_allocate_column_title_buttons (sheet);
5960
5961   /* position the window which holds the row title buttons */
5962   sheet->row_title_area.x = 0;
5963   sheet->row_title_area.y = 0;
5964   if (sheet->column_titles_visible)
5965     sheet->row_title_area.y = sheet->column_title_area.height;
5966   sheet->row_title_area.height = sheet_allocation.height -
5967     sheet->row_title_area.y;
5968
5969   if (GTK_WIDGET_REALIZED (widget) && sheet->row_titles_visible)
5970     gdk_window_move_resize (sheet->row_title_window,
5971                             sheet->row_title_area.x,
5972                             sheet->row_title_area.y,
5973                             sheet->row_title_area.width,
5974                             sheet->row_title_area.height);
5975
5976
5977   /* row button allocation */
5978   size_allocate_row_title_buttons (sheet);
5979   size_allocate_column_title_buttons (sheet);
5980
5981   /* re - scale backing pixmap */
5982   gtk_sheet_make_backing_pixmap (sheet, 0, 0);
5983   gtk_sheet_position_children (sheet);
5984
5985   /* set the scrollbars adjustments */
5986   adjust_scrollbars (sheet);
5987 }
5988
5989 static void
5990 size_allocate_column_title_buttons (GtkSheet * sheet)
5991 {
5992   gint i;
5993   gint x, width;
5994
5995   if (!sheet->column_titles_visible) return;
5996   if (!GTK_WIDGET_REALIZED (sheet))
5997     return;
5998
5999   width = sheet->sheet_window_width;
6000   x = 0;
6001
6002   if (sheet->row_titles_visible)
6003     {
6004       width -= sheet->row_title_area.width;
6005       x = sheet->row_title_area.width;
6006     }
6007
6008   if (sheet->column_title_area.width != width || sheet->column_title_area.x != x)
6009     {
6010       sheet->column_title_area.width = width;
6011       sheet->column_title_area.x = x;
6012       gdk_window_move_resize (sheet->column_title_window,
6013                               sheet->column_title_area.x,
6014                               sheet->column_title_area.y,
6015                               sheet->column_title_area.width,
6016                               sheet->column_title_area.height);
6017     }
6018
6019
6020   if (MAX_VISIBLE_COLUMN (sheet) == xxx_column_count (sheet) - 1)
6021     gdk_window_clear_area (sheet->column_title_window,
6022                            0, 0,
6023                            sheet->column_title_area.width,
6024                            sheet->column_title_area.height);
6025
6026   if (!GTK_WIDGET_DRAWABLE (sheet)) return;
6027
6028   size_allocate_global_button (sheet);
6029
6030   for (i = MIN_VISIBLE_COLUMN (sheet); i <= MAX_VISIBLE_COLUMN (sheet); i++)
6031     gtk_sheet_column_title_button_draw (sheet, i);
6032 }
6033
6034 static void
6035 size_allocate_row_title_buttons (GtkSheet * sheet)
6036 {
6037   gint i;
6038   gint y, height;
6039
6040   if (!sheet->row_titles_visible) return;
6041   if (!GTK_WIDGET_REALIZED (sheet))
6042     return;
6043
6044   height = sheet->sheet_window_height;
6045   y = 0;
6046
6047   if (sheet->column_titles_visible)
6048     {
6049       height -= sheet->column_title_area.height;
6050       y = sheet->column_title_area.height;
6051     }
6052
6053   if (sheet->row_title_area.height != height || sheet->row_title_area.y != y)
6054     {
6055       sheet->row_title_area.y = y;
6056       sheet->row_title_area.height = height;
6057       gdk_window_move_resize (sheet->row_title_window,
6058                               sheet->row_title_area.x,
6059                               sheet->row_title_area.y,
6060                               sheet->row_title_area.width,
6061                               sheet->row_title_area.height);
6062     }
6063   if (MAX_VISIBLE_ROW (sheet) == yyy_row_count (sheet)- 1)
6064     gdk_window_clear_area (sheet->row_title_window,
6065                            0, 0,
6066                            sheet->row_title_area.width,
6067                            sheet->row_title_area.height);
6068
6069   if (!GTK_WIDGET_DRAWABLE (sheet)) return;
6070
6071   size_allocate_global_button (sheet);
6072
6073   for (i = MIN_VISIBLE_ROW (sheet); i <= MAX_VISIBLE_ROW (sheet); i++)
6074     {
6075       if ( i >= yyy_row_count (sheet))
6076         break;
6077       gtk_sheet_row_title_button_draw (sheet, i);
6078     }
6079 }
6080
6081
6082 static void
6083 gtk_sheet_size_allocate_entry (GtkSheet *sheet)
6084 {
6085   GtkAllocation shentry_allocation;
6086   GtkSheetCellAttr attributes = { 0 };
6087   GtkEntry *sheet_entry;
6088   GtkStyle *style = NULL, *previous_style = NULL;
6089   gint row, col;
6090   gint size, max_size, text_size, column_width;
6091   const gchar *text;
6092
6093   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
6094   if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
6095
6096   sheet_entry = GTK_ENTRY (gtk_sheet_get_entry (sheet));
6097
6098   if ( ! gtk_sheet_get_attributes (sheet, sheet->active_cell.row,
6099                                    sheet->active_cell.col,
6100                                    &attributes) )
6101     return ;
6102
6103   if ( GTK_WIDGET_REALIZED (sheet->entry_widget) )
6104     {
6105       if (!GTK_WIDGET (sheet_entry)->style)
6106         gtk_widget_ensure_style (GTK_WIDGET (sheet_entry));
6107
6108       previous_style = GTK_WIDGET (sheet_entry)->style;
6109
6110       style = gtk_style_copy (previous_style);
6111       style->bg[GTK_STATE_NORMAL] = attributes.background;
6112       style->fg[GTK_STATE_NORMAL] = attributes.foreground;
6113       style->text[GTK_STATE_NORMAL] = attributes.foreground;
6114       style->bg[GTK_STATE_ACTIVE] = attributes.background;
6115       style->fg[GTK_STATE_ACTIVE] = attributes.foreground;
6116       style->text[GTK_STATE_ACTIVE] = attributes.foreground;
6117
6118       pango_font_description_free (style->font_desc);
6119       g_assert (attributes.font_desc);
6120       style->font_desc = pango_font_description_copy (attributes.font_desc);
6121
6122       GTK_WIDGET (sheet_entry)->style = style;
6123       gtk_widget_size_request (sheet->entry_widget, NULL);
6124       GTK_WIDGET (sheet_entry)->style = previous_style;
6125
6126       if (style != previous_style)
6127         {
6128           if (!GTK_IS_ITEM_ENTRY (sheet->entry_widget))
6129             {
6130               style->bg[GTK_STATE_NORMAL] = previous_style->bg[GTK_STATE_NORMAL];
6131               style->fg[GTK_STATE_NORMAL] = previous_style->fg[GTK_STATE_NORMAL];
6132               style->bg[GTK_STATE_ACTIVE] = previous_style->bg[GTK_STATE_ACTIVE];
6133               style->fg[GTK_STATE_ACTIVE] = previous_style->fg[GTK_STATE_ACTIVE];
6134             }
6135           gtk_widget_set_style (GTK_WIDGET (sheet_entry), style);
6136           g_object_unref (style);
6137         }
6138     }
6139
6140   if (GTK_IS_ITEM_ENTRY (sheet_entry))
6141     max_size = GTK_ITEM_ENTRY (sheet_entry)->text_max_size;
6142   else
6143     max_size = 0;
6144
6145   text_size = 0;
6146   text = gtk_entry_get_text (GTK_ENTRY (sheet_entry));
6147   if (text && strlen (text) > 0)
6148     text_size = STRING_WIDTH (GTK_WIDGET (sheet), attributes.font_desc, text);
6149
6150   column_width = xxx_column_width (sheet, sheet->active_cell.col);
6151
6152   size = MIN (text_size, max_size);
6153   size = MAX (size, column_width - 2 * CELLOFFSET);
6154
6155   row = sheet->active_cell.row;
6156   col = sheet->active_cell.col;
6157
6158   shentry_allocation.x = COLUMN_LEFT_XPIXEL (sheet, sheet->active_cell.col);
6159   shentry_allocation.y = ROW_TOP_YPIXEL (sheet, sheet->active_cell.row);
6160   shentry_allocation.width = column_width;
6161   shentry_allocation.height = yyy_row_height (sheet, sheet->active_cell.row);
6162
6163   if (GTK_IS_ITEM_ENTRY (sheet->entry_widget))
6164     {
6165       shentry_allocation.height -= 2 * CELLOFFSET;
6166       shentry_allocation.y += CELLOFFSET;
6167       shentry_allocation.width = size;
6168
6169       switch (GTK_ITEM_ENTRY (sheet_entry)->justification)
6170         {
6171         case GTK_JUSTIFY_CENTER:
6172           shentry_allocation.x += column_width / 2 - size / 2;
6173           break;
6174         case GTK_JUSTIFY_RIGHT:
6175           shentry_allocation.x += column_width - size - CELLOFFSET;
6176           break;
6177         case GTK_JUSTIFY_LEFT:
6178         case GTK_JUSTIFY_FILL:
6179           shentry_allocation.x += CELLOFFSET;
6180           break;
6181         }
6182     }
6183
6184   if (!GTK_IS_ITEM_ENTRY (sheet->entry_widget))
6185     {
6186       shentry_allocation.x += 2;
6187       shentry_allocation.y += 2;
6188       shentry_allocation.width -= MIN (shentry_allocation.width, 3);
6189       shentry_allocation.height -= MIN (shentry_allocation.height, 3);
6190     }
6191
6192   gtk_widget_size_allocate (sheet->entry_widget, &shentry_allocation);
6193
6194   if (previous_style == style) g_object_unref (previous_style);
6195 }
6196
6197 static void
6198 gtk_sheet_entry_set_max_size (GtkSheet *sheet)
6199 {
6200   gint i;
6201   gint size = 0;
6202   gint sizel = 0, sizer = 0;
6203   gint row, col;
6204   GtkJustification justification;
6205   gchar *s = NULL;
6206
6207   row = sheet->active_cell.row;
6208   col = sheet->active_cell.col;
6209
6210   if ( ! GTK_IS_ITEM_ENTRY (sheet->entry_widget) )
6211     return;
6212
6213   justification = GTK_ITEM_ENTRY (sheet->entry_widget)->justification;
6214
6215   switch (justification)
6216     {
6217     case GTK_JUSTIFY_FILL:
6218     case GTK_JUSTIFY_LEFT:
6219       for (i = col + 1; i <= MAX_VISIBLE_COLUMN (sheet); i++)
6220         {
6221           if ((s = gtk_sheet_cell_get_text (sheet, row, i)))
6222             {
6223               g_free (s);
6224               break;
6225             }
6226           size +=xxx_column_width (sheet, i);
6227         }
6228       size = MIN (size, sheet->sheet_window_width - COLUMN_LEFT_XPIXEL (sheet, col));
6229       break;
6230     case GTK_JUSTIFY_RIGHT:
6231       for (i = col - 1; i >= MIN_VISIBLE_COLUMN (sheet); i--)
6232         {
6233           if ((s = gtk_sheet_cell_get_text (sheet, row, i)))
6234             {
6235               g_free (s);
6236               break;
6237             }
6238           size +=xxx_column_width (sheet, i);
6239         }
6240       break;
6241     case GTK_JUSTIFY_CENTER:
6242       for (i = col + 1; i <= MAX_VISIBLE_COLUMN (sheet); i++)
6243         {
6244           sizer += xxx_column_width (sheet, i);
6245         }
6246       for (i = col - 1; i >= MIN_VISIBLE_COLUMN (sheet); i--)
6247         {
6248           if ((s = gtk_sheet_cell_get_text (sheet, row, i)))
6249             {
6250               g_free (s);
6251               break;
6252             }
6253           sizel +=xxx_column_width (sheet, i);
6254         }
6255       size = 2 * MIN (sizel, sizer);
6256       break;
6257     }
6258
6259   if (size != 0)
6260     size += xxx_column_width (sheet, col);
6261   GTK_ITEM_ENTRY (sheet->entry_widget)->text_max_size = size;
6262 }
6263
6264
6265 static void
6266 create_sheet_entry (GtkSheet *sheet)
6267 {
6268   if (sheet->entry_widget)
6269     {
6270       gtk_widget_unparent (sheet->entry_widget);
6271     }
6272
6273   if (sheet->entry_type)
6274     {
6275       sheet->entry_container = g_object_new (sheet->entry_type, NULL);
6276       g_object_ref_sink (sheet->entry_container);
6277       sheet->entry_widget = gtk_sheet_get_entry (sheet);
6278
6279       if  ( NULL == sheet->entry_widget)
6280         {
6281           g_warning ("Entry type is %s. It must be GtkEntry subclass, or a widget containing one. "
6282                      "Using default", g_type_name (sheet->entry_type));
6283           g_object_unref (sheet->entry_container);
6284           sheet->entry_widget = sheet->entry_container = gtk_item_entry_new ();
6285         }
6286       else
6287         {
6288           sheet->entry_widget = sheet->entry_container ;
6289         }
6290     }
6291   else
6292     {
6293       sheet->entry_widget = sheet->entry_container = gtk_item_entry_new ();
6294       g_object_ref_sink (sheet->entry_container);
6295     }
6296
6297   gtk_widget_size_request (sheet->entry_widget, NULL);
6298
6299   if (GTK_WIDGET_REALIZED (sheet))
6300     {
6301       gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
6302       gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
6303       gtk_widget_realize (sheet->entry_widget);
6304     }
6305
6306   g_signal_connect_swapped (sheet->entry_widget, "key_press_event",
6307                             G_CALLBACK (gtk_sheet_entry_key_press),
6308                             sheet);
6309
6310   gtk_widget_show (sheet->entry_widget);
6311 }
6312
6313
6314 /* Finds the last child widget that happens to be of type GtkEntry */
6315 static void
6316 find_entry (GtkWidget *w, gpointer user_data)
6317 {
6318   GtkWidget **entry = user_data;
6319   if ( GTK_IS_ENTRY (w))
6320     {
6321       *entry = w;
6322     }
6323 }
6324
6325 GtkWidget *
6326 gtk_sheet_get_entry (GtkSheet *sheet)
6327 {
6328   GtkWidget *parent;
6329   GtkWidget *entry = NULL;
6330   GtkTableChild *table_child;
6331   GtkBoxChild *box_child;
6332   GList *children = NULL;
6333
6334   g_return_val_if_fail (sheet != NULL, NULL);
6335   g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);
6336   g_return_val_if_fail (sheet->entry_widget != NULL, NULL);
6337
6338   if (GTK_IS_ENTRY (sheet->entry_container))
6339     return (sheet->entry_container);
6340
6341   parent = sheet->entry_container;
6342
6343   if (GTK_IS_TABLE (parent)) children = GTK_TABLE (parent)->children;
6344   if (GTK_IS_BOX (parent)) children = GTK_BOX (parent)->children;
6345
6346   if (GTK_IS_CONTAINER (parent))
6347     {
6348       gtk_container_forall (GTK_CONTAINER (parent), find_entry, &entry);
6349
6350       if (GTK_IS_ENTRY (entry))
6351         return entry;
6352     }
6353
6354   if (!children) return NULL;
6355
6356   while (children)
6357     {
6358       if (GTK_IS_TABLE (parent))
6359         {
6360           table_child = children->data;
6361           entry = table_child->widget;
6362         }
6363       if (GTK_IS_BOX (parent))
6364         {
6365           box_child = children->data;
6366           entry = box_child->widget;
6367         }
6368
6369       if (GTK_IS_ENTRY (entry))
6370         break;
6371       children = children->next;
6372     }
6373
6374
6375   if (!GTK_IS_ENTRY (entry)) return NULL;
6376
6377   return (entry);
6378
6379 }
6380
6381 GtkWidget *
6382 gtk_sheet_get_entry_widget (GtkSheet *sheet)
6383 {
6384   g_return_val_if_fail (sheet != NULL, NULL);
6385   g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);
6386   g_return_val_if_fail (sheet->entry_widget != NULL, NULL);
6387
6388   return (sheet->entry_widget);
6389 }
6390
6391
6392 static void
6393 gtk_sheet_button_draw (GtkSheet *sheet, GdkWindow *window,
6394                        GtkSheetButton *button, gboolean is_sensitive,
6395                        GdkRectangle allocation)
6396 {
6397   GtkShadowType shadow_type;
6398   gint text_width = 0, text_height = 0;
6399   GtkSheetChild *child = NULL;
6400   PangoAlignment align = PANGO_ALIGN_LEFT;
6401
6402   gboolean rtl ;
6403
6404   gint state = 0;
6405   gint len = 0;
6406   gchar *line = 0;
6407
6408   g_return_if_fail (sheet != NULL);
6409   g_return_if_fail (button != NULL);
6410
6411   rtl = gtk_widget_get_direction (GTK_WIDGET (sheet)) == GTK_TEXT_DIR_RTL;
6412
6413   gdk_window_clear_area (window,
6414                          allocation.x, allocation.y,
6415                          allocation.width, allocation.height);
6416
6417   gtk_paint_box (sheet->button->style, window,
6418                  GTK_STATE_NORMAL, GTK_SHADOW_OUT,
6419                  &allocation, GTK_WIDGET (sheet->button),
6420                  "buttondefault",
6421                  allocation.x, allocation.y,
6422                  allocation.width, allocation.height);
6423
6424   state = button->state;
6425   if (!is_sensitive) state = GTK_STATE_INSENSITIVE;
6426
6427   if (state == GTK_STATE_ACTIVE)
6428     shadow_type = GTK_SHADOW_IN;
6429   else
6430     shadow_type = GTK_SHADOW_OUT;
6431
6432   if (state != GTK_STATE_NORMAL && state != GTK_STATE_INSENSITIVE)
6433     gtk_paint_box (sheet->button->style, window,
6434                    button->state, shadow_type,
6435                    &allocation, GTK_WIDGET (sheet->button),
6436                    "button",
6437                    allocation.x, allocation.y,
6438                    allocation.width, allocation.height);
6439
6440   if (button->label_visible)
6441     {
6442
6443       text_height = DEFAULT_ROW_HEIGHT (GTK_WIDGET (sheet))- 2 * CELLOFFSET;
6444
6445       gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
6446                                  &allocation);
6447       gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc, &allocation);
6448
6449       allocation.y += 2 * sheet->button->style->ythickness;
6450
6451
6452       if (button->label && strlen (button->label)>0)
6453         {
6454           gchar *words = 0;
6455           PangoLayout *layout = NULL;
6456           gint real_x = allocation.x, real_y = allocation.y;
6457
6458           words = button->label;
6459           line = g_new (gchar, 1);
6460           line[0]='\0';
6461
6462           while (words && *words != '\0')
6463             {
6464               if (*words != '\n')
6465                 {
6466                   len = strlen (line);
6467                   line = g_realloc (line, len + 2);
6468                   line[len]=*words;
6469                   line[len + 1]='\0';
6470                 }
6471               if (*words == '\n' || * (words + 1) == '\0')
6472                 {
6473                   text_width = STRING_WIDTH (GTK_WIDGET (sheet), GTK_WIDGET (sheet)->style->font_desc, line);
6474
6475                   layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), line);
6476                   switch (button->justification)
6477                     {
6478                     case GTK_JUSTIFY_LEFT:
6479                       real_x = allocation.x + CELLOFFSET;
6480                       align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
6481                       break;
6482                     case GTK_JUSTIFY_RIGHT:
6483                       real_x = allocation.x + allocation.width - text_width - CELLOFFSET;
6484                       align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
6485                       break;
6486                     case GTK_JUSTIFY_CENTER:
6487                     default:
6488                       real_x = allocation.x + (allocation.width - text_width)/2;
6489                       align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
6490                       pango_layout_set_justify (layout, TRUE);
6491                     }
6492                   pango_layout_set_alignment (layout, align);
6493                   gtk_paint_layout (GTK_WIDGET (sheet)->style,
6494                                     window,
6495                                     state,
6496                                     FALSE,
6497                                     &allocation,
6498                                     GTK_WIDGET (sheet),
6499                                     "label",
6500                                     real_x, real_y,
6501                                     layout);
6502                   g_object_unref (layout);
6503
6504                   real_y += text_height + 2;
6505
6506                   g_free (line);
6507                   line = g_new (gchar, 1);
6508                   line[0]='\0';
6509                 }
6510               words++;
6511             }
6512           g_free (line);
6513         }
6514
6515       gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
6516                                  NULL);
6517       gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc, NULL);
6518
6519     }
6520
6521   if ((child = button->child) && (child->widget))
6522     {
6523       child->x = allocation.x;
6524       child->y = allocation.y;
6525
6526       child->x += (allocation.width - child->widget->requisition.width) / 2;
6527       child->y += (allocation.height - child->widget->requisition.height) / 2;
6528       allocation.x = child->x;
6529       allocation.y = child->y;
6530       allocation.width = child->widget->requisition.width;
6531       allocation.height = child->widget->requisition.height;
6532
6533       allocation.x = child->x;
6534       allocation.y = child->y;
6535
6536       gtk_widget_set_state (child->widget, button->state);
6537
6538       if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) &&
6539           GTK_WIDGET_MAPPED (child->widget))
6540         {
6541           gtk_widget_size_allocate (child->widget,
6542                                     &allocation);
6543           gtk_widget_queue_draw (child->widget);
6544         }
6545     }
6546
6547   gtk_sheet_button_free (button);
6548 }
6549
6550
6551 /* COLUMN value of - 1 indicates that the area to the right of the rightmost
6552    button should be redrawn */
6553 static void
6554 gtk_sheet_column_title_button_draw (GtkSheet *sheet, gint column)
6555 {
6556   GdkWindow *window = NULL;
6557   GdkRectangle allocation;
6558
6559   gboolean is_sensitive = FALSE;
6560
6561   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
6562
6563   if (column >= 0 && ! xxx_column_is_visible (sheet, column)) return;
6564   if (column >= 0 && !sheet->column_titles_visible) return;
6565   if (column >= 0 && column < MIN_VISIBLE_COLUMN (sheet)) return;
6566   if (column >= 0 && column > MAX_VISIBLE_COLUMN (sheet)) return;
6567
6568   window = sheet->column_title_window;
6569   allocation.y = 0;
6570   allocation.height = sheet->column_title_area.height;
6571
6572   if ( column == -1 )
6573     {
6574       const gint cols = xxx_column_count (sheet) ;
6575       allocation.x = COLUMN_LEFT_XPIXEL (sheet, cols - 1)
6576         ;
6577       allocation.width = sheet->column_title_area.width
6578         + sheet->column_title_area.x
6579         - allocation.x;
6580
6581       gdk_window_clear_area (window,
6582                              allocation.x, allocation.y,
6583                              allocation.width, allocation.height);
6584     }
6585   else
6586     {
6587       GtkSheetButton *button = xxx_column_button (sheet, column);
6588       allocation.x = COLUMN_LEFT_XPIXEL (sheet, column) + CELL_SPACING;
6589       if (sheet->row_titles_visible)
6590         allocation.x -= sheet->row_title_area.width;
6591
6592       allocation.width = xxx_column_width (sheet, column);
6593
6594       is_sensitive = xxx_column_is_sensitive (sheet, column);
6595       gtk_sheet_button_draw (sheet, window, button,
6596                              is_sensitive, allocation);
6597
6598       /* FIXME: Not freeing this button is correct (sort of),
6599          because in PSPP the model always returns a static copy */
6600
6601       /* gtk_sheet_button_free (button); */
6602
6603     }
6604 }
6605
6606 static void
6607 gtk_sheet_row_title_button_draw (GtkSheet *sheet, gint row)
6608 {
6609   GdkWindow *window = NULL;
6610   GdkRectangle allocation;
6611   GtkSheetButton *button = NULL;
6612   gboolean is_sensitive = FALSE;
6613
6614
6615   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
6616
6617   if (row >= 0 && !yyy_row_is_visible (sheet, row)) return;
6618   if (row >= 0 && !sheet->row_titles_visible) return;
6619   if (row >= 0 && row < MIN_VISIBLE_ROW (sheet)) return;
6620   if (row >= 0 && row > MAX_VISIBLE_ROW (sheet)) return;
6621
6622
6623   window = sheet->row_title_window;
6624   button = yyy_row_button (sheet, row);
6625   allocation.x = 0;
6626   allocation.y = ROW_TOP_YPIXEL (sheet, row) + CELL_SPACING;
6627   if (sheet->column_titles_visible)
6628     allocation.y -= sheet->column_title_area.height;
6629   allocation.width = sheet->row_title_area.width;
6630   allocation.height = yyy_row_height (sheet, row);
6631   is_sensitive = yyy_row_is_sensitive (sheet, row);
6632
6633   gtk_sheet_button_draw (sheet, window, button, is_sensitive, allocation);
6634 }
6635
6636 /* SCROLLBARS
6637  *
6638  * functions:
6639  * adjust_scrollbars
6640  * vadjustment_value_changed
6641  * hadjustment_value_changed */
6642
6643 static void
6644 adjust_scrollbars (GtkSheet * sheet)
6645 {
6646   if (sheet->vadjustment)
6647     {
6648       sheet->vadjustment->page_size = sheet->sheet_window_height;
6649       sheet->vadjustment->page_increment = sheet->sheet_window_height / 2;
6650       sheet->vadjustment->step_increment = DEFAULT_ROW_HEIGHT (GTK_WIDGET (sheet));
6651       sheet->vadjustment->lower = 0;
6652       sheet->vadjustment->upper = SHEET_HEIGHT (sheet) + 80;
6653       g_signal_emit_by_name (sheet->vadjustment, "changed");
6654
6655     }
6656
6657   if (sheet->hadjustment)
6658     {
6659       sheet->hadjustment->page_size = sheet->sheet_window_width;
6660       sheet->hadjustment->page_increment = sheet->sheet_window_width / 2;
6661       sheet->hadjustment->step_increment = DEFAULT_COLUMN_WIDTH;
6662       sheet->hadjustment->lower = 0;
6663       sheet->hadjustment->upper = SHEET_WIDTH (sheet)+ 80;
6664       g_signal_emit_by_name (sheet->hadjustment, "changed");
6665
6666     }
6667 }
6668
6669 static void
6670 vadjustment_value_changed (GtkAdjustment * adjustment,
6671                            gpointer data)
6672 {
6673   GtkSheet *sheet;
6674   gint diff, value, old_value;
6675   gint row, new_row;
6676   gint y = 0;
6677
6678   g_return_if_fail (adjustment != NULL);
6679   g_return_if_fail (data != NULL);
6680   g_return_if_fail (GTK_IS_SHEET (data));
6681
6682   sheet = GTK_SHEET (data);
6683
6684   if (GTK_SHEET_IS_FROZEN (sheet)) return;
6685
6686   row = ROW_FROM_YPIXEL (sheet, CELL_SPACING);
6687
6688   old_value = - sheet->voffset;
6689
6690   new_row = g_sheet_row_pixel_to_row (sheet->row_geometry,
6691                                       adjustment->value);
6692
6693   y = g_sheet_row_start_pixel (sheet->row_geometry, new_row);
6694
6695   if (adjustment->value > sheet->old_vadjustment && sheet->old_vadjustment > 0. &&
6696       yyy_row_height (sheet, row) > sheet->vadjustment->step_increment)
6697     {
6698       /* This avoids embarrassing twitching */
6699       if (row == new_row && row != yyy_row_count (sheet) - 1 &&
6700           adjustment->value - sheet->old_vadjustment >=
6701           sheet->vadjustment->step_increment &&
6702           new_row + 1 != MIN_VISIBLE_ROW (sheet))
6703         {
6704           new_row +=1;
6705           y = y+yyy_row_height (sheet, row);
6706         }
6707     }
6708
6709   /* Negative old_adjustment enforces the redraw, otherwise avoid
6710      spureous redraw */
6711   if (sheet->old_vadjustment >= 0. && row == new_row)
6712     {
6713       sheet->old_vadjustment = sheet->vadjustment->value;
6714       return;
6715     }
6716
6717   sheet->old_vadjustment = sheet->vadjustment->value;
6718   adjustment->value = y;
6719
6720
6721   if (new_row == 0)
6722     {
6723       sheet->vadjustment->step_increment = yyy_row_height (sheet, 0);
6724     }
6725   else
6726     {
6727       sheet->vadjustment->step_increment =
6728         MIN (yyy_row_height (sheet, new_row), yyy_row_height (sheet, new_row - 1));
6729     }
6730
6731   sheet->vadjustment->value = adjustment->value;
6732
6733   value = adjustment->value;
6734
6735   if (value >= - sheet->voffset)
6736     {
6737       /* scroll down */
6738       diff = value + sheet->voffset;
6739     }
6740   else
6741     {
6742       /* scroll up */
6743       diff = - sheet->voffset - value;
6744     }
6745
6746   sheet->voffset = - value;
6747
6748   if (GTK_WIDGET_REALIZED (sheet->entry_widget) &&
6749       sheet->state == GTK_SHEET_NORMAL &&
6750       sheet->active_cell.row >= 0 && sheet->active_cell.col >= 0 &&
6751       !gtk_sheet_cell_isvisible (sheet, sheet->active_cell.row,
6752                                  sheet->active_cell.col))
6753     {
6754       const gchar *text;
6755
6756       text = gtk_entry_get_text (GTK_ENTRY (gtk_sheet_get_entry (sheet)));
6757
6758       if (!text || strlen (text) == 0)
6759         gtk_sheet_cell_clear (sheet,
6760                               sheet->active_cell.row,
6761                               sheet->active_cell.col);
6762       gtk_widget_unmap (sheet->entry_widget);
6763     }
6764
6765   gtk_sheet_position_children (sheet);
6766
6767   gtk_sheet_range_draw (sheet, NULL);
6768   size_allocate_row_title_buttons (sheet);
6769   size_allocate_global_button (sheet);
6770 }
6771
6772 static void
6773 hadjustment_value_changed (GtkAdjustment * adjustment,
6774                            gpointer data)
6775 {
6776   GtkSheet *sheet;
6777   gint i, diff, value, old_value;
6778   gint column, new_column;
6779   gint x = 0;
6780
6781   g_return_if_fail (adjustment != NULL);
6782   g_return_if_fail (data != NULL);
6783   g_return_if_fail (GTK_IS_SHEET (data));
6784
6785   sheet = GTK_SHEET (data);
6786
6787   if (GTK_SHEET_IS_FROZEN (sheet)) return;
6788
6789   column = COLUMN_FROM_XPIXEL (sheet, CELL_SPACING);
6790
6791   old_value = - sheet->hoffset;
6792
6793   for (i = 0; i < xxx_column_count (sheet); i++)
6794     {
6795       if (xxx_column_is_visible (sheet, i)) x += xxx_column_width (sheet, i);
6796       if (x > adjustment->value) break;
6797     }
6798   x -= xxx_column_width (sheet, i);
6799   new_column = i;
6800
6801   if (adjustment->value > sheet->old_hadjustment && sheet->old_hadjustment > 0 &&
6802       xxx_column_width (sheet, i) > sheet->hadjustment->step_increment)
6803     {
6804       /* This avoids embarrassing twitching */
6805       if (column == new_column && column != xxx_column_count (sheet) - 1 &&
6806           adjustment->value - sheet->old_hadjustment >=
6807           sheet->hadjustment->step_increment &&
6808           new_column + 1 != MIN_VISIBLE_COLUMN (sheet))
6809         {
6810           new_column += 1;
6811           x += xxx_column_width (sheet, column);
6812         }
6813     }
6814
6815   /* Negative old_adjustment enforces the redraw, otherwise avoid spureous redraw */
6816   if (sheet->old_hadjustment >= 0. && new_column == column)
6817     {
6818       sheet->old_hadjustment = sheet->hadjustment->value;
6819       return;
6820     }
6821
6822   sheet->old_hadjustment = sheet->hadjustment->value;
6823   adjustment->value = x;
6824
6825   if (new_column == 0)
6826     {
6827       sheet->hadjustment->step_increment = xxx_column_width (sheet, 0);
6828     }
6829   else
6830     {
6831       sheet->hadjustment->step_increment =
6832         MIN (xxx_column_width (sheet, new_column), xxx_column_width (sheet, new_column - 1));
6833     }
6834
6835
6836   sheet->hadjustment->value = adjustment->value;
6837
6838   value = adjustment->value;
6839
6840   if (value >= - sheet->hoffset)
6841     {
6842       /* scroll right */
6843       diff = value + sheet->hoffset;
6844     }
6845   else
6846     {
6847       /* scroll left */
6848       diff = - sheet->hoffset - value;
6849     }
6850
6851   sheet->hoffset = - value;
6852   if (GTK_WIDGET_REALIZED (sheet->entry_widget) &&
6853       sheet->state == GTK_SHEET_NORMAL &&
6854       sheet->active_cell.row >= 0 && sheet->active_cell.col >= 0 &&
6855       !gtk_sheet_cell_isvisible (sheet, sheet->active_cell.row,
6856                                  sheet->active_cell.col))
6857     {
6858       const gchar *text;
6859
6860       text = gtk_entry_get_text (GTK_ENTRY (gtk_sheet_get_entry (sheet)));
6861       if (!text || strlen (text) == 0)
6862         gtk_sheet_cell_clear (sheet,
6863                               sheet->active_cell.row,
6864                               sheet->active_cell.col);
6865
6866       gtk_widget_unmap (sheet->entry_widget);
6867     }
6868
6869   gtk_sheet_position_children (sheet);
6870
6871   gtk_sheet_range_draw (sheet, NULL);
6872   size_allocate_column_title_buttons (sheet);
6873 }
6874
6875
6876 /* COLUMN RESIZING */
6877 static void
6878 draw_xor_vline (GtkSheet * sheet)
6879 {
6880   GtkWidget *widget;
6881
6882   g_return_if_fail (sheet != NULL);
6883
6884   widget = GTK_WIDGET (sheet);
6885
6886   gdk_draw_line (widget->window, sheet->xor_gc,
6887                  sheet->x_drag,
6888                  sheet->column_title_area.height,
6889                  sheet->x_drag,
6890                  sheet->sheet_window_height + 1);
6891 }
6892
6893 /* ROW RESIZING */
6894 static void
6895 draw_xor_hline (GtkSheet * sheet)
6896 {
6897   GtkWidget *widget;
6898
6899   g_return_if_fail (sheet != NULL);
6900
6901   widget = GTK_WIDGET (sheet);
6902
6903   gdk_draw_line (widget->window, sheet->xor_gc,
6904                  sheet->row_title_area.width,
6905                  sheet->y_drag,
6906
6907                  sheet->sheet_window_width + 1,
6908                  sheet->y_drag);
6909 }
6910
6911 /* SELECTED RANGE */
6912 static void
6913 draw_xor_rectangle (GtkSheet *sheet, GtkSheetRange range)
6914 {
6915   gint i;
6916   GdkRectangle clip_area, area;
6917   GdkGCValues values;
6918
6919   area.x = COLUMN_LEFT_XPIXEL (sheet, range.col0);
6920   area.y = ROW_TOP_YPIXEL (sheet, range.row0);
6921   area.width = COLUMN_LEFT_XPIXEL (sheet, range.coli)- area.x+
6922     xxx_column_width (sheet, range.coli);
6923   area.height = ROW_TOP_YPIXEL (sheet, range.rowi)- area.y+
6924     yyy_row_height (sheet, range.rowi);
6925
6926   clip_area.x = sheet->row_title_area.width;
6927   clip_area.y = sheet->column_title_area.height;
6928   clip_area.width = sheet->sheet_window_width;
6929   clip_area.height = sheet->sheet_window_height;
6930
6931   if (!sheet->row_titles_visible) clip_area.x = 0;
6932   if (!sheet->column_titles_visible) clip_area.y = 0;
6933
6934   if (area.x < 0)
6935     {
6936       area.width = area.width + area.x;
6937       area.x = 0;
6938     }
6939   if (area.width > clip_area.width) area.width = clip_area.width + 10;
6940   if (area.y < 0)
6941     {
6942       area.height = area.height + area.y;
6943       area.y = 0;
6944     }
6945   if (area.height > clip_area.height) area.height = clip_area.height + 10;
6946
6947   clip_area.x--;
6948   clip_area.y--;
6949   clip_area.width += 3;
6950   clip_area.height += 3;
6951
6952   gdk_gc_get_values (sheet->xor_gc, &values);
6953
6954   gdk_gc_set_clip_rectangle (sheet->xor_gc, &clip_area);
6955
6956   for (i = -1; i <= 1; ++i)
6957     gdk_draw_rectangle (sheet->sheet_window,
6958                         sheet->xor_gc,
6959                         FALSE,
6960                         area.x + i, area.y + i,
6961                         area.width - 2 * i, area.height - 2 * i);
6962
6963
6964   gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
6965
6966   gdk_gc_set_foreground (sheet->xor_gc, &values.foreground);
6967
6968 }
6969
6970
6971 /* this function returns the new width of the column being resized given
6972  * the column and x position of the cursor; the x cursor position is passed
6973  * in as a pointer and automaticaly corrected if it's beyond min / max limits */
6974 static guint
6975 new_column_width (GtkSheet * sheet,
6976                   gint column,
6977                   gint * x)
6978 {
6979   gint cx, width;
6980   guint min_width;
6981
6982   cx = *x;
6983
6984   min_width = sheet->column_requisition;
6985
6986   /* you can't shrink a column to less than its minimum width */
6987   if (cx < COLUMN_LEFT_XPIXEL (sheet, column) + min_width)
6988     {
6989       *x = cx = COLUMN_LEFT_XPIXEL (sheet, column) + min_width;
6990     }
6991
6992   /* calculate new column width making sure it doesn't end up
6993    * less than the minimum width */
6994   width = cx - COLUMN_LEFT_XPIXEL (sheet, column);
6995   if (width < min_width)
6996     width = min_width;
6997
6998   xxx_set_column_width (sheet, column, width);
6999   size_allocate_column_title_buttons (sheet);
7000
7001   return width;
7002 }
7003
7004 /* this function returns the new height of the row being resized given
7005  * the row and y position of the cursor; the y cursor position is passed
7006  * in as a pointer and automaticaly corrected if it's beyond min / max limits */
7007 static guint
7008 new_row_height (GtkSheet * sheet,
7009                 gint row,
7010                 gint * y)
7011 {
7012   gint cy, height;
7013   guint min_height;
7014
7015   cy = *y;
7016   min_height = sheet->row_requisition;
7017
7018   /* you can't shrink a row to less than its minimum height */
7019   if (cy < ROW_TOP_YPIXEL (sheet, row) + min_height)
7020
7021     {
7022       *y = cy = ROW_TOP_YPIXEL (sheet, row) + min_height;
7023     }
7024
7025   /* calculate new row height making sure it doesn't end up
7026    * less than the minimum height */
7027   height = (cy - ROW_TOP_YPIXEL (sheet, row));
7028   if (height < min_height)
7029     height = min_height;
7030
7031   yyy_set_row_height (sheet, row, height);
7032   size_allocate_row_title_buttons (sheet);
7033
7034   return height;
7035 }
7036
7037 static void
7038 gtk_sheet_set_column_width (GtkSheet * sheet,
7039                             gint column,
7040                             guint width)
7041 {
7042   guint min_width;
7043
7044   g_return_if_fail (sheet != NULL);
7045   g_return_if_fail (GTK_IS_SHEET (sheet));
7046
7047   if (column < 0 || column >= xxx_column_count (sheet))
7048     return;
7049
7050   gtk_sheet_column_size_request (sheet, column, &min_width);
7051   if (width < min_width) return;
7052
7053   xxx_set_column_width (sheet, column, width);
7054
7055   if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) && !GTK_SHEET_IS_FROZEN (sheet))
7056     {
7057       size_allocate_column_title_buttons (sheet);
7058       adjust_scrollbars (sheet);
7059       gtk_sheet_size_allocate_entry (sheet);
7060       gtk_sheet_range_draw (sheet, NULL);
7061     }
7062
7063   g_signal_emit (sheet, sheet_signals[CHANGED], 0, -1, column);
7064 }
7065
7066
7067
7068 void
7069 gtk_sheet_set_row_height (GtkSheet * sheet,
7070                           gint row,
7071                           guint height)
7072 {
7073   guint min_height;
7074
7075   g_return_if_fail (sheet != NULL);
7076   g_return_if_fail (GTK_IS_SHEET (sheet));
7077
7078   if (row < 0 || row >= yyy_row_count (sheet))
7079     return;
7080
7081   gtk_sheet_row_size_request (sheet, row, &min_height);
7082   if (height < min_height) return;
7083
7084   yyy_set_row_height (sheet, row, height);
7085
7086   if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) && !GTK_SHEET_IS_FROZEN (sheet))
7087     {
7088       size_allocate_row_title_buttons (sheet);
7089       adjust_scrollbars (sheet);
7090       gtk_sheet_size_allocate_entry (sheet);
7091       gtk_sheet_range_draw (sheet, NULL);
7092     }
7093
7094   g_signal_emit (sheet, sheet_signals[CHANGED], 0, row, - 1);
7095 }
7096 gboolean
7097 gtk_sheet_get_attributes (const GtkSheet *sheet, gint row, gint col,
7098                           GtkSheetCellAttr *attributes)
7099 {
7100   const GdkColor *fg, *bg;
7101   const GtkJustification *j ;
7102   const PangoFontDescription *font_desc ;
7103   const GtkSheetCellBorder *border ;
7104
7105   g_return_val_if_fail (sheet != NULL, FALSE);
7106   g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE);
7107
7108   if (row < 0 || col < 0) return FALSE;
7109
7110   init_attributes (sheet, col, attributes);
7111
7112   if ( !sheet->model)
7113     return FALSE;
7114
7115   attributes->is_editable = g_sheet_model_is_editable (sheet->model, row, col);
7116   attributes->is_visible = g_sheet_model_is_visible (sheet->model, row, col);
7117
7118   fg = g_sheet_model_get_foreground (sheet->model, row, col);
7119   if ( fg )
7120     attributes->foreground = *fg;
7121
7122   bg = g_sheet_model_get_background (sheet->model, row, col);
7123   if ( bg )
7124     attributes->background = *bg;
7125
7126   j = g_sheet_model_get_justification (sheet->model, row, col);
7127   if (j) attributes->justification = *j;
7128
7129   font_desc = g_sheet_model_get_font_desc (sheet->model, row, col);
7130   if ( font_desc ) attributes->font_desc = font_desc;
7131
7132   border = g_sheet_model_get_cell_border (sheet->model, row, col);
7133
7134   if ( border ) attributes->border = *border;
7135
7136   return TRUE;
7137 }
7138
7139 static void
7140 init_attributes (const GtkSheet *sheet, gint col, GtkSheetCellAttr *attributes)
7141 {
7142   /* DEFAULT VALUES */
7143   attributes->foreground = GTK_WIDGET (sheet)->style->black;
7144   attributes->background = sheet->bg_color;
7145   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
7146     {
7147       GdkColormap *colormap;
7148       colormap = gdk_colormap_get_system ();
7149       attributes->background = sheet->bg_color;
7150     }
7151   attributes->justification = xxx_column_justification (sheet, col);
7152   attributes->border.width = 0;
7153   attributes->border.line_style = GDK_LINE_SOLID;
7154   attributes->border.cap_style = GDK_CAP_NOT_LAST;
7155   attributes->border.join_style = GDK_JOIN_MITER;
7156   attributes->border.mask = 0;
7157   attributes->border.color = GTK_WIDGET (sheet)->style->black;
7158   attributes->is_editable = TRUE;
7159   attributes->is_visible = TRUE;
7160   attributes->font_desc = GTK_WIDGET (sheet)->style->font_desc;
7161 }
7162
7163
7164 /********************************************************************
7165  * Container Functions:
7166  * gtk_sheet_add
7167  * gtk_sheet_put
7168  * gtk_sheet_attach
7169  * gtk_sheet_remove
7170  * gtk_sheet_move_child
7171  * gtk_sheet_position_child
7172  * gtk_sheet_position_children
7173  * gtk_sheet_realize_child
7174  * gtk_sheet_get_child_at
7175  ********************************************************************/
7176
7177 GtkSheetChild *
7178 gtk_sheet_put (GtkSheet *sheet, GtkWidget *child, gint x, gint y)
7179 {
7180   GtkRequisition child_requisition;
7181   GtkSheetChild *child_info;
7182
7183   g_return_val_if_fail (sheet != NULL, NULL);
7184   g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);
7185   g_return_val_if_fail (child != NULL, NULL);
7186   g_return_val_if_fail (child->parent == NULL, NULL);
7187
7188   child_info = g_new (GtkSheetChild, 1);
7189   child_info->widget = child;
7190   child_info->x = x;
7191   child_info->y = y;
7192   child_info->attached_to_cell = FALSE;
7193   child_info->floating = TRUE;
7194   child_info->xpadding = child_info->ypadding = 0;
7195   child_info->xexpand = child_info->yexpand = FALSE;
7196   child_info->xshrink = child_info->yshrink = FALSE;
7197   child_info->xfill = child_info->yfill = FALSE;
7198
7199   sheet->children = g_list_append (sheet->children, child_info);
7200
7201   gtk_widget_set_parent (child, GTK_WIDGET (sheet));
7202
7203   gtk_widget_size_request (child, &child_requisition);
7204
7205   if (GTK_WIDGET_VISIBLE (GTK_WIDGET (sheet)))
7206     {
7207       if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) &&
7208           (!GTK_WIDGET_REALIZED (child) || GTK_WIDGET_NO_WINDOW (child)))
7209         gtk_sheet_realize_child (sheet, child_info);
7210
7211       if (GTK_WIDGET_MAPPED (GTK_WIDGET (sheet)) &&
7212           !GTK_WIDGET_MAPPED (child))
7213         gtk_widget_map (child);
7214     }
7215
7216   gtk_sheet_position_child (sheet, child_info);
7217
7218   /* This will avoid drawing on the titles */
7219
7220   if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
7221     {
7222       if (sheet->row_titles_visible)
7223         gdk_window_show (sheet->row_title_window);
7224       if (sheet->column_titles_visible)
7225         gdk_window_show (sheet->column_title_window);
7226     }
7227
7228   return (child_info);
7229 }
7230
7231 void
7232 gtk_sheet_attach_floating (GtkSheet *sheet,
7233                            GtkWidget *widget,
7234                            gint row, gint col)
7235 {
7236   GdkRectangle area;
7237   GtkSheetChild *child;
7238
7239   if (row < 0 || col < 0)
7240     {
7241       gtk_sheet_button_attach (sheet, widget, row, col);
7242       return;
7243     }
7244
7245   gtk_sheet_get_cell_area (sheet, row, col, &area);
7246   child = gtk_sheet_put (sheet, widget, area.x, area.y);
7247   child->attached_to_cell = TRUE;
7248   child->row = row;
7249   child->col = col;
7250 }
7251
7252 void
7253 gtk_sheet_attach_default (GtkSheet *sheet,
7254                           GtkWidget *widget,
7255                           gint row, gint col)
7256 {
7257   if (row < 0 || col < 0)
7258     {
7259       gtk_sheet_button_attach (sheet, widget, row, col);
7260       return;
7261     }
7262
7263   gtk_sheet_attach (sheet, widget, row, col,
7264                     GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
7265 }
7266
7267 void
7268 gtk_sheet_attach (GtkSheet *sheet,
7269                   GtkWidget *widget,
7270                   gint row, gint col,
7271                   gint xoptions,
7272                   gint yoptions,
7273                   gint xpadding,
7274                   gint ypadding)
7275 {
7276   GdkRectangle area;
7277   GtkSheetChild *child = NULL;
7278
7279   if (row < 0 || col < 0)
7280     {
7281       gtk_sheet_button_attach (sheet, widget, row, col);
7282       return;
7283     }
7284
7285   child = g_new0 (GtkSheetChild, 1);
7286   child->attached_to_cell = TRUE;
7287   child->floating = FALSE;
7288   child->widget = widget;
7289   child->row = row;
7290   child->col = col;
7291   child->xpadding = xpadding;
7292   child->ypadding = ypadding;
7293   child->xexpand = (xoptions & GTK_EXPAND) != 0;
7294   child->yexpand = (yoptions & GTK_EXPAND) != 0;
7295   child->xshrink = (xoptions & GTK_SHRINK) != 0;
7296   child->yshrink = (yoptions & GTK_SHRINK) != 0;
7297   child->xfill = (xoptions & GTK_FILL) != 0;
7298   child->yfill = (yoptions & GTK_FILL) != 0;
7299
7300   sheet->children = g_list_append (sheet->children, child);
7301
7302   gtk_sheet_get_cell_area (sheet, row, col, &area);
7303
7304   child->x = area.x + child->xpadding;
7305   child->y = area.y + child->ypadding;
7306
7307   if (GTK_WIDGET_VISIBLE (GTK_WIDGET (sheet)))
7308     {
7309       if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) &&
7310           (!GTK_WIDGET_REALIZED (widget) || GTK_WIDGET_NO_WINDOW (widget)))
7311         gtk_sheet_realize_child (sheet, child);
7312
7313       if (GTK_WIDGET_MAPPED (GTK_WIDGET (sheet)) &&
7314           !GTK_WIDGET_MAPPED (widget))
7315         gtk_widget_map (widget);
7316     }
7317
7318   gtk_sheet_position_child (sheet, child);
7319
7320   /* This will avoid drawing on the titles */
7321
7322   if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
7323     {
7324       if (GTK_SHEET_ROW_TITLES_VISIBLE (sheet))
7325         gdk_window_show (sheet->row_title_window);
7326       if (GTK_SHEET_COL_TITLES_VISIBLE (sheet))
7327         gdk_window_show (sheet->column_title_window);
7328     }
7329
7330 }
7331
7332 void
7333 gtk_sheet_button_attach          (GtkSheet *sheet,
7334                                   GtkWidget *widget,
7335                                   gint row, gint col)
7336 {
7337   GtkSheetButton *button = 0;
7338   GtkSheetChild *child;
7339   GtkRequisition button_requisition;
7340
7341   if (row >= 0 && col >= 0) return;
7342   if (row < 0 && col < 0) return;
7343
7344   child = g_new (GtkSheetChild, 1);
7345   child->widget = widget;
7346   child->x = 0;
7347   child->y = 0;
7348   child->attached_to_cell = TRUE;
7349   child->floating = FALSE;
7350   child->row = row;
7351   child->col = col;
7352   child->xpadding = child->ypadding = 0;
7353   child->xshrink = child->yshrink = FALSE;
7354   child->xfill = child->yfill = FALSE;
7355
7356
7357   sheet->children = g_list_append (sheet->children, child);
7358
7359   gtk_sheet_button_size_request (sheet, button, &button_requisition);
7360
7361
7362   if (GTK_WIDGET_VISIBLE (GTK_WIDGET (sheet)))
7363     {
7364       if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) &&
7365           (!GTK_WIDGET_REALIZED (widget) || GTK_WIDGET_NO_WINDOW (widget)))
7366         gtk_sheet_realize_child (sheet, child);
7367
7368       if (GTK_WIDGET_MAPPED (GTK_WIDGET (sheet)) &&
7369           !GTK_WIDGET_MAPPED (widget))
7370         gtk_widget_map (widget);
7371     }
7372
7373   if (row == -1) size_allocate_column_title_buttons (sheet);
7374   if (col == -1) size_allocate_row_title_buttons (sheet);
7375
7376 }
7377
7378 static void
7379 label_size_request (GtkSheet *sheet, gchar *label, GtkRequisition *req)
7380 {
7381   gchar *words;
7382   gchar word[1000];
7383   gint n = 0;
7384   gint row_height = DEFAULT_ROW_HEIGHT (GTK_WIDGET (sheet)) - 2 * CELLOFFSET + 2;
7385
7386   req->height = 0;
7387   req->width = 0;
7388   words = label;
7389
7390   while (words && *words != '\0')
7391     {
7392       if (*words == '\n' || * (words + 1) == '\0')
7393         {
7394           req->height += row_height;
7395
7396           word[n] = '\0';
7397           req->width = MAX (req->width, STRING_WIDTH (GTK_WIDGET (sheet), GTK_WIDGET (sheet)->style->font_desc, word));
7398           n = 0;
7399         }
7400       else
7401         {
7402           word[n++] = *words;
7403         }
7404       words++;
7405     }
7406
7407   if (n > 0) req->height -= 2;
7408 }
7409
7410 static void
7411 gtk_sheet_button_size_request    (GtkSheet *sheet,
7412                                   const GtkSheetButton *button,
7413                                   GtkRequisition *button_requisition)
7414 {
7415   GtkRequisition requisition;
7416   GtkRequisition label_requisition;
7417
7418   if (gtk_sheet_autoresize (sheet) && button->label && strlen (button->label) > 0)
7419     {
7420       label_size_request (sheet, button->label, &label_requisition);
7421       label_requisition.width += 2 * CELLOFFSET;
7422       label_requisition.height += 2 * CELLOFFSET;
7423     }
7424   else
7425     {
7426       label_requisition.height = DEFAULT_ROW_HEIGHT (GTK_WIDGET (sheet));
7427       label_requisition.width = COLUMN_MIN_WIDTH;
7428     }
7429
7430   if (button->child)
7431     {
7432       gtk_widget_size_request (button->child->widget, &requisition);
7433       requisition.width += 2 * button->child->xpadding;
7434       requisition.height += 2 * button->child->ypadding;
7435       requisition.width += 2 * sheet->button->style->xthickness;
7436       requisition.height += 2 * sheet->button->style->ythickness;
7437     }
7438   else
7439     {
7440       requisition.height = DEFAULT_ROW_HEIGHT (GTK_WIDGET (sheet));
7441       requisition.width = COLUMN_MIN_WIDTH;
7442     }
7443
7444   *button_requisition = requisition;
7445   button_requisition->width = MAX (requisition.width, label_requisition.width);
7446   button_requisition->height = MAX (requisition.height, label_requisition.height);
7447
7448 }
7449
7450 static void
7451 gtk_sheet_row_size_request (GtkSheet *sheet,
7452                             gint row,
7453                             guint *requisition)
7454 {
7455   GtkRequisition button_requisition;
7456   GList *children;
7457
7458   gtk_sheet_button_size_request (sheet,
7459                                  yyy_row_button (sheet, row),
7460                                  &button_requisition);
7461
7462   *requisition = button_requisition.height;
7463
7464   children = sheet->children;
7465   while (children)
7466     {
7467       GtkSheetChild *child = (GtkSheetChild *)children->data;
7468       GtkRequisition child_requisition;
7469
7470       if (child->attached_to_cell && child->row == row && child->col != -1 && !child->floating && !child->yshrink)
7471         {
7472           gtk_widget_get_child_requisition (child->widget, &child_requisition);
7473
7474           if (child_requisition.height + 2 * child->ypadding > *requisition)
7475             *requisition = child_requisition.height + 2 * child->ypadding;
7476         }
7477       children = children->next;
7478     }
7479
7480   sheet->row_requisition = * requisition;
7481 }
7482
7483 static void
7484 gtk_sheet_column_size_request (GtkSheet *sheet,
7485                                gint col,
7486                                guint *requisition)
7487 {
7488   GtkRequisition button_requisition;
7489   GList *children;
7490   GtkSheetButton *button = xxx_column_button (sheet, col);
7491
7492   gtk_sheet_button_size_request (sheet,
7493                                  button,
7494                                  &button_requisition);
7495
7496   gtk_sheet_button_free (button);
7497
7498   *requisition = button_requisition.width;
7499
7500   children = sheet->children;
7501   while (children)
7502     {
7503       GtkSheetChild *child = (GtkSheetChild *)children->data;
7504       GtkRequisition child_requisition;
7505
7506       if (child->attached_to_cell && child->col == col && child->row != -1 && !child->floating && !child->xshrink)
7507         {
7508           gtk_widget_get_child_requisition (child->widget, &child_requisition);
7509
7510           if (child_requisition.width + 2 * child->xpadding > *requisition)
7511             *requisition = child_requisition.width + 2 * child->xpadding;
7512         }
7513       children = children->next;
7514     }
7515
7516   sheet->column_requisition = *requisition;
7517 }
7518
7519 void
7520 gtk_sheet_move_child (GtkSheet *sheet, GtkWidget *widget, gint x, gint y)
7521 {
7522   GtkSheetChild *child;
7523   GList *children;
7524
7525   g_return_if_fail (sheet != NULL);
7526   g_return_if_fail (GTK_IS_SHEET (sheet));
7527
7528   children = sheet->children;
7529   while (children)
7530     {
7531       child = children->data;
7532
7533       if (child->widget == widget)
7534         {
7535           child->x = x;
7536           child->y = y;
7537           child->row = ROW_FROM_YPIXEL (sheet, y);
7538           child->col = COLUMN_FROM_XPIXEL (sheet, x);
7539           gtk_sheet_position_child (sheet, child);
7540           return;
7541         }
7542
7543       children = children->next;
7544     }
7545
7546   g_warning ("Widget must be a GtkSheet child");
7547
7548 }
7549
7550 static void
7551 gtk_sheet_position_child (GtkSheet *sheet, GtkSheetChild *child)
7552 {
7553   GtkRequisition child_requisition;
7554   GtkAllocation child_allocation;
7555   gint xoffset = 0;
7556   gint yoffset = 0;
7557   gint x = 0, y = 0;
7558   GdkRectangle area;
7559
7560   gtk_widget_get_child_requisition (child->widget, &child_requisition);
7561
7562   if (sheet->column_titles_visible)
7563     yoffset = sheet->column_title_area.height;
7564
7565   if (sheet->row_titles_visible)
7566     xoffset = sheet->row_title_area.width;
7567
7568   if (child->attached_to_cell)
7569     {
7570       gtk_sheet_get_cell_area (sheet, child->row, child->col, &area);
7571       child->x = area.x + child->xpadding;
7572       child->y = area.y + child->ypadding;
7573
7574       if (!child->floating)
7575         {
7576           if (child_requisition.width + 2 * child->xpadding <= xxx_column_width (sheet, child->col))
7577             {
7578               if (child->xfill)
7579                 {
7580                   child_requisition.width = child_allocation.width = xxx_column_width (sheet, child->col) - 2 * child->xpadding;
7581                 }
7582               else
7583                 {
7584                   if (child->xexpand)
7585                     {
7586                       child->x = area.x + xxx_column_width (sheet, child->col) / 2 -
7587                         child_requisition.width / 2;
7588                     }
7589                   child_allocation.width = child_requisition.width;
7590                 }
7591             }
7592           else
7593             {
7594               if (!child->xshrink)
7595                 {
7596                   gtk_sheet_set_column_width (sheet, child->col, child_requisition.width + 2 * child->xpadding);
7597                 }
7598               child_allocation.width = xxx_column_width (sheet, child->col) - 2 * child->xpadding;
7599             }
7600
7601           if (child_requisition.height +
7602               2 * child->ypadding <= yyy_row_height (sheet, child->row))
7603             {
7604               if (child->yfill)
7605                 {
7606                   child_requisition.height = child_allocation.height =
7607                     yyy_row_height (sheet, child->row) - 2 * child->ypadding;
7608                 }
7609               else
7610                 {
7611                   if (child->yexpand)
7612                     {
7613                       child->y = area.y + yyy_row_height (sheet, child->row) / 2
7614                         - child_requisition.height / 2;
7615                     }
7616                   child_allocation.height = child_requisition.height;
7617                 }
7618             }
7619           else
7620             {
7621               if (!child->yshrink)
7622                 {
7623                   gtk_sheet_set_row_height (sheet, child->row, child_requisition.height + 2 * child->ypadding);
7624                 }
7625               child_allocation.height = yyy_row_height (sheet, child->row) -
7626                 2 * child->ypadding;
7627             }
7628         }
7629       else
7630         {
7631           child_allocation.width = child_requisition.width;
7632           child_allocation.height = child_requisition.height;
7633         }
7634
7635       x = child_allocation.x = child->x + xoffset;
7636       y = child_allocation.y = child->y + yoffset;
7637     }
7638   else
7639     {
7640       x = child_allocation.x = child->x + sheet->hoffset + xoffset;
7641       x = child_allocation.x = child->x + xoffset;
7642       y = child_allocation.y = child->y + sheet->voffset + yoffset;
7643       y = child_allocation.y = child->y + yoffset;
7644       child_allocation.width = child_requisition.width;
7645       child_allocation.height = child_requisition.height;
7646     }
7647
7648   gtk_widget_size_allocate (child->widget, &child_allocation);
7649   gtk_widget_queue_draw (child->widget);
7650 }
7651
7652 static void
7653 gtk_sheet_forall (GtkContainer *container,
7654                   gboolean include_internals,
7655                   GtkCallback callback,
7656                   gpointer callback_data)
7657 {
7658   GtkSheet *sheet;
7659   GtkSheetChild *child;
7660   GList *children;
7661
7662   g_return_if_fail (GTK_IS_SHEET (container));
7663   g_return_if_fail (callback != NULL);
7664
7665   sheet = GTK_SHEET (container);
7666   children = sheet->children;
7667   while (children)
7668     {
7669       child = children->data;
7670       children = children->next;
7671
7672       (* callback) (child->widget, callback_data);
7673     }
7674
7675   if (sheet->button && sheet->button->parent)
7676     (* callback) (sheet->button, callback_data);
7677
7678   if (sheet->entry_container && GTK_IS_CONTAINER (sheet->entry_container))
7679     (* callback) (sheet->entry_container, callback_data);
7680 }
7681
7682
7683 static void
7684 gtk_sheet_position_children (GtkSheet *sheet)
7685 {
7686   GList *children;
7687   GtkSheetChild *child;
7688
7689   children = sheet->children;
7690
7691   while (children)
7692     {
7693       child = (GtkSheetChild *)children->data;
7694
7695       if (child->col != -1 && child->row != -1)
7696         gtk_sheet_position_child (sheet, child);
7697
7698       if (child->row == -1)
7699         {
7700           if (child->col < MIN_VISIBLE_COLUMN (sheet) ||
7701               child->col > MAX_VISIBLE_COLUMN (sheet))
7702             gtk_sheet_child_hide (child);
7703           else
7704             gtk_sheet_child_show (child);
7705         }
7706       if (child->col == -1)
7707         {
7708           if (child->row < MIN_VISIBLE_ROW (sheet) ||
7709               child->row > MAX_VISIBLE_ROW (sheet))
7710             gtk_sheet_child_hide (child);
7711           else
7712             gtk_sheet_child_show (child);
7713         }
7714
7715       children = children->next;
7716     }
7717 }
7718
7719 static void
7720 gtk_sheet_remove (GtkContainer *container, GtkWidget *widget)
7721 {
7722   GtkSheet *sheet;
7723   GList *children;
7724   GtkSheetChild *child = 0;
7725
7726   g_return_if_fail (container != NULL);
7727   g_return_if_fail (GTK_IS_SHEET (container));
7728
7729   sheet = GTK_SHEET (container);
7730
7731   children = sheet->children;
7732
7733   while (children)
7734     {
7735       child = (GtkSheetChild *)children->data;
7736
7737       if (child->widget == widget) break;
7738
7739       children = children->next;
7740     }
7741
7742   if (children)
7743     {
7744       gtk_widget_unparent (widget);
7745       child->widget = NULL;
7746
7747       sheet->children = g_list_remove_link (sheet->children, children);
7748       g_list_free_1 (children);
7749       g_free (child);
7750     }
7751
7752   gtk_widget_unparent (sheet->button);
7753 }
7754
7755 static void
7756 gtk_sheet_realize_child (GtkSheet *sheet, GtkSheetChild *child)
7757 {
7758   GtkWidget *widget;
7759
7760   widget = GTK_WIDGET (sheet);
7761
7762   if (GTK_WIDGET_REALIZED (widget))
7763     {
7764       if (child->row == -1)
7765         gtk_widget_set_parent_window (child->widget, sheet->column_title_window);
7766       else if (child->col == -1)
7767         gtk_widget_set_parent_window (child->widget, sheet->row_title_window);
7768       else
7769         gtk_widget_set_parent_window (child->widget, sheet->sheet_window);
7770     }
7771
7772   gtk_widget_set_parent (child->widget, widget);
7773 }
7774
7775
7776
7777 GtkSheetChild *
7778 gtk_sheet_get_child_at (GtkSheet *sheet, gint row, gint col)
7779 {
7780   GList *children;
7781   GtkSheetChild *child = 0;
7782
7783   g_return_val_if_fail (sheet != NULL, NULL);
7784   g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);
7785
7786   children = sheet->children;
7787
7788   while (children)
7789     {
7790       child = (GtkSheetChild *)children->data;
7791
7792       if (child->attached_to_cell)
7793         if (child->row == row && child->col == col) break;
7794
7795       children = children->next;
7796     }
7797
7798   if (children) return child;
7799
7800   return NULL;
7801 }
7802
7803 static void
7804 gtk_sheet_child_hide (GtkSheetChild *child)
7805 {
7806   g_return_if_fail (child != NULL);
7807   gtk_widget_hide (child->widget);
7808 }
7809
7810 static void
7811 gtk_sheet_child_show (GtkSheetChild *child)
7812 {
7813   g_return_if_fail (child != NULL);
7814
7815   gtk_widget_show (child->widget);
7816 }
7817
7818 GSheetModel *
7819 gtk_sheet_get_model (const GtkSheet *sheet)
7820 {
7821   g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);
7822
7823   return sheet->model;
7824 }
7825
7826
7827 GtkSheetButton *
7828 gtk_sheet_button_new (void)
7829 {
7830   GtkSheetButton *button = g_malloc (sizeof (GtkSheetButton));
7831
7832   button->state = GTK_STATE_NORMAL;
7833   button->label = NULL;
7834   button->label_visible = TRUE;
7835   button->child = NULL;
7836   button->justification = GTK_JUSTIFY_FILL;
7837
7838   return button;
7839 }
7840
7841
7842 void
7843 gtk_sheet_button_free (GtkSheetButton *button)
7844 {
7845   if (!button) return ;
7846
7847   g_free (button->label);
7848   g_free (button);
7849 }
7850
7851
7852 static GString *
7853 range_to_text (const GtkSheet *sheet)
7854 {
7855   gchar *celltext = NULL;
7856   gint r, c;
7857   GString *string;
7858
7859   if ( !gtk_sheet_range_isvisible (sheet, sheet->range))
7860     return NULL;
7861
7862   string = g_string_sized_new (80);
7863
7864   for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
7865     {
7866       for (c = sheet->range.col0; c < sheet->range.coli; ++c)
7867         {
7868           celltext = gtk_sheet_cell_get_text (sheet, r, c);
7869           g_string_append (string, celltext);
7870           g_string_append (string, "\t");
7871           g_free (celltext);
7872         }
7873       celltext = gtk_sheet_cell_get_text (sheet, r, c);
7874       g_string_append (string, celltext);
7875       if ( r < sheet->range.rowi)
7876         g_string_append (string, "\n");
7877       g_free (celltext);
7878     }
7879
7880   return string;
7881 }
7882
7883 static GString *
7884 range_to_html (const GtkSheet *sheet)
7885 {
7886   gchar *celltext = NULL;
7887   gint r, c;
7888   GString *string;
7889
7890   if ( !gtk_sheet_range_isvisible (sheet, sheet->range))
7891     return NULL;
7892
7893   string = g_string_sized_new (480);
7894
7895   g_string_append (string, "<html>\n");
7896   g_string_append (string, "<body>\n");
7897   g_string_append (string, "<table>\n");
7898   for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
7899     {
7900       g_string_append (string, "<tr>\n");
7901       for (c = sheet->range.col0; c <= sheet->range.coli; ++c)
7902         {
7903           g_string_append (string, "<td>");
7904           celltext = gtk_sheet_cell_get_text (sheet, r, c);
7905           g_string_append (string, celltext);
7906           g_string_append (string, "</td>\n");
7907           g_free (celltext);
7908         }
7909       g_string_append (string, "</tr>\n");
7910     }
7911   g_string_append (string, "</table>\n");
7912   g_string_append (string, "</body>\n");
7913   g_string_append (string, "</html>\n");
7914
7915   return string;
7916 }
7917
7918 enum {
7919   SELECT_FMT_NULL,
7920   SELECT_FMT_TEXT,
7921   SELECT_FMT_HTML
7922 };
7923
7924 static void
7925 primary_get_cb (GtkClipboard     *clipboard,
7926                 GtkSelectionData *selection_data,
7927                 guint             info,
7928                 gpointer          data)
7929 {
7930   GtkSheet *sheet = GTK_SHEET (data);
7931   GString *string = NULL;
7932
7933   switch (info)
7934     {
7935     case SELECT_FMT_TEXT:
7936       string = range_to_text (sheet);
7937       break;
7938     case SELECT_FMT_HTML:
7939       string = range_to_html (sheet);
7940       break;
7941     default:
7942       g_assert_not_reached ();
7943     }
7944
7945   gtk_selection_data_set (selection_data, selection_data->target,
7946                           8,
7947                           (const guchar *) string->str, string->len);
7948   g_string_free (string, TRUE);
7949 }
7950
7951 static void
7952 primary_clear_cb (GtkClipboard *clipboard,
7953                   gpointer      data)
7954 {
7955   GtkSheet *sheet = GTK_SHEET (data);
7956   if ( ! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
7957     return;
7958
7959   gtk_sheet_real_unselect_range (sheet, NULL);
7960 }
7961
7962 static void
7963 gtk_sheet_update_primary_selection (GtkSheet *sheet)
7964 {
7965   static const GtkTargetEntry targets[] = {
7966     { "UTF8_STRING",   0, SELECT_FMT_TEXT },
7967     { "STRING",        0, SELECT_FMT_TEXT },
7968     { "TEXT",          0, SELECT_FMT_TEXT },
7969     { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
7970     { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
7971     { "text/plain",    0, SELECT_FMT_TEXT },
7972     { "text/html",     0, SELECT_FMT_HTML }
7973   };
7974
7975   GtkClipboard *clipboard;
7976
7977   if (!GTK_WIDGET_REALIZED (sheet))
7978     return;
7979
7980   clipboard = gtk_widget_get_clipboard (GTK_WIDGET (sheet),
7981                                         GDK_SELECTION_PRIMARY);
7982
7983   if (gtk_sheet_range_isvisible (sheet, sheet->range))
7984     {
7985       if (!gtk_clipboard_set_with_owner (clipboard, targets,
7986                                          G_N_ELEMENTS (targets),
7987                                          primary_get_cb, primary_clear_cb,
7988                                          G_OBJECT (sheet)))
7989         primary_clear_cb (clipboard, sheet);
7990     }
7991   else
7992     {
7993       if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (sheet))
7994         gtk_clipboard_clear (clipboard);
7995     }
7996 }
7997