1f0e83fac72d6651c3e3ef79b19c240c96e00e0a
[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   sheet->entry_container = NULL;
2444
2445   g_object_unref (sheet->button);
2446   sheet->button = NULL;
2447
2448   /* unref adjustments */
2449   if (sheet->hadjustment)
2450     {
2451       g_signal_handlers_disconnect_matched (sheet->hadjustment,
2452                                             G_SIGNAL_MATCH_DATA,
2453                                             0, 0, 0, 0,
2454                                             sheet);
2455
2456       g_object_unref (sheet->hadjustment);
2457       sheet->hadjustment = NULL;
2458     }
2459
2460   if (sheet->vadjustment)
2461     {
2462       g_signal_handlers_disconnect_matched (sheet->vadjustment,
2463                                             G_SIGNAL_MATCH_DATA,
2464                                             0, 0, 0, 0,
2465                                             sheet);
2466
2467       g_object_unref (sheet->vadjustment);
2468
2469       sheet->vadjustment = NULL;
2470     }
2471
2472   children = sheet->children;
2473   while (children)
2474     {
2475       GtkSheetChild *child = (GtkSheetChild *)children->data;
2476       if (child && child->widget)
2477         gtk_sheet_remove (GTK_CONTAINER (sheet), child->widget);
2478       children = sheet->children;
2479     }
2480   sheet->children = NULL;
2481
2482   if (G_OBJECT_CLASS (parent_class)->dispose)
2483     (*G_OBJECT_CLASS (parent_class)->dispose) (object);
2484 }
2485
2486 static void
2487 gtk_sheet_style_set (GtkWidget *widget,
2488                      GtkStyle *previous_style)
2489 {
2490   GtkSheet *sheet;
2491
2492   g_return_if_fail (widget != NULL);
2493   g_return_if_fail (GTK_IS_SHEET (widget));
2494
2495   if (GTK_WIDGET_CLASS (parent_class)->style_set)
2496     (*GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous_style);
2497
2498   sheet = GTK_SHEET (widget);
2499
2500   if (GTK_WIDGET_REALIZED (widget))
2501     {
2502       gtk_style_set_background (widget->style, widget->window, widget->state);
2503     }
2504
2505 }
2506
2507 static void
2508 gtk_sheet_realize (GtkWidget * widget)
2509 {
2510   GtkSheet *sheet;
2511   GdkWindowAttr attributes;
2512   gint attributes_mask;
2513   GdkGCValues values, auxvalues;
2514   GdkColormap *colormap;
2515   GtkSheetChild *child;
2516   GList *children;
2517
2518   g_return_if_fail (widget != NULL);
2519   g_return_if_fail (GTK_IS_SHEET (widget));
2520
2521   sheet = GTK_SHEET (widget);
2522
2523   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
2524
2525   attributes.window_type = GDK_WINDOW_CHILD;
2526   attributes.x = widget->allocation.x;
2527   attributes.y = widget->allocation.y;
2528   attributes.width = widget->allocation.width;
2529   attributes.height = widget->allocation.height;
2530   attributes.wclass = GDK_INPUT_OUTPUT;
2531
2532   attributes.visual = gtk_widget_get_visual (widget);
2533   attributes.colormap = gtk_widget_get_colormap (widget);
2534
2535   attributes.event_mask = gtk_widget_get_events (widget);
2536   attributes.event_mask |= (GDK_EXPOSURE_MASK |
2537                             GDK_BUTTON_PRESS_MASK |
2538                             GDK_BUTTON_RELEASE_MASK |
2539                             GDK_KEY_PRESS_MASK |
2540                             GDK_POINTER_MOTION_MASK |
2541                             GDK_POINTER_MOTION_HINT_MASK);
2542   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP |
2543     GDK_WA_CURSOR;
2544
2545   attributes.cursor = gdk_cursor_new (GDK_TOP_LEFT_ARROW);
2546
2547   /* main window */
2548   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
2549
2550   gdk_window_set_user_data (widget->window, sheet);
2551
2552   widget->style = gtk_style_attach (widget->style, widget->window);
2553
2554   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
2555
2556   attributes.x = 0;
2557   if (sheet->row_titles_visible)
2558     attributes.x = sheet->row_title_area.width;
2559   attributes.y = 0;
2560   attributes.width = sheet->column_title_area.width;
2561   attributes.height = sheet->column_title_area.height;
2562
2563   /* column - title window */
2564   sheet->column_title_window = gdk_window_new (widget->window, &attributes, attributes_mask);
2565   gdk_window_set_user_data (sheet->column_title_window, sheet);
2566   gtk_style_set_background (widget->style, sheet->column_title_window, GTK_STATE_NORMAL);
2567
2568   attributes.x = 0;
2569   attributes.y = 0;
2570   if (sheet->column_titles_visible)
2571     attributes.y = sheet->column_title_area.height;
2572   attributes.width = sheet->row_title_area.width;
2573   attributes.height = sheet->row_title_area.height;
2574
2575   /* row - title window */
2576   sheet->row_title_window = gdk_window_new (widget->window, &attributes, attributes_mask);
2577   gdk_window_set_user_data (sheet->row_title_window, sheet);
2578   gtk_style_set_background (widget->style, sheet->row_title_window, GTK_STATE_NORMAL);
2579
2580   /* sheet - window */
2581   attributes.cursor = gdk_cursor_new (GDK_PLUS);
2582
2583   attributes.x = 0;
2584   attributes.y = 0;
2585   attributes.width = sheet->sheet_window_width,
2586     attributes.height = sheet->sheet_window_height;
2587
2588   sheet->sheet_window = gdk_window_new (widget->window, &attributes, attributes_mask);
2589   gdk_window_set_user_data (sheet->sheet_window, sheet);
2590
2591   gdk_cursor_unref (attributes.cursor);
2592
2593   gdk_window_set_background (sheet->sheet_window, &widget->style->white);
2594   gdk_window_show (sheet->sheet_window);
2595
2596   /* backing_pixmap */
2597   gtk_sheet_make_backing_pixmap (sheet, 0, 0);
2598
2599   /* GCs */
2600   if (sheet->fg_gc)
2601     g_object_unref (sheet->fg_gc);
2602   if (sheet->bg_gc)
2603     g_object_unref (sheet->bg_gc);
2604   sheet->fg_gc = gdk_gc_new (widget->window);
2605   sheet->bg_gc = gdk_gc_new (widget->window);
2606
2607   colormap = gtk_widget_get_colormap (widget);
2608
2609   gdk_gc_get_values (sheet->fg_gc, &auxvalues);
2610
2611   values.foreground = widget->style->white;
2612   values.function = GDK_INVERT;
2613   values.subwindow_mode = GDK_INCLUDE_INFERIORS;
2614   if (sheet->xor_gc)
2615     g_object_unref (sheet->xor_gc);
2616   sheet->xor_gc = gdk_gc_new_with_values (widget->window,
2617                                           &values,
2618                                           GDK_GC_FOREGROUND |
2619                                           GDK_GC_FUNCTION |
2620                                           GDK_GC_SUBWINDOW);
2621
2622
2623   gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
2624   gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
2625
2626   gtk_widget_set_parent_window (sheet->button, sheet->sheet_window);
2627   gtk_widget_set_parent (sheet->button, GTK_WIDGET (sheet));
2628
2629
2630   gdk_cursor_unref (sheet->cursor_drag);
2631   sheet->cursor_drag = gdk_cursor_new (GDK_PLUS);
2632
2633   if (sheet->column_titles_visible)
2634     gdk_window_show (sheet->column_title_window);
2635   if (sheet->row_titles_visible)
2636     gdk_window_show (sheet->row_title_window);
2637
2638   size_allocate_row_title_buttons (sheet);
2639   size_allocate_column_title_buttons (sheet);
2640
2641   children = sheet->children;
2642   while (children)
2643     {
2644       child = children->data;
2645       children = children->next;
2646
2647       gtk_sheet_realize_child (sheet, child);
2648     }
2649
2650   gtk_sheet_update_primary_selection (sheet);
2651 }
2652
2653 static void
2654 create_global_button (GtkSheet *sheet)
2655 {
2656   sheet->button = gtk_button_new_with_label (" ");
2657
2658   g_object_ref_sink (sheet->button);
2659
2660   g_signal_connect (sheet->button,
2661                     "pressed",
2662                     G_CALLBACK (global_button_clicked),
2663                     sheet);
2664 }
2665
2666 static void
2667 size_allocate_global_button (GtkSheet *sheet)
2668 {
2669   GtkAllocation allocation;
2670
2671   if (!sheet->column_titles_visible) return;
2672   if (!sheet->row_titles_visible) return;
2673
2674   gtk_widget_size_request (sheet->button, NULL);
2675
2676   allocation.x = 0;
2677   allocation.y = 0;
2678   allocation.width = sheet->row_title_area.width;
2679   allocation.height = sheet->column_title_area.height;
2680
2681   gtk_widget_size_allocate (sheet->button, &allocation);
2682   gtk_widget_show (sheet->button);
2683 }
2684
2685 static void
2686 global_button_clicked (GtkWidget *widget, gpointer data)
2687 {
2688   gboolean veto;
2689
2690   gtk_sheet_click_cell (GTK_SHEET (data), - 1, - 1, &veto);
2691   gtk_widget_grab_focus (GTK_WIDGET (data));
2692 }
2693
2694
2695 static void
2696 gtk_sheet_unrealize (GtkWidget * widget)
2697 {
2698   GtkSheet *sheet;
2699
2700   g_return_if_fail (widget != NULL);
2701   g_return_if_fail (GTK_IS_SHEET (widget));
2702
2703   sheet = GTK_SHEET (widget);
2704
2705   gdk_cursor_unref (sheet->cursor_drag);
2706
2707   g_object_unref (sheet->xor_gc);
2708   g_object_unref (sheet->fg_gc);
2709   g_object_unref (sheet->bg_gc);
2710
2711   gdk_window_destroy (sheet->sheet_window);
2712   gdk_window_destroy (sheet->column_title_window);
2713   gdk_window_destroy (sheet->row_title_window);
2714
2715   if (sheet->pixmap)
2716     {
2717       g_object_unref (sheet->pixmap);
2718       sheet->pixmap = NULL;
2719     }
2720
2721   sheet->column_title_window = NULL;
2722   sheet->sheet_window = NULL;
2723   sheet->xor_gc = NULL;
2724   sheet->fg_gc = NULL;
2725   sheet->bg_gc = NULL;
2726
2727   gtk_widget_unparent (sheet->entry_widget);
2728   gtk_widget_unparent (sheet->button);
2729
2730   if (GTK_WIDGET_CLASS (parent_class)->unrealize)
2731     (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
2732 }
2733
2734 static void
2735 gtk_sheet_map (GtkWidget * widget)
2736 {
2737   GtkSheet *sheet = GTK_SHEET (widget);
2738   GtkSheetChild *child;
2739   GList *children;
2740
2741   g_return_if_fail (widget != NULL);
2742   g_return_if_fail (GTK_IS_SHEET (widget));
2743
2744   if (!GTK_WIDGET_MAPPED (widget))
2745     {
2746       GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
2747
2748       gdk_window_show (widget->window);
2749       gdk_window_show (sheet->sheet_window);
2750
2751       if (sheet->column_titles_visible)
2752         {
2753           size_allocate_column_title_buttons (sheet);
2754           gdk_window_show (sheet->column_title_window);
2755         }
2756       if (sheet->row_titles_visible)
2757         {
2758           size_allocate_row_title_buttons (sheet);
2759           gdk_window_show (sheet->row_title_window);
2760         }
2761
2762       if (!GTK_WIDGET_MAPPED (sheet->entry_widget)
2763           && sheet->active_cell.row >= 0
2764           && sheet->active_cell.col >= 0 )
2765         {
2766           gtk_widget_show (sheet->entry_widget);
2767           gtk_widget_map (sheet->entry_widget);
2768         }
2769
2770       if (GTK_WIDGET_VISIBLE (sheet->button) &&
2771           !GTK_WIDGET_MAPPED (sheet->button))
2772         {
2773           gtk_widget_show (sheet->button);
2774           gtk_widget_map (sheet->button);
2775         }
2776
2777       if (GTK_BIN (sheet->button)->child)
2778         if (GTK_WIDGET_VISIBLE (GTK_BIN (sheet->button)->child) &&
2779             !GTK_WIDGET_MAPPED (GTK_BIN (sheet->button)->child))
2780           gtk_widget_map (GTK_BIN (sheet->button)->child);
2781
2782       gtk_sheet_range_draw (sheet, NULL);
2783       gtk_sheet_activate_cell (sheet,
2784                                sheet->active_cell.row,
2785                                sheet->active_cell.col);
2786
2787       children = sheet->children;
2788       while (children)
2789         {
2790           child = children->data;
2791           children = children->next;
2792
2793           if (GTK_WIDGET_VISIBLE (child->widget) &&
2794               !GTK_WIDGET_MAPPED (child->widget))
2795             {
2796               gtk_widget_map (child->widget);
2797               gtk_sheet_position_child (sheet, child);
2798             }
2799         }
2800
2801     }
2802 }
2803
2804 static void
2805 gtk_sheet_unmap (GtkWidget * widget)
2806 {
2807   GtkSheet *sheet;
2808   GtkSheetChild *child;
2809   GList *children;
2810
2811   g_return_if_fail (widget != NULL);
2812   g_return_if_fail (GTK_IS_SHEET (widget));
2813
2814   sheet = GTK_SHEET (widget);
2815
2816   if (GTK_WIDGET_MAPPED (widget))
2817     {
2818       GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
2819
2820       gdk_window_hide (sheet->sheet_window);
2821       if (sheet->column_titles_visible)
2822         gdk_window_hide (sheet->column_title_window);
2823       if (sheet->row_titles_visible)
2824         gdk_window_hide (sheet->row_title_window);
2825       gdk_window_hide (widget->window);
2826
2827       if (GTK_WIDGET_MAPPED (sheet->entry_widget))
2828         gtk_widget_unmap (sheet->entry_widget);
2829
2830       if (GTK_WIDGET_MAPPED (sheet->button))
2831         gtk_widget_unmap (sheet->button);
2832
2833       children = sheet->children;
2834       while (children)
2835         {
2836           child = children->data;
2837           children = children->next;
2838
2839           if (GTK_WIDGET_VISIBLE (child->widget) &&
2840               GTK_WIDGET_MAPPED (child->widget))
2841             {
2842               gtk_widget_unmap (child->widget);
2843             }
2844         }
2845
2846     }
2847 }
2848
2849
2850 static void
2851 gtk_sheet_cell_draw_default (GtkSheet *sheet, gint row, gint col)
2852 {
2853   GtkWidget *widget;
2854   GdkGC *fg_gc, *bg_gc;
2855   GtkSheetCellAttr attributes;
2856   GdkRectangle area;
2857
2858   g_return_if_fail (sheet != NULL);
2859
2860   /* bail now if we arn't drawable yet */
2861   if (!GTK_WIDGET_DRAWABLE (sheet)) return;
2862
2863   if (row < 0 || row >= yyy_row_count (sheet)) return;
2864   if (col < 0 || col >= xxx_column_count (sheet)) return;
2865   if (! xxx_column_is_visible (sheet, col)) return;
2866   if (! yyy_row_is_visible (sheet, row)) return;
2867
2868   widget = GTK_WIDGET (sheet);
2869
2870   gtk_sheet_get_attributes (sheet, row, col, &attributes);
2871
2872   /* select GC for background rectangle */
2873   gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground);
2874   gdk_gc_set_foreground (sheet->bg_gc, &attributes.background);
2875
2876   fg_gc = sheet->fg_gc;
2877   bg_gc = sheet->bg_gc;
2878
2879   area.x = COLUMN_LEFT_XPIXEL (sheet, col);
2880   area.y = ROW_TOP_YPIXEL (sheet, row);
2881   area.width= xxx_column_width (sheet, col);
2882   area.height = yyy_row_height (sheet, row);
2883
2884   gdk_draw_rectangle (sheet->pixmap,
2885                       bg_gc,
2886                       TRUE,
2887                       area.x,
2888                       area.y,
2889                       area.width,
2890                       area.height);
2891
2892   gdk_gc_set_line_attributes (sheet->fg_gc, 1, 0, 0, 0);
2893
2894   if (sheet->show_grid)
2895     {
2896       gdk_gc_set_foreground (sheet->bg_gc, &sheet->grid_color);
2897
2898       gdk_draw_rectangle (sheet->pixmap,
2899                           sheet->bg_gc,
2900                           FALSE,
2901                           area.x, area.y,
2902                           area.width, area.height);
2903     }
2904 }
2905
2906 static void
2907 gtk_sheet_cell_draw_label (GtkSheet *sheet, gint row, gint col)
2908 {
2909   GtkWidget *widget;
2910   GdkRectangle area;
2911   gint i;
2912   gint text_width, text_height, y;
2913   gint xoffset = 0;
2914   gint size, sizel, sizer;
2915   GdkGC *fg_gc, *bg_gc;
2916   GtkSheetCellAttr attributes;
2917   PangoLayout *layout;
2918   PangoRectangle rect;
2919   PangoRectangle logical_rect;
2920   PangoLayoutLine *line;
2921   PangoFontMetrics *metrics;
2922   PangoContext *context = gtk_widget_get_pango_context (GTK_WIDGET (sheet));
2923   gint ascent, descent, y_pos;
2924
2925   gchar *label;
2926
2927   g_return_if_fail (sheet != NULL);
2928
2929   /* bail now if we aren't drawable yet */
2930   if (!GTK_WIDGET_DRAWABLE (sheet))
2931     return;
2932
2933   label = gtk_sheet_cell_get_text (sheet, row, col);
2934   if (!label)
2935     return;
2936
2937   if (row < 0 || row >= yyy_row_count (sheet)) return;
2938   if (col < 0 || col >= xxx_column_count (sheet)) return;
2939   if (! xxx_column_is_visible (sheet, col)) return;
2940   if (!yyy_row_is_visible (sheet, row)) return;
2941
2942
2943   widget = GTK_WIDGET (sheet);
2944
2945   gtk_sheet_get_attributes (sheet, row, col, &attributes);
2946
2947   /* select GC for background rectangle */
2948   gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground);
2949   gdk_gc_set_foreground (sheet->bg_gc, &attributes.background);
2950
2951   fg_gc = sheet->fg_gc;
2952   bg_gc = sheet->bg_gc;
2953
2954   area.x = COLUMN_LEFT_XPIXEL (sheet, col);
2955   area.y = ROW_TOP_YPIXEL (sheet, row);
2956   area.width = xxx_column_width (sheet, col);
2957   area.height = yyy_row_height (sheet, row);
2958
2959
2960   layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), label);
2961   dispose_string (sheet, label);
2962   pango_layout_set_font_description (layout, attributes.font_desc);
2963
2964   pango_layout_get_pixel_extents (layout, NULL, &rect);
2965
2966   line = pango_layout_get_lines (layout)->data;
2967   pango_layout_line_get_extents (line, NULL, &logical_rect);
2968
2969   metrics = pango_context_get_metrics (context,
2970                                        attributes.font_desc,
2971                                        pango_context_get_language (context));
2972
2973   ascent = pango_font_metrics_get_ascent (metrics) / PANGO_SCALE;
2974   descent = pango_font_metrics_get_descent (metrics) / PANGO_SCALE;
2975
2976   pango_font_metrics_unref (metrics);
2977
2978   /* Align primarily for locale's ascent / descent */
2979
2980   logical_rect.height /= PANGO_SCALE;
2981   logical_rect.y /= PANGO_SCALE;
2982   y_pos = area.height - logical_rect.height;
2983
2984   if (logical_rect.height > area.height)
2985     y_pos = (logical_rect.height - area.height - 2 * CELLOFFSET) / 2;
2986   else if (y_pos < 0)
2987     y_pos = 0;
2988   else if (y_pos + logical_rect.height > area.height)
2989     y_pos = area.height - logical_rect.height;
2990
2991   text_width = rect.width;
2992   text_height = rect.height;
2993   y = area.y + y_pos - CELLOFFSET;
2994
2995   switch (attributes.justification)
2996     {
2997     case GTK_JUSTIFY_RIGHT:
2998       size = area.width;
2999       area.x +=area.width;
3000       {
3001         for (i = col - 1; i >= MIN_VISIBLE_COLUMN (sheet); i--)
3002           {
3003             if ( !gtk_sheet_cell_empty (sheet, row, i)) break;
3004             if (size >= text_width + CELLOFFSET) break;
3005             size +=xxx_column_width (sheet, i);
3006             xxx_column_set_right_column (sheet, i,
3007                                          MAX (col,
3008                                               xxx_column_right_column (sheet, i)));
3009           }
3010         area.width = size;
3011       }
3012       area.x -= size;
3013       xoffset += area.width - text_width - 2 * CELLOFFSET -
3014         attributes.border.width / 2;
3015       break;
3016     case GTK_JUSTIFY_CENTER:
3017       sizel = area.width / 2;
3018       sizer = area.width / 2;
3019       area.x += area.width / 2;
3020       {
3021         for (i = col + 1; i <= MAX_VISIBLE_COLUMN (sheet); i++)
3022           {
3023             if ( ! gtk_sheet_cell_empty (sheet, row, i)) break;
3024             if (sizer >= text_width / 2) break;
3025             sizer += xxx_column_width (sheet, i);
3026             xxx_column_set_left_column (sheet, i,
3027                                         MIN (
3028                                              col,
3029                                              xxx_column_left_column (sheet, i)));
3030           }
3031         for (i = col - 1; i >= MIN_VISIBLE_COLUMN (sheet); i--)
3032           {
3033             if ( ! gtk_sheet_cell_empty (sheet, row, i)) break;
3034             if (sizel >= text_width / 2) break;
3035             sizel +=xxx_column_width (sheet, i);
3036             xxx_column_set_right_column (sheet, i,
3037                                          MAX (col,
3038                                               xxx_column_right_column (sheet, i)));
3039           }
3040         size = MIN (sizel, sizer);
3041       }
3042       area.x -= sizel;
3043       xoffset += sizel - text_width / 2 - CELLOFFSET;
3044       area.width = sizel + sizer;
3045       break;
3046     case GTK_JUSTIFY_LEFT:
3047     default:
3048       size = area.width;
3049       {
3050         for (i = col + 1; i <= MAX_VISIBLE_COLUMN (sheet); i++)
3051           {
3052             if (! gtk_sheet_cell_empty (sheet, row, i)) break;
3053             if (size >= text_width + CELLOFFSET) break;
3054             size +=xxx_column_width (sheet, i);
3055             xxx_column_set_left_column (sheet, i,
3056                                         MIN (
3057                                              col,
3058                                              xxx_column_left_column (sheet, i)));
3059
3060           }
3061         area.width = size;
3062       }
3063       xoffset += attributes.border.width / 2;
3064       break;
3065     }
3066
3067   gdk_gc_set_clip_rectangle (fg_gc, &area);
3068
3069
3070   gdk_draw_layout (sheet->pixmap, fg_gc,
3071                    area.x + xoffset + CELLOFFSET,
3072                    y,
3073                    layout);
3074
3075   gdk_gc_set_clip_rectangle (fg_gc, NULL);
3076   g_object_unref (layout);
3077
3078   gdk_draw_drawable (sheet->sheet_window,
3079                    GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
3080                    sheet->pixmap,
3081                    area.x,
3082                    area.y,
3083                    area.x,
3084                    area.y,
3085                    area.width,
3086                    area.height);
3087
3088 }
3089
3090 static void
3091 gtk_sheet_range_draw (GtkSheet *sheet, const GtkSheetRange *range)
3092 {
3093   gint i, j;
3094   GtkSheetRange drawing_range;
3095   GdkRectangle area;
3096
3097   g_return_if_fail (sheet != NULL);
3098   g_return_if_fail (GTK_SHEET (sheet));
3099
3100   if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (sheet))) return;
3101   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
3102   if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
3103
3104   if (range == NULL)
3105     {
3106       drawing_range.row0 = MIN_VISIBLE_ROW (sheet);
3107       drawing_range.col0 = MIN_VISIBLE_COLUMN (sheet);
3108       drawing_range.rowi = MIN (MAX_VISIBLE_ROW (sheet),
3109                                 yyy_row_count (sheet) - 1);
3110       drawing_range.coli = MAX_VISIBLE_COLUMN (sheet);
3111
3112
3113       gdk_draw_rectangle (sheet->pixmap,
3114                           GTK_WIDGET (sheet)->style->white_gc,
3115                           TRUE,
3116                           0, 0,
3117                           sheet->sheet_window_width,
3118                           sheet->sheet_window_height);
3119     }
3120   else
3121     {
3122       drawing_range.row0 = MAX (range->row0, MIN_VISIBLE_ROW (sheet));
3123       drawing_range.col0 = MAX (range->col0, MIN_VISIBLE_COLUMN (sheet));
3124       drawing_range.rowi = MIN (range->rowi, MAX_VISIBLE_ROW (sheet));
3125       drawing_range.coli = MIN (range->coli, MAX_VISIBLE_COLUMN (sheet));
3126     }
3127
3128   if (drawing_range.coli == xxx_column_count (sheet) - 1)
3129     {
3130       area.x = COLUMN_LEFT_XPIXEL (sheet,
3131                                    xxx_column_count (sheet) - 1) +
3132         xxx_column_width (sheet, xxx_column_count (sheet) - 1) + 1;
3133
3134       area.y = 0;
3135
3136       gdk_gc_set_foreground (sheet->fg_gc, &sheet->bg_color);
3137
3138       gdk_draw_rectangle (sheet->pixmap,
3139                           sheet->fg_gc,
3140                           TRUE,
3141                           area.x, area.y,
3142                           sheet->sheet_window_width - area.x,
3143                           sheet->sheet_window_height);
3144
3145       gdk_draw_drawable (sheet->sheet_window,
3146                        GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
3147                        sheet->pixmap,
3148                        area.x,
3149                        area.y,
3150                        area.x,
3151                        area.y,
3152                        sheet->sheet_window_width - area.x,
3153                        sheet->sheet_window_height);
3154     }
3155
3156   if (drawing_range.rowi == yyy_row_count (sheet) - 1)
3157     {
3158       area.x = 0;
3159       area.y = ROW_TOP_YPIXEL (sheet,
3160                                yyy_row_count (sheet) - 1) +
3161         yyy_row_height (sheet, yyy_row_count (sheet) - 1) + 1;
3162
3163       gdk_gc_set_foreground (sheet->fg_gc, &sheet->bg_color);
3164
3165       gdk_draw_rectangle (sheet->pixmap,
3166                           sheet->fg_gc,
3167                           TRUE,
3168                           area.x, area.y,
3169                           sheet->sheet_window_width,
3170                           sheet->sheet_window_height - area.y);
3171
3172       gdk_draw_drawable (sheet->sheet_window,
3173                        GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
3174                        sheet->pixmap,
3175                        area.x,
3176                        area.y,
3177                        area.x,
3178                        area.y,
3179                        sheet->sheet_window_width,
3180                        sheet->sheet_window_height - area.y);
3181     }
3182
3183   for (i = drawing_range.row0; i <= drawing_range.rowi; i++)
3184     for (j = drawing_range.col0; j <= drawing_range.coli; j++)
3185       {
3186         gtk_sheet_cell_draw_default (sheet, i, j);
3187         gtk_sheet_cell_draw_label (sheet, i, j);
3188       }
3189
3190   gtk_sheet_draw_backing_pixmap (sheet, drawing_range);
3191
3192   if (sheet->state != GTK_SHEET_NORMAL &&
3193       gtk_sheet_range_isvisible (sheet, sheet->range))
3194     gtk_sheet_range_draw_selection (sheet, drawing_range);
3195
3196   if (sheet->state == GTK_STATE_NORMAL &&
3197       sheet->active_cell.row >= drawing_range.row0 &&
3198       sheet->active_cell.row <= drawing_range.rowi &&
3199       sheet->active_cell.col >= drawing_range.col0 &&
3200       sheet->active_cell.col <= drawing_range.coli)
3201     gtk_sheet_show_active_cell (sheet);
3202 }
3203
3204 static void
3205 gtk_sheet_range_draw_selection (GtkSheet *sheet, GtkSheetRange range)
3206 {
3207   GdkRectangle area;
3208   gint i, j;
3209   GtkSheetRange aux;
3210
3211   if (range.col0 > sheet->range.coli || range.coli < sheet->range.col0 ||
3212       range.row0 > sheet->range.rowi || range.rowi < sheet->range.row0)
3213     return;
3214
3215   if (!gtk_sheet_range_isvisible (sheet, range)) return;
3216   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
3217
3218   aux = range;
3219
3220   range.col0 = MAX (sheet->range.col0, range.col0);
3221   range.coli = MIN (sheet->range.coli, range.coli);
3222   range.row0 = MAX (sheet->range.row0, range.row0);
3223   range.rowi = MIN (sheet->range.rowi, range.rowi);
3224
3225   range.col0 = MAX (range.col0, MIN_VISIBLE_COLUMN (sheet));
3226   range.coli = MIN (range.coli, MAX_VISIBLE_COLUMN (sheet));
3227   range.row0 = MAX (range.row0, MIN_VISIBLE_ROW (sheet));
3228   range.rowi = MIN (range.rowi, MAX_VISIBLE_ROW (sheet));
3229
3230   for (i = range.row0; i <= range.rowi; i++)
3231     {
3232       for (j = range.col0; j <= range.coli; j++)
3233         {
3234
3235           if (gtk_sheet_cell_get_state (sheet, i, j) == GTK_STATE_SELECTED &&
3236               xxx_column_is_visible (sheet, j) && yyy_row_is_visible (sheet, i))
3237             {
3238
3239               area.x = COLUMN_LEFT_XPIXEL (sheet, j);
3240               area.y = ROW_TOP_YPIXEL (sheet, i);
3241               area.width= xxx_column_width (sheet, j);
3242               area.height = yyy_row_height (sheet, i);
3243
3244               if (i == sheet->range.row0)
3245                 {
3246                   area.y = area.y + 2;
3247                   area.height = area.height - 2;
3248                 }
3249               if (i == sheet->range.rowi) area.height = area.height - 3;
3250               if (j == sheet->range.col0)
3251                 {
3252                   area.x = area.x + 2;
3253                   area.width = area.width - 2;
3254                 }
3255               if (j == sheet->range.coli) area.width = area.width - 3;
3256
3257               if (i != sheet->active_cell.row || j != sheet->active_cell.col)
3258                 {
3259                   gdk_draw_rectangle (sheet->sheet_window,
3260                                       sheet->xor_gc,
3261                                       TRUE,
3262                                       area.x + 1, area.y + 1,
3263                                       area.width, area.height);
3264                 }
3265             }
3266
3267         }
3268     }
3269
3270   gtk_sheet_draw_border (sheet, sheet->range);
3271 }
3272
3273 static void
3274 gtk_sheet_draw_backing_pixmap (GtkSheet *sheet, GtkSheetRange range)
3275 {
3276   gint x, y, width, height;
3277
3278   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
3279
3280   x = COLUMN_LEFT_XPIXEL (sheet, range.col0);
3281   y = ROW_TOP_YPIXEL (sheet, range.row0);
3282   width = COLUMN_LEFT_XPIXEL (sheet, range.coli) - x +
3283     xxx_column_width (sheet, range.coli);
3284
3285   height = ROW_TOP_YPIXEL (sheet, range.rowi)- y + yyy_row_height (sheet, range.rowi);
3286
3287   if (range.row0 == sheet->range.row0)
3288     {
3289       y = y - 5;
3290       height = height + 5;
3291     }
3292   if (range.rowi == sheet->range.rowi) height = height + 5;
3293   if (range.col0 == sheet->range.col0)
3294     {
3295       x = x - 5;
3296       width = width + 5;
3297     }
3298   if (range.coli == sheet->range.coli) width = width + 5;
3299
3300   width = MIN (width, sheet->sheet_window_width - x);
3301   height = MIN (height, sheet->sheet_window_height - y);
3302
3303   x--;
3304   y--;
3305   width +=2;
3306   height +=2;
3307
3308   x = (sheet->row_titles_visible)
3309     ? MAX (x, sheet->row_title_area.width) : MAX (x, 0);
3310   y = (sheet->column_titles_visible)
3311     ? MAX (y, sheet->column_title_area.height) : MAX (y, 0);
3312
3313   if (range.coli == xxx_column_count (sheet) - 1)
3314     width = sheet->sheet_window_width - x;
3315   if (range.rowi == yyy_row_count (sheet) - 1)
3316     height = sheet->sheet_window_height - y;
3317
3318   gdk_draw_drawable (sheet->sheet_window,
3319                    GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
3320                    sheet->pixmap,
3321                    x,
3322                    y,
3323                    x,
3324                    y,
3325                    width + 1,
3326                    height + 1);
3327 }
3328
3329
3330 void
3331 gtk_sheet_set_cell_text (GtkSheet *sheet, gint row, gint col, const gchar *text)
3332 {
3333   GtkSheetCellAttr attributes;
3334
3335   g_return_if_fail (sheet != NULL);
3336   g_return_if_fail (GTK_IS_SHEET (sheet));
3337   if (col >= xxx_column_count (sheet) || row >= yyy_row_count (sheet)) return;
3338   if (col < 0 || row < 0) return;
3339
3340   gtk_sheet_get_attributes (sheet, row, col, &attributes);
3341   gtk_sheet_set_cell (sheet, row, col, attributes.justification, text);
3342 }
3343
3344 static inline gint
3345 safe_strcmp (const gchar *s1, const gchar *s2)
3346 {
3347   if ( !s1 && !s2) return 0;
3348   if ( !s1) return - 1;
3349   if ( !s2) return +1;
3350   return strcmp (s1, s2);
3351 }
3352
3353 void
3354 gtk_sheet_set_cell (GtkSheet *sheet, gint row, gint col,
3355                     GtkJustification justification,
3356                     const gchar *text)
3357 {
3358   GSheetModel *model ;
3359   gboolean changed ;
3360   gchar *old_text ;
3361
3362   GtkSheetRange range;
3363   gint text_width;
3364   GtkSheetCellAttr attributes;
3365
3366   g_return_if_fail (sheet != NULL);
3367   g_return_if_fail (GTK_IS_SHEET (sheet));
3368   if (col >= xxx_column_count (sheet) || row >= yyy_row_count (sheet)) return;
3369   if (col < 0 || row < 0) return;
3370
3371   gtk_sheet_get_attributes (sheet, row, col, &attributes);
3372
3373   attributes.justification = justification;
3374
3375   model = gtk_sheet_get_model (sheet);
3376
3377   old_text = g_sheet_model_get_string (model, row, col);
3378
3379   changed = FALSE;
3380
3381   if (0 != safe_strcmp (old_text, text))
3382     changed = g_sheet_model_set_string (model, text, row, col);
3383
3384   if ( g_sheet_model_free_strings (model))
3385     g_free (old_text);
3386
3387
3388   if (changed && attributes.is_visible)
3389     {
3390       gchar *s = gtk_sheet_cell_get_text (sheet, row, col);
3391       text_width = 0;
3392       if (s && strlen (s) > 0)
3393         {
3394           text_width = STRING_WIDTH (GTK_WIDGET (sheet),
3395                                      attributes.font_desc, text);
3396         }
3397       dispose_string (sheet, s);
3398
3399       range.row0 = row;
3400       range.rowi = row;
3401       range.col0 = MIN_VISIBLE_COLUMN (sheet);
3402       range.coli = MAX_VISIBLE_COLUMN (sheet);
3403
3404       if (gtk_sheet_autoresize (sheet) &&
3405           text_width > xxx_column_width (sheet, col) -
3406           2 * CELLOFFSET- attributes.border.width)
3407         {
3408           gtk_sheet_set_column_width (sheet, col, text_width + 2 * CELLOFFSET
3409                                       + attributes.border.width);
3410           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_REDRAW_PENDING);
3411         }
3412       else
3413         if (!GTK_SHEET_IS_FROZEN (sheet))
3414           gtk_sheet_range_draw (sheet, &range);
3415     }
3416
3417   if ( changed )
3418     g_signal_emit (sheet, sheet_signals[CHANGED], 0, row, col);
3419
3420 }
3421
3422
3423 void
3424 gtk_sheet_cell_clear (GtkSheet *sheet, gint row, gint column)
3425 {
3426   GtkSheetRange range;
3427
3428   g_return_if_fail (sheet != NULL);
3429   g_return_if_fail (GTK_IS_SHEET (sheet));
3430   if (column >= xxx_column_count (sheet) ||
3431       row >= yyy_row_count (sheet)) return;
3432
3433   if (column < 0 || row < 0) return;
3434
3435   range.row0 = row;
3436   range.rowi = row;
3437   range.col0 = MIN_VISIBLE_COLUMN (sheet);
3438   range.coli = MAX_VISIBLE_COLUMN (sheet);
3439
3440   gtk_sheet_real_cell_clear (sheet, row, column);
3441
3442   if (!GTK_SHEET_IS_FROZEN (sheet))
3443     {
3444       gtk_sheet_range_draw (sheet, &range);
3445     }
3446 }
3447
3448 static void
3449 gtk_sheet_real_cell_clear (GtkSheet *sheet, gint row, gint column)
3450 {
3451   GSheetModel *model = gtk_sheet_get_model (sheet);
3452
3453   gchar *old_text = gtk_sheet_cell_get_text (sheet, row, column);
3454
3455   if (old_text && strlen (old_text) > 0 )
3456     {
3457       g_sheet_model_datum_clear (model, row, column);
3458     }
3459
3460   dispose_string (sheet, old_text);
3461 }
3462
3463 void
3464 gtk_sheet_range_clear (GtkSheet *sheet, const GtkSheetRange *range)
3465 {
3466   g_return_if_fail (sheet != NULL);
3467   g_return_if_fail (GTK_IS_SHEET (sheet));
3468
3469   gtk_sheet_real_range_clear (sheet, range);
3470 }
3471
3472 static void
3473 gtk_sheet_real_range_clear (GtkSheet *sheet, const GtkSheetRange *range)
3474 {
3475   gint i, j;
3476   GtkSheetRange clear;
3477
3478   if (!range)
3479     {
3480       clear.row0 = 0;
3481       clear.rowi = yyy_row_count (sheet) - 1;
3482       clear.col0 = 0;
3483       clear.coli = xxx_column_count (sheet) - 1;
3484     }
3485   else
3486     clear=*range;
3487
3488   clear.row0 = MAX (clear.row0, 0);
3489   clear.col0 = MAX (clear.col0, 0);
3490   clear.rowi = MIN (clear.rowi, yyy_row_count (sheet) - 1 );
3491   clear.coli = MIN (clear.coli, xxx_column_count (sheet) - 1 );
3492
3493   for (i = clear.row0; i <= clear.rowi; i++)
3494     for (j = clear.col0; j <= clear.coli; j++)
3495       {
3496         gtk_sheet_real_cell_clear (sheet, i, j);
3497       }
3498
3499   gtk_sheet_range_draw (sheet, NULL);
3500 }
3501
3502
3503 static gboolean
3504 gtk_sheet_cell_empty (const GtkSheet *sheet, gint row, gint col)
3505 {
3506   gboolean empty;
3507   char *text = gtk_sheet_cell_get_text (sheet, row, col);
3508   empty = (text == NULL );
3509
3510   dispose_string (sheet, text);
3511
3512   return empty;
3513 }
3514
3515
3516 gchar *
3517 gtk_sheet_cell_get_text (const GtkSheet *sheet, gint row, gint col)
3518 {
3519   GSheetModel *model;
3520   g_return_val_if_fail (sheet != NULL, NULL);
3521   g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);
3522
3523   if (col >= xxx_column_count (sheet) || row >= yyy_row_count (sheet))
3524     return NULL;
3525   if (col < 0 || row < 0) return NULL;
3526
3527   model = gtk_sheet_get_model (sheet);
3528
3529   if ( !model )
3530     return NULL;
3531
3532   return g_sheet_model_get_string (model, row, col);
3533 }
3534
3535
3536 GtkStateType
3537 gtk_sheet_cell_get_state (GtkSheet *sheet, gint row, gint col)
3538 {
3539   gint state;
3540   GtkSheetRange *range;
3541
3542   g_return_val_if_fail (sheet != NULL, 0);
3543   g_return_val_if_fail (GTK_IS_SHEET (sheet), 0);
3544   if (col >= xxx_column_count (sheet) || row >= yyy_row_count (sheet)) return 0;
3545   if (col < 0 || row < 0) return 0;
3546
3547   state = sheet->state;
3548   range = &sheet->range;
3549
3550   switch (state)
3551     {
3552     case GTK_SHEET_NORMAL:
3553       return GTK_STATE_NORMAL;
3554       break;
3555     case GTK_SHEET_ROW_SELECTED:
3556       if (row >= range->row0 && row <= range->rowi)
3557         return GTK_STATE_SELECTED;
3558       break;
3559     case GTK_SHEET_COLUMN_SELECTED:
3560       if (col >= range->col0 && col <= range->coli)
3561         return GTK_STATE_SELECTED;
3562       break;
3563     case GTK_SHEET_RANGE_SELECTED:
3564       if (row >= range->row0 && row <= range->rowi && \
3565           col >= range->col0 && col <= range->coli)
3566         return GTK_STATE_SELECTED;
3567       break;
3568     }
3569   return GTK_STATE_NORMAL;
3570 }
3571
3572 /* Convert X, Y (in pixels) to *ROW, *COLUMN (in cell coords)
3573    -1 indicates the title buttons.
3574    If the function returns FALSE, then the results will be unreliable.
3575 */
3576 gboolean
3577 gtk_sheet_get_pixel_info (GtkSheet *sheet,
3578                           gint x,
3579                           gint y,
3580                           gint *row,
3581                           gint *column)
3582 {
3583   gint trow, tcol;
3584   *row = -G_MAXINT;
3585   *column = -G_MAXINT;
3586
3587   g_return_val_if_fail (sheet != NULL, 0);
3588   g_return_val_if_fail (GTK_IS_SHEET (sheet), 0);
3589
3590   /* bounds checking, return false if the user clicked
3591      on a blank area */
3592   if (y < 0)
3593     return FALSE;
3594
3595   if (x < 0)
3596     return FALSE;
3597
3598   if ( y < sheet->column_title_area.height + sheet->column_title_area.y)
3599     *row = -1;
3600
3601   else
3602     {
3603       trow = ROW_FROM_YPIXEL (sheet, y);
3604       if (trow > yyy_row_count (sheet))
3605         return FALSE;
3606
3607       *row = trow;
3608     }
3609
3610   if ( x < sheet->row_title_area.width + sheet->row_title_area.x)
3611     *column = -1;
3612   else
3613     {
3614       tcol = COLUMN_FROM_XPIXEL (sheet, x);
3615       if (tcol > xxx_column_count (sheet))
3616         return FALSE;
3617
3618       *column = tcol;
3619     }
3620
3621   return TRUE;
3622 }
3623
3624 gboolean
3625 gtk_sheet_get_cell_area (GtkSheet * sheet,
3626                          gint row,
3627                          gint column,
3628                          GdkRectangle *area)
3629 {
3630   g_return_val_if_fail (sheet != NULL, 0);
3631   g_return_val_if_fail (GTK_IS_SHEET (sheet), 0);
3632
3633   if (row >= yyy_row_count (sheet) || column >= xxx_column_count (sheet))
3634     return FALSE;
3635
3636   area->x = (column == -1) ? 0 : (COLUMN_LEFT_XPIXEL (sheet, column) -
3637                                   (sheet->row_titles_visible
3638                                    ? sheet->row_title_area.width
3639                                    : 0));
3640   area->y = (row == -1) ? 0 : (ROW_TOP_YPIXEL (sheet, row) -
3641                                (sheet->column_titles_visible
3642                                 ? sheet->column_title_area.height
3643                                 : 0));
3644   area->width= (column == -1) ? sheet->row_title_area.width
3645     : xxx_column_width (sheet, column);
3646
3647   area->height= (row == -1) ? sheet->column_title_area.height
3648     : yyy_row_height (sheet, row);
3649
3650   return TRUE;
3651 }
3652
3653 gboolean
3654 gtk_sheet_set_active_cell (GtkSheet *sheet, gint row, gint column)
3655 {
3656   g_return_val_if_fail (sheet != NULL, 0);
3657   g_return_val_if_fail (GTK_IS_SHEET (sheet), 0);
3658
3659   if (row < - 1 || column < - 1) return FALSE;
3660   if (row >= yyy_row_count (sheet) || column >= xxx_column_count (sheet))
3661     return FALSE;
3662
3663   if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
3664     gtk_sheet_deactivate_cell (sheet);
3665
3666   sheet->active_cell.row = row;
3667   sheet->active_cell.col = column;
3668
3669   if ( row == -1 || column == -1)
3670     {
3671       gtk_sheet_hide_active_cell (sheet);
3672       return TRUE;
3673     }
3674
3675   if (!gtk_sheet_activate_cell (sheet, row, column)) return FALSE;
3676
3677   if (gtk_sheet_autoscroll (sheet))
3678     gtk_sheet_move_query (sheet, row, column);
3679
3680   return TRUE;
3681 }
3682
3683 void
3684 gtk_sheet_get_active_cell (GtkSheet *sheet, gint *row, gint *column)
3685 {
3686   g_return_if_fail (sheet != NULL);
3687   g_return_if_fail (GTK_IS_SHEET (sheet));
3688
3689   if ( row ) *row = sheet->active_cell.row;
3690   if (column) *column = sheet->active_cell.col;
3691 }
3692
3693 static void
3694 gtk_sheet_entry_changed (GtkWidget *widget, gpointer data)
3695 {
3696   GtkSheet *sheet;
3697   gint row, col;
3698   const char *text;
3699   GtkJustification justification;
3700   GtkSheetCellAttr attributes;
3701
3702   g_return_if_fail (data != NULL);
3703   g_return_if_fail (GTK_IS_SHEET (data));
3704
3705   sheet = GTK_SHEET (data);
3706
3707   if (!GTK_WIDGET_VISIBLE (widget)) return;
3708   if (sheet->state != GTK_STATE_NORMAL) return;
3709
3710   row = sheet->active_cell.row;
3711   col = sheet->active_cell.col;
3712
3713   if (row < 0 || col < 0) return;
3714
3715   sheet->active_cell.row = -1;
3716   sheet->active_cell.col = -1;
3717
3718   text = gtk_entry_get_text (GTK_ENTRY (gtk_sheet_get_entry (sheet)));
3719
3720   GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IS_FROZEN);
3721
3722   if (text && strlen (text) > 0)
3723     {
3724       gtk_sheet_get_attributes (sheet, row, col, &attributes);
3725       justification = attributes.justification;
3726       gtk_sheet_set_cell (sheet, row, col, justification, text);
3727     }
3728
3729   if (sheet->freeze_count == 0)
3730     GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IS_FROZEN);
3731
3732   sheet->active_cell.row = row;;
3733   sheet->active_cell.col = col;
3734 }
3735
3736
3737 static void
3738 gtk_sheet_deactivate_cell (GtkSheet *sheet)
3739 {
3740   g_return_if_fail (sheet != NULL);
3741   g_return_if_fail (GTK_IS_SHEET (sheet));
3742
3743   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return ;
3744   if (sheet->state != GTK_SHEET_NORMAL) return ;
3745
3746   if ( sheet->active_cell.row == -1 || sheet->active_cell.col == -1 )
3747     return ;
3748
3749   g_signal_emit (sheet, sheet_signals[DEACTIVATE], 0,
3750                  sheet->active_cell.row,
3751                  sheet->active_cell.col);
3752
3753
3754   g_signal_handlers_disconnect_by_func (gtk_sheet_get_entry (sheet),
3755                                         G_CALLBACK (gtk_sheet_entry_changed),
3756                                         sheet);
3757
3758   gtk_sheet_hide_active_cell (sheet);
3759   sheet->active_cell.row = -1;
3760   sheet->active_cell.col = -1;
3761
3762   if (GTK_SHEET_REDRAW_PENDING (sheet))
3763     {
3764       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_REDRAW_PENDING);
3765       gtk_sheet_range_draw (sheet, NULL);
3766     }
3767 }
3768
3769 static void
3770 gtk_sheet_hide_active_cell (GtkSheet *sheet)
3771 {
3772   const char *text;
3773   gint row, col;
3774   GtkJustification justification;
3775   GtkSheetCellAttr attributes;
3776
3777   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
3778
3779   row = sheet->active_cell.row;
3780   col = sheet->active_cell.col;
3781
3782   if (row < 0 || col < 0) return;
3783
3784   if (sheet->freeze_count == 0)
3785     GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IS_FROZEN);
3786
3787   text = gtk_entry_get_text (GTK_ENTRY (gtk_sheet_get_entry (sheet)));
3788
3789   gtk_sheet_get_attributes (sheet, row, col, &attributes);
3790   justification = attributes.justification;
3791
3792   row = sheet->active_cell.row;
3793   col = sheet->active_cell.col;
3794
3795   gtk_widget_hide (sheet->entry_widget);
3796   gtk_widget_unmap (sheet->entry_widget);
3797
3798   if (row != -1 && col != -1)
3799     gdk_draw_drawable (sheet->sheet_window,
3800                      GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
3801                      sheet->pixmap,
3802                      COLUMN_LEFT_XPIXEL (sheet, col)- 1,
3803                      ROW_TOP_YPIXEL (sheet, row)- 1,
3804                      COLUMN_LEFT_XPIXEL (sheet, col)- 1,
3805                      ROW_TOP_YPIXEL (sheet, row)- 1,
3806                      xxx_column_width (sheet, col) + 4,
3807                      yyy_row_height (sheet, row)+4);
3808
3809   gtk_widget_grab_focus (GTK_WIDGET (sheet));
3810
3811   GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
3812
3813 }
3814
3815 static gboolean
3816 gtk_sheet_activate_cell (GtkSheet *sheet, gint row, gint col)
3817 {
3818   gboolean veto = TRUE;
3819
3820   g_return_val_if_fail (sheet != NULL, FALSE);
3821   g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE);
3822
3823   if (row < 0 || col < 0) return FALSE;
3824
3825   if ( row > yyy_row_count (sheet) || col > xxx_column_count (sheet))
3826     return FALSE;
3827
3828   if (!veto) return FALSE;
3829   if (sheet->state != GTK_SHEET_NORMAL)
3830     {
3831       sheet->state = GTK_SHEET_NORMAL;
3832       gtk_sheet_real_unselect_range (sheet, NULL);
3833     }
3834
3835   sheet->range.row0 = row;
3836   sheet->range.col0 = col;
3837   sheet->range.rowi = row;
3838   sheet->range.coli = col;
3839   sheet->active_cell.row = row;
3840   sheet->active_cell.col = col;
3841   sheet->selection_cell.row = row;
3842   sheet->selection_cell.col = col;
3843
3844   GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
3845
3846   gtk_sheet_show_active_cell (sheet);
3847
3848   g_signal_connect (gtk_sheet_get_entry (sheet),
3849                     "changed",
3850                     G_CALLBACK (gtk_sheet_entry_changed),
3851                     sheet);
3852
3853   _gtkextra_signal_emit (GTK_OBJECT (sheet), sheet_signals [ACTIVATE], row, col, &veto);
3854
3855   return TRUE;
3856 }
3857
3858 static void
3859 gtk_sheet_show_active_cell (GtkSheet *sheet)
3860 {
3861   GtkEntry *sheet_entry;
3862   GtkSheetCellAttr attributes;
3863   gchar *text = NULL;
3864   const gchar *old_text;
3865   GtkJustification justification;
3866   gint row, col;
3867
3868   g_return_if_fail (sheet != NULL);
3869   g_return_if_fail (GTK_IS_SHEET (sheet));
3870
3871   row = sheet->active_cell.row;
3872   col = sheet->active_cell.col;
3873
3874   /* Don't show the active cell, if there is no active cell: */
3875   if (! (row >= 0 && col >= 0)) /* e.g row or coll == -1. */
3876     return;
3877
3878   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
3879   if (sheet->state != GTK_SHEET_NORMAL) return;
3880   if (GTK_SHEET_IN_SELECTION (sheet)) return;
3881
3882   GTK_WIDGET_SET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
3883
3884   sheet_entry = GTK_ENTRY (gtk_sheet_get_entry (sheet));
3885
3886   gtk_sheet_get_attributes (sheet, row, col, &attributes);
3887
3888   justification = GTK_JUSTIFY_LEFT;
3889
3890   if (gtk_sheet_justify_entry (sheet))
3891     justification = attributes.justification;
3892
3893   text = gtk_sheet_cell_get_text (sheet, row, col);
3894   if ( ! text )
3895     text = g_strdup ("");
3896
3897   gtk_entry_set_visibility (GTK_ENTRY (sheet_entry), attributes.is_visible);
3898
3899
3900   /*** Added by John Gotts. Mar 25, 2005 *********/
3901   old_text = gtk_entry_get_text (GTK_ENTRY (sheet_entry));
3902   if (strcmp (old_text, text) != 0)
3903     {
3904       if (!GTK_IS_ITEM_ENTRY (sheet_entry))
3905         gtk_entry_set_text (GTK_ENTRY (sheet_entry), text);
3906       else
3907         gtk_item_entry_set_text (GTK_ITEM_ENTRY (sheet_entry), text, justification);
3908     }
3909
3910   gtk_sheet_entry_set_max_size (sheet);
3911   gtk_sheet_size_allocate_entry (sheet);
3912
3913   gtk_widget_map (sheet->entry_widget);
3914
3915   gtk_widget_grab_focus (GTK_WIDGET (sheet_entry));
3916
3917   dispose_string (sheet, text);
3918 }
3919
3920 static void
3921 gtk_sheet_draw_active_cell (GtkSheet *sheet)
3922 {
3923   gint row, col;
3924   GtkSheetRange range;
3925
3926   if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (sheet))) return;
3927   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
3928
3929   row = sheet->active_cell.row;
3930   col = sheet->active_cell.col;
3931
3932   if (row < 0 || col < 0) return;
3933
3934   if (!gtk_sheet_cell_isvisible (sheet, row, col)) return;
3935
3936   range.col0 = range.coli = col;
3937   range.row0 = range.rowi = row;
3938
3939   gtk_sheet_draw_border (sheet, range);
3940 }
3941
3942
3943 static void
3944 gtk_sheet_make_backing_pixmap (GtkSheet *sheet, guint width, guint height)
3945 {
3946   gint pixmap_width, pixmap_height;
3947
3948   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
3949
3950   if (width == 0 && height == 0)
3951     {
3952       width = sheet->sheet_window_width + 80;
3953       height = sheet->sheet_window_height + 80;
3954     }
3955
3956   if (!sheet->pixmap)
3957     {
3958       /* allocate */
3959       sheet->pixmap = gdk_pixmap_new (sheet->sheet_window,
3960                                       width, height,
3961                                       - 1);
3962       if (!GTK_SHEET_IS_FROZEN (sheet)) gtk_sheet_range_draw (sheet, NULL);
3963     }
3964   else
3965     {
3966       /* reallocate if sizes don't match */
3967       gdk_drawable_get_size (sheet->pixmap,
3968                              &pixmap_width, &pixmap_height);
3969       if ( (pixmap_width != width) || (pixmap_height != height))
3970         {
3971           g_object_unref (sheet->pixmap);
3972           sheet->pixmap = gdk_pixmap_new (sheet->sheet_window,
3973                                           width, height,
3974                                           - 1);
3975           if (!GTK_SHEET_IS_FROZEN (sheet)) gtk_sheet_range_draw (sheet, NULL);
3976         }
3977     }
3978 }
3979
3980 static void
3981 gtk_sheet_new_selection (GtkSheet *sheet, GtkSheetRange *range)
3982 {
3983   gint i, j, mask1, mask2;
3984   gint state, selected;
3985   gint x, y, width, height;
3986   GtkSheetRange new_range, aux_range;
3987
3988   g_return_if_fail (sheet != NULL);
3989
3990   if (range == NULL) range=&sheet->range;
3991
3992   new_range=*range;
3993
3994   range->row0 = MIN (range->row0, sheet->range.row0);
3995   range->rowi = MAX (range->rowi, sheet->range.rowi);
3996   range->col0 = MIN (range->col0, sheet->range.col0);
3997   range->coli = MAX (range->coli, sheet->range.coli);
3998
3999   range->row0 = MAX (range->row0, MIN_VISIBLE_ROW (sheet));
4000   range->rowi = MIN (range->rowi, MAX_VISIBLE_ROW (sheet));
4001   range->col0 = MAX (range->col0, MIN_VISIBLE_COLUMN (sheet));
4002   range->coli = MIN (range->coli, MAX_VISIBLE_COLUMN (sheet));
4003
4004   aux_range.row0 = MAX (new_range.row0, MIN_VISIBLE_ROW (sheet));
4005   aux_range.rowi = MIN (new_range.rowi, MAX_VISIBLE_ROW (sheet));
4006   aux_range.col0 = MAX (new_range.col0, MIN_VISIBLE_COLUMN (sheet));
4007   aux_range.coli = MIN (new_range.coli, MAX_VISIBLE_COLUMN (sheet));
4008
4009   for (i = range->row0; i <= range->rowi; i++)
4010     {
4011       for (j = range->col0; j <= range->coli; j++)
4012         {
4013
4014           state = gtk_sheet_cell_get_state (sheet, i, j);
4015           selected= (i <= new_range.rowi && i >= new_range.row0 &&
4016                      j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
4017
4018           if (state == GTK_STATE_SELECTED && selected &&
4019               xxx_column_is_visible (sheet, j) && yyy_row_is_visible (sheet, i) &&
4020               (i == sheet->range.row0 || i == sheet->range.rowi ||
4021                j == sheet->range.col0 || j == sheet->range.coli ||
4022                i == new_range.row0 || i == new_range.rowi ||
4023                j == new_range.col0 || j == new_range.coli))
4024             {
4025
4026               mask1 = i == sheet->range.row0 ? 1 : 0;
4027               mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1;
4028               mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1;
4029               mask1 = j == sheet->range.coli ? mask1 + 8 : mask1;
4030
4031               mask2 = i == new_range.row0 ? 1 : 0;
4032               mask2 = i == new_range.rowi ? mask2 + 2 : mask2;
4033               mask2 = j == new_range.col0 ? mask2 + 4 : mask2;
4034               mask2 = j == new_range.coli ? mask2 + 8 : mask2;
4035
4036               if (mask1 != mask2)
4037                 {
4038                   x = COLUMN_LEFT_XPIXEL (sheet, j);
4039                   y = ROW_TOP_YPIXEL (sheet, i);
4040                   width = COLUMN_LEFT_XPIXEL (sheet, j)- x+
4041                     xxx_column_width (sheet, j);
4042                   height = ROW_TOP_YPIXEL (sheet, i)- y + yyy_row_height (sheet, i);
4043
4044                   if (i == sheet->range.row0)
4045                     {
4046                       y = y - 3;
4047                       height = height + 3;
4048                     }
4049                   if (i == sheet->range.rowi) height = height + 3;
4050                   if (j == sheet->range.col0)
4051                     {
4052                       x = x - 3;
4053                       width = width + 3;
4054                     }
4055                   if (j == sheet->range.coli) width = width + 3;
4056
4057                   gdk_draw_drawable (sheet->sheet_window,
4058                                    GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
4059                                    sheet->pixmap,
4060                                    x + 1,
4061                                    y + 1,
4062                                    x + 1,
4063                                    y + 1,
4064                                    width,
4065                                    height);
4066
4067                   if (i != sheet->active_cell.row || j != sheet->active_cell.col)
4068                     {
4069                       x = COLUMN_LEFT_XPIXEL (sheet, j);
4070                       y = ROW_TOP_YPIXEL (sheet, i);
4071                       width = COLUMN_LEFT_XPIXEL (sheet, j)- x+
4072                         xxx_column_width (sheet, j);
4073
4074                       height = ROW_TOP_YPIXEL (sheet, i)- y + yyy_row_height (sheet, i);
4075
4076                       if (i == new_range.row0)
4077                         {
4078                           y = y+2;
4079                           height = height - 2;
4080                         }
4081                       if (i == new_range.rowi) height = height - 3;
4082                       if (j == new_range.col0)
4083                         {
4084                           x = x+2;
4085                           width = width - 2;
4086                         }
4087                       if (j == new_range.coli) width = width - 3;
4088
4089                       gdk_draw_rectangle (sheet->sheet_window,
4090                                           sheet->xor_gc,
4091                                           TRUE,
4092                                           x + 1, y + 1,
4093                                           width, height);
4094                     }
4095                 }
4096             }
4097         }
4098     }
4099
4100   for (i = range->row0; i <= range->rowi; i++)
4101     {
4102       for (j = range->col0; j <= range->coli; j++)
4103         {
4104
4105           state = gtk_sheet_cell_get_state (sheet, i, j);
4106           selected= (i <= new_range.rowi && i >= new_range.row0 &&
4107                      j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
4108
4109           if (state == GTK_STATE_SELECTED && !selected &&
4110               xxx_column_is_visible (sheet, j) && yyy_row_is_visible (sheet, i))
4111             {
4112
4113               x = COLUMN_LEFT_XPIXEL (sheet, j);
4114               y = ROW_TOP_YPIXEL (sheet, i);
4115               width = COLUMN_LEFT_XPIXEL (sheet, j)- x+ xxx_column_width (sheet, j);
4116               height = ROW_TOP_YPIXEL (sheet, i)- y + yyy_row_height (sheet, i);
4117
4118               if (i == sheet->range.row0)
4119                 {
4120                   y = y - 3;
4121                   height = height + 3;
4122                 }
4123               if (i == sheet->range.rowi) height = height + 3;
4124               if (j == sheet->range.col0)
4125                 {
4126                   x = x - 3;
4127                   width = width + 3;
4128                 }
4129               if (j == sheet->range.coli) width = width + 3;
4130
4131               gdk_draw_drawable (sheet->sheet_window,
4132                                GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
4133                                sheet->pixmap,
4134                                x + 1,
4135                                y + 1,
4136                                x + 1,
4137                                y + 1,
4138                                width,
4139                                height);
4140             }
4141         }
4142     }
4143
4144   for (i = range->row0; i <= range->rowi; i++)
4145     {
4146       for (j = range->col0; j <= range->coli; j++)
4147         {
4148
4149           state = gtk_sheet_cell_get_state (sheet, i, j);
4150           selected= (i <= new_range.rowi && i >= new_range.row0 &&
4151                      j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
4152
4153           if (state != GTK_STATE_SELECTED && selected &&
4154               xxx_column_is_visible (sheet, j) && yyy_row_is_visible (sheet, i) &&
4155               (i != sheet->active_cell.row || j != sheet->active_cell.col))
4156             {
4157
4158               x = COLUMN_LEFT_XPIXEL (sheet, j);
4159               y = ROW_TOP_YPIXEL (sheet, i);
4160               width = COLUMN_LEFT_XPIXEL (sheet, j)- x+ xxx_column_width (sheet, j);
4161               height = ROW_TOP_YPIXEL (sheet, i)- y + yyy_row_height (sheet, i);
4162
4163               if (i == new_range.row0)
4164                 {
4165                   y = y+2;
4166                   height = height - 2;
4167                 }
4168               if (i == new_range.rowi) height = height - 3;
4169               if (j == new_range.col0)
4170                 {
4171                   x = x+2;
4172                   width = width - 2;
4173                 }
4174               if (j == new_range.coli) width = width - 3;
4175
4176               gdk_draw_rectangle (sheet->sheet_window,
4177                                   sheet->xor_gc,
4178                                   TRUE,
4179                                   x + 1, y + 1,
4180                                   width, height);
4181
4182             }
4183
4184         }
4185     }
4186
4187   for (i = aux_range.row0; i <= aux_range.rowi; i++)
4188     {
4189       for (j = aux_range.col0; j <= aux_range.coli; j++)
4190         {
4191
4192           if (xxx_column_is_visible (sheet, j) && yyy_row_is_visible (sheet, i))
4193             {
4194
4195               state = gtk_sheet_cell_get_state (sheet, i, j);
4196
4197               mask1 = i == sheet->range.row0 ? 1 : 0;
4198               mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1;
4199               mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1;
4200               mask1 = j == sheet->range.coli ? mask1 + 8 : mask1;
4201
4202               mask2 = i == new_range.row0 ? 1 : 0;
4203               mask2 = i == new_range.rowi ? mask2 + 2 : mask2;
4204               mask2 = j == new_range.col0 ? mask2 + 4 : mask2;
4205               mask2 = j == new_range.coli ? mask2 + 8 : mask2;
4206               if (mask2 != mask1 || (mask2 == mask1 && state != GTK_STATE_SELECTED))
4207                 {
4208                   x = COLUMN_LEFT_XPIXEL (sheet, j);
4209                   y = ROW_TOP_YPIXEL (sheet, i);
4210                   width = xxx_column_width (sheet, j);
4211                   height = yyy_row_height (sheet, i);
4212                   if (mask2 & 1)
4213                     gdk_draw_rectangle (sheet->sheet_window,
4214                                         sheet->xor_gc,
4215                                         TRUE,
4216                                         x + 1, y - 1,
4217                                         width, 3);
4218
4219
4220                   if (mask2 & 2)
4221                     gdk_draw_rectangle (sheet->sheet_window,
4222                                         sheet->xor_gc,
4223                                         TRUE,
4224                                         x + 1, y + height - 1,
4225                                         width, 3);
4226
4227                   if (mask2 & 4)
4228                     gdk_draw_rectangle (sheet->sheet_window,
4229                                         sheet->xor_gc,
4230                                         TRUE,
4231                                         x - 1, y + 1,
4232                                         3, height);
4233
4234
4235                   if (mask2 & 8)
4236                     gdk_draw_rectangle (sheet->sheet_window,
4237                                         sheet->xor_gc,
4238                                         TRUE,
4239                                         x + width - 1, y + 1,
4240                                         3, height);
4241
4242
4243
4244                 }
4245
4246             }
4247
4248         }
4249     }
4250
4251
4252   *range = new_range;
4253   gtk_sheet_draw_corners (sheet, new_range);
4254
4255 }
4256
4257 static void
4258 gtk_sheet_draw_border (GtkSheet *sheet, GtkSheetRange new_range)
4259 {
4260   GtkWidget *widget;
4261   GdkRectangle area;
4262   gint i;
4263   gint x, y, width, height;
4264
4265   widget = GTK_WIDGET (sheet);
4266
4267   x = COLUMN_LEFT_XPIXEL (sheet, new_range.col0);
4268   y = ROW_TOP_YPIXEL (sheet, new_range.row0);
4269   width = COLUMN_LEFT_XPIXEL (sheet, new_range.coli) - x +
4270     xxx_column_width (sheet, new_range.coli);
4271
4272   height = ROW_TOP_YPIXEL (sheet, new_range.rowi) - y +
4273     yyy_row_height (sheet, new_range.rowi);
4274
4275   area.x = COLUMN_LEFT_XPIXEL (sheet, MIN_VISIBLE_COLUMN (sheet));
4276   area.y = ROW_TOP_YPIXEL (sheet, MIN_VISIBLE_ROW (sheet));
4277   area.width = sheet->sheet_window_width;
4278   area.height = sheet->sheet_window_height;
4279
4280   if (x < 0)
4281     {
4282       width = width + x;
4283       x = 0;
4284     }
4285   if (width > area.width) width = area.width + 10;
4286   if (y < 0)
4287     {
4288       height = height + y;
4289       y = 0;
4290     }
4291   if (height > area.height) height = area.height + 10;
4292
4293   gdk_gc_set_clip_rectangle (sheet->xor_gc, &area);
4294
4295   for (i = -1; i <= 1; ++i)
4296     gdk_draw_rectangle (sheet->sheet_window,
4297                         sheet->xor_gc,
4298                         FALSE,
4299                         x + i,
4300                         y + i,
4301                         width - 2 * i,
4302                         height - 2 * i);
4303
4304   gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
4305
4306
4307   gtk_sheet_draw_corners (sheet, new_range);
4308 }
4309
4310 static void
4311 gtk_sheet_draw_corners (GtkSheet *sheet, GtkSheetRange range)
4312 {
4313   gint x, y;
4314   guint width = 1;
4315
4316   if (gtk_sheet_cell_isvisible (sheet, range.row0, range.col0))
4317     {
4318       x = COLUMN_LEFT_XPIXEL (sheet, range.col0);
4319       y = ROW_TOP_YPIXEL (sheet, range.row0);
4320       gdk_draw_drawable (sheet->sheet_window,
4321                        GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
4322                        sheet->pixmap,
4323                        x - 1,
4324                        y - 1,
4325                        x - 1,
4326                        y - 1,
4327                        3,
4328                        3);
4329       gdk_draw_rectangle (sheet->sheet_window,
4330                           sheet->xor_gc,
4331                           TRUE,
4332                           x - 1, y - 1,
4333                           3, 3);
4334     }
4335
4336   if (gtk_sheet_cell_isvisible (sheet, range.row0, range.coli) ||
4337       sheet->state == GTK_SHEET_COLUMN_SELECTED)
4338     {
4339       x = COLUMN_LEFT_XPIXEL (sheet, range.coli)+
4340         xxx_column_width (sheet, range.coli);
4341       y = ROW_TOP_YPIXEL (sheet, range.row0);
4342       width = 1;
4343       if (sheet->state == GTK_SHEET_COLUMN_SELECTED)
4344         {
4345           y = ROW_TOP_YPIXEL (sheet, MIN_VISIBLE_ROW (sheet))+3;
4346           width = 3;
4347         }
4348       gdk_draw_drawable (sheet->sheet_window,
4349                        GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
4350                        sheet->pixmap,
4351                        x - width,
4352                        y - width,
4353                        x - width,
4354                        y - width,
4355                        2 * width + 1,
4356                        2 * width + 1);
4357       gdk_draw_rectangle (sheet->sheet_window,
4358                           sheet->xor_gc,
4359                           TRUE,
4360                           x - width + width / 2, y - width + width / 2,
4361                           2 + width, 2 + width);
4362     }
4363
4364   if (gtk_sheet_cell_isvisible (sheet, range.rowi, range.col0) ||
4365       sheet->state == GTK_SHEET_ROW_SELECTED)
4366     {
4367       x = COLUMN_LEFT_XPIXEL (sheet, range.col0);
4368       y = ROW_TOP_YPIXEL (sheet, range.rowi)+
4369         yyy_row_height (sheet, range.rowi);
4370       width = 1;
4371       if (sheet->state == GTK_SHEET_ROW_SELECTED)
4372         {
4373           x = COLUMN_LEFT_XPIXEL (sheet, MIN_VISIBLE_COLUMN (sheet))+3;
4374           width = 3;
4375         }
4376       gdk_draw_drawable (sheet->sheet_window,
4377                        GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
4378                        sheet->pixmap,
4379                        x - width,
4380                        y - width,
4381                        x - width,
4382                        y - width,
4383                        2 * width + 1,
4384                        2 * width + 1);
4385       gdk_draw_rectangle (sheet->sheet_window,
4386                           sheet->xor_gc,
4387                           TRUE,
4388                           x - width + width / 2, y - width + width / 2,
4389                           2 + width, 2 + width);
4390     }
4391
4392   if (gtk_sheet_cell_isvisible (sheet, range.rowi, range.coli))
4393     {
4394       x = COLUMN_LEFT_XPIXEL (sheet, range.coli)+
4395         xxx_column_width (sheet, range.coli);
4396       y = ROW_TOP_YPIXEL (sheet, range.rowi)+
4397         yyy_row_height (sheet, range.rowi);
4398       width = 1;
4399       if (sheet->state == GTK_SHEET_RANGE_SELECTED) width = 3;
4400       if (sheet->state == GTK_SHEET_NORMAL) width = 3;
4401       gdk_draw_drawable (sheet->sheet_window,
4402                        GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
4403                        sheet->pixmap,
4404                        x - width,
4405                        y - width,
4406                        x - width,
4407                        y - width,
4408                        2 * width + 1,
4409                        2 * width + 1);
4410       gdk_draw_rectangle (sheet->sheet_window,
4411                           sheet->xor_gc,
4412                           TRUE,
4413                           x - width + width / 2, y - width + width / 2,
4414                           2 + width, 2 + width);
4415
4416     }
4417
4418 }
4419
4420
4421 static void
4422 gtk_sheet_real_select_range (GtkSheet * sheet,
4423                              const GtkSheetRange * range)
4424 {
4425   gint state;
4426
4427   g_return_if_fail (sheet != NULL);
4428
4429   if (range == NULL) range = &sheet->range;
4430
4431   memcpy (&sheet->range, range, sizeof (*range));
4432
4433   if (range->row0 < 0 || range->rowi < 0) return;
4434   if (range->col0 < 0 || range->coli < 0) return;
4435
4436   state = sheet->state;
4437
4438   if (range->coli != sheet->range.coli || range->col0 != sheet->range.col0 ||
4439       range->rowi != sheet->range.rowi || range->row0 != sheet->range.row0)
4440     {
4441       gtk_sheet_new_selection (sheet, &sheet->range);
4442     }
4443   else
4444     {
4445       gtk_sheet_draw_backing_pixmap (sheet, sheet->range);
4446       gtk_sheet_range_draw_selection (sheet, sheet->range);
4447     }
4448
4449   gtk_sheet_update_primary_selection (sheet);
4450
4451   g_signal_emit (sheet, sheet_signals[SELECT_RANGE], 0, &sheet->range);
4452 }
4453
4454
4455 void
4456 gtk_sheet_get_selected_range            (GtkSheet *sheet,
4457                                          GtkSheetRange *range)
4458 {
4459   g_return_if_fail (sheet != NULL);
4460   *range = sheet->range;
4461 }
4462
4463
4464 void
4465 gtk_sheet_select_range (GtkSheet * sheet, const GtkSheetRange *range)
4466 {
4467   g_return_if_fail (sheet != NULL);
4468
4469   if (range == NULL) range=&sheet->range;
4470
4471   if (range->row0 < 0 || range->rowi < 0) return;
4472   if (range->col0 < 0 || range->coli < 0) return;
4473
4474
4475   if (sheet->state != GTK_SHEET_NORMAL)
4476     gtk_sheet_real_unselect_range (sheet, NULL);
4477   else
4478     gtk_sheet_deactivate_cell (sheet);
4479
4480   sheet->range.row0 = range->row0;
4481   sheet->range.rowi = range->rowi;
4482   sheet->range.col0 = range->col0;
4483   sheet->range.coli = range->coli;
4484   sheet->active_cell.row = range->row0;
4485   sheet->active_cell.col = range->col0;
4486   sheet->selection_cell.row = range->rowi;
4487   sheet->selection_cell.col = range->coli;
4488
4489   sheet->state = GTK_SHEET_RANGE_SELECTED;
4490   gtk_sheet_real_select_range (sheet, NULL);
4491
4492 }
4493
4494 void
4495 gtk_sheet_unselect_range (GtkSheet * sheet)
4496 {
4497   if (! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4498     return;
4499
4500   gtk_sheet_real_unselect_range (sheet, NULL);
4501   sheet->state = GTK_STATE_NORMAL;
4502
4503   gtk_sheet_activate_cell (sheet,
4504                            sheet->active_cell.row, sheet->active_cell.col);
4505 }
4506
4507
4508 static void
4509 gtk_sheet_real_unselect_range (GtkSheet * sheet,
4510                                const GtkSheetRange *range)
4511 {
4512   g_return_if_fail (sheet != NULL);
4513   g_return_if_fail (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)));
4514
4515   if ( range == NULL)
4516     range = &sheet->range;
4517
4518   if (range->row0 < 0 || range->rowi < 0) return;
4519   if (range->col0 < 0 || range->coli < 0) return;
4520
4521   g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, -1);
4522   g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, -1);
4523
4524   if (gtk_sheet_range_isvisible (sheet, *range))
4525     gtk_sheet_draw_backing_pixmap (sheet, *range);
4526
4527   sheet->range.row0 = -1;
4528   sheet->range.rowi = -1;
4529   sheet->range.col0 = -1;
4530   sheet->range.coli = -1;
4531
4532   gtk_sheet_position_children (sheet);
4533 }
4534
4535
4536 static gint
4537 gtk_sheet_expose (GtkWidget * widget,
4538                   GdkEventExpose * event)
4539 {
4540   GtkSheet *sheet;
4541   GtkSheetRange range;
4542
4543   g_return_val_if_fail (widget != NULL, FALSE);
4544   g_return_val_if_fail (GTK_IS_SHEET (widget), FALSE);
4545   g_return_val_if_fail (event != NULL, FALSE);
4546
4547
4548   sheet = GTK_SHEET (widget);
4549
4550   if (GTK_WIDGET_DRAWABLE (widget))
4551     {
4552       range.row0 = ROW_FROM_YPIXEL (sheet, event->area.y);
4553       range.col0 = COLUMN_FROM_XPIXEL (sheet, event->area.x);
4554       range.rowi = ROW_FROM_YPIXEL (sheet, event->area.y + event->area.height);
4555       range.coli = COLUMN_FROM_XPIXEL (sheet, event->area.x + event->area.width);
4556
4557       /* exposure events on the sheet */
4558       if (event->window == sheet->row_title_window &&
4559           sheet->row_titles_visible)
4560         {
4561           gint i;
4562           for (i = MIN_VISIBLE_ROW (sheet); i <= MAX_VISIBLE_ROW (sheet); i++)
4563             gtk_sheet_row_title_button_draw (sheet, i);
4564         }
4565
4566       if (event->window == sheet->column_title_window &&
4567           sheet->column_titles_visible)
4568         {
4569           gint i;
4570           for (i = MIN_VISIBLE_COLUMN (sheet); i <= MAX_VISIBLE_COLUMN (sheet); i++)
4571             gtk_sheet_column_title_button_draw (sheet, i);
4572         }
4573
4574       if (event->window == sheet->sheet_window)
4575         {
4576           gtk_sheet_draw_backing_pixmap (sheet, range);
4577
4578           if (sheet->state != GTK_SHEET_NORMAL)
4579             {
4580               if (gtk_sheet_range_isvisible (sheet, sheet->range))
4581                 gtk_sheet_draw_backing_pixmap (sheet, sheet->range);
4582               if (GTK_SHEET_IN_RESIZE (sheet) || GTK_SHEET_IN_DRAG (sheet))
4583                 gtk_sheet_draw_backing_pixmap (sheet, sheet->drag_range);
4584
4585               if (gtk_sheet_range_isvisible (sheet, sheet->range))
4586                 gtk_sheet_range_draw_selection (sheet, sheet->range);
4587               if (GTK_SHEET_IN_RESIZE (sheet) || GTK_SHEET_IN_DRAG (sheet))
4588                 draw_xor_rectangle (sheet, sheet->drag_range);
4589             }
4590
4591           if ((!GTK_SHEET_IN_XDRAG (sheet)) && (!GTK_SHEET_IN_YDRAG (sheet)))
4592             {
4593               if (sheet->state == GTK_SHEET_NORMAL)
4594                 gtk_sheet_draw_active_cell (sheet);
4595             }
4596         }
4597     }
4598
4599   if (sheet->state != GTK_SHEET_NORMAL && GTK_SHEET_IN_SELECTION (sheet))
4600     gtk_widget_grab_focus (GTK_WIDGET (sheet));
4601
4602   (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
4603
4604   return FALSE;
4605 }
4606
4607
4608 static gboolean
4609 gtk_sheet_button_press (GtkWidget * widget,
4610                         GdkEventButton * event)
4611 {
4612   GtkSheet *sheet;
4613   GdkModifierType mods;
4614   gint x, y, row, column;
4615   gboolean veto;
4616
4617   g_return_val_if_fail (widget != NULL, FALSE);
4618   g_return_val_if_fail (GTK_IS_SHEET (widget), FALSE);
4619   g_return_val_if_fail (event != NULL, FALSE);
4620
4621   sheet = GTK_SHEET (widget);
4622
4623   /* Cancel any pending tooltips */
4624   if (sheet->motion_timer)
4625     {
4626       g_source_remove (sheet->motion_timer);
4627       sheet->motion_timer = 0;
4628     }
4629
4630   gtk_widget_get_pointer (widget, &x, &y);
4631   gtk_sheet_get_pixel_info (sheet, x, y, &row, &column);
4632
4633
4634   if (event->window == sheet->column_title_window)
4635     {
4636       g_signal_emit (sheet,
4637                      sheet_signals[BUTTON_EVENT_COLUMN], 0,
4638                      column, event);
4639
4640       if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
4641         g_signal_emit (sheet,
4642                        sheet_signals[DOUBLE_CLICK_COLUMN], 0, column);
4643
4644     }
4645   else if (event->window == sheet->row_title_window)
4646     {
4647       g_signal_emit (sheet,
4648                      sheet_signals[BUTTON_EVENT_ROW], 0,
4649                      row, event);
4650
4651       if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
4652         g_signal_emit (sheet,
4653                        sheet_signals[DOUBLE_CLICK_ROW], 0, row);
4654     }
4655
4656
4657   gdk_window_get_pointer (widget->window, NULL, NULL, &mods);
4658
4659   if (! (mods & GDK_BUTTON1_MASK)) return TRUE;
4660
4661
4662   /* press on resize windows */
4663   if (event->window == sheet->column_title_window &&
4664       gtk_sheet_columns_resizable (sheet))
4665     {
4666       gtk_widget_get_pointer (widget, &sheet->x_drag, NULL);
4667       if (POSSIBLE_XDRAG (sheet, sheet->x_drag, &sheet->drag_cell.col))
4668         {
4669           guint req;
4670           if (event->type == GDK_2BUTTON_PRESS)
4671             {
4672               gtk_sheet_autoresize_column (sheet, sheet->drag_cell.col);
4673               GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_XDRAG);
4674               return TRUE;
4675             }
4676           gtk_sheet_column_size_request (sheet, sheet->drag_cell.col, &req);
4677           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_XDRAG);
4678           gdk_pointer_grab (sheet->column_title_window, FALSE,
4679                             GDK_POINTER_MOTION_HINT_MASK |
4680                             GDK_BUTTON1_MOTION_MASK |
4681                             GDK_BUTTON_RELEASE_MASK,
4682                             NULL, NULL, event->time);
4683
4684           draw_xor_vline (sheet);
4685           return TRUE;
4686         }
4687     }
4688
4689   if (event->window == sheet->row_title_window && gtk_sheet_rows_resizable (sheet))
4690     {
4691       gtk_widget_get_pointer (widget, NULL, &sheet->y_drag);
4692
4693       if (POSSIBLE_YDRAG (sheet, sheet->y_drag, &sheet->drag_cell.row))
4694         {
4695           guint req;
4696           gtk_sheet_row_size_request (sheet, sheet->drag_cell.row, &req);
4697           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_YDRAG);
4698           gdk_pointer_grab (sheet->row_title_window, FALSE,
4699                             GDK_POINTER_MOTION_HINT_MASK |
4700                             GDK_BUTTON1_MOTION_MASK |
4701                             GDK_BUTTON_RELEASE_MASK,
4702                             NULL, NULL, event->time);
4703
4704           draw_xor_hline (sheet);
4705           return TRUE;
4706         }
4707     }
4708
4709   /* the sheet itself does not handle other than single click events */
4710   if (event->type != GDK_BUTTON_PRESS) return FALSE;
4711
4712   /* selections on the sheet */
4713   if (event->window == sheet->sheet_window)
4714     {
4715       gtk_widget_get_pointer (widget, &x, &y);
4716       gtk_sheet_get_pixel_info (sheet, x, y, &row, &column);
4717       gdk_pointer_grab (sheet->sheet_window, FALSE,
4718                         GDK_POINTER_MOTION_HINT_MASK |
4719                         GDK_BUTTON1_MOTION_MASK |
4720                         GDK_BUTTON_RELEASE_MASK,
4721                         NULL, NULL, event->time);
4722       gtk_grab_add (GTK_WIDGET (sheet));
4723
4724       /* This seems to be a kludge to work around a problem where the sheet
4725          scrolls to another position.  The timeout scrolls it back to its
4726          original posn.          JMD 3 July 2007
4727       */
4728       gtk_widget_grab_focus (GTK_WIDGET (sheet));
4729
4730       if (sheet->selection_mode != GTK_SELECTION_SINGLE &&
4731           sheet->selection_mode != GTK_SELECTION_NONE &&
4732           sheet->cursor_drag->type == GDK_SIZING &&
4733           !GTK_SHEET_IN_SELECTION (sheet) && !GTK_SHEET_IN_RESIZE (sheet))
4734         {
4735           if (sheet->state == GTK_STATE_NORMAL)
4736             {
4737               row = sheet->active_cell.row;
4738               column = sheet->active_cell.col;
4739               gtk_sheet_deactivate_cell (sheet);
4740               sheet->active_cell.row = row;
4741               sheet->active_cell.col = column;
4742               sheet->drag_range = sheet->range;
4743               sheet->state = GTK_SHEET_RANGE_SELECTED;
4744               gtk_sheet_select_range (sheet, &sheet->drag_range);
4745             }
4746           sheet->x_drag = x;
4747           sheet->y_drag = y;
4748           if (row > sheet->range.rowi) row--;
4749           if (column > sheet->range.coli) column--;
4750           sheet->drag_cell.row = row;
4751           sheet->drag_cell.col = column;
4752           sheet->drag_range = sheet->range;
4753           draw_xor_rectangle (sheet, sheet->drag_range);
4754           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_RESIZE);
4755         }
4756       else if (sheet->cursor_drag->type == GDK_TOP_LEFT_ARROW &&
4757                !GTK_SHEET_IN_SELECTION (sheet)
4758                && ! GTK_SHEET_IN_DRAG (sheet)
4759                && sheet->active_cell.row >= 0
4760                && sheet->active_cell.col >= 0
4761                )
4762         {
4763           if (sheet->state == GTK_STATE_NORMAL)
4764             {
4765               row = sheet->active_cell.row;
4766               column = sheet->active_cell.col;
4767               gtk_sheet_deactivate_cell (sheet);
4768               sheet->active_cell.row = row;
4769               sheet->active_cell.col = column;
4770               sheet->drag_range = sheet->range;
4771               sheet->state = GTK_SHEET_RANGE_SELECTED;
4772               gtk_sheet_select_range (sheet, &sheet->drag_range);
4773             }
4774           sheet->x_drag = x;
4775           sheet->y_drag = y;
4776           if (row < sheet->range.row0) row++;
4777           if (row > sheet->range.rowi) row--;
4778           if (column < sheet->range.col0) column++;
4779           if (column > sheet->range.coli) column--;
4780           sheet->drag_cell.row = row;
4781           sheet->drag_cell.col = column;
4782           sheet->drag_range = sheet->range;
4783           draw_xor_rectangle (sheet, sheet->drag_range);
4784           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_DRAG);
4785         }
4786       else
4787         {
4788           gtk_sheet_click_cell (sheet, row, column, &veto);
4789           if (veto) GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
4790         }
4791     }
4792
4793   if (event->window == sheet->column_title_window)
4794     {
4795       gtk_widget_get_pointer (widget, &x, &y);
4796       column = COLUMN_FROM_XPIXEL (sheet, x);
4797
4798       if (xxx_column_is_sensitive (sheet, column))
4799         {
4800           gtk_sheet_click_cell (sheet, - 1, column, &veto);
4801           gtk_grab_add (GTK_WIDGET (sheet));
4802           gtk_widget_grab_focus (GTK_WIDGET (sheet));
4803           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
4804         }
4805     }
4806
4807   if (event->window == sheet->row_title_window)
4808     {
4809       gtk_widget_get_pointer (widget, &x, &y);
4810       row = ROW_FROM_YPIXEL (sheet, y);
4811       if (yyy_row_is_sensitive (sheet, row))
4812         {
4813           gtk_sheet_click_cell (sheet, row, - 1, &veto);
4814           gtk_grab_add (GTK_WIDGET (sheet));
4815           gtk_widget_grab_focus (GTK_WIDGET (sheet));
4816           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
4817         }
4818     }
4819
4820   return TRUE;
4821 }
4822
4823 static void
4824 gtk_sheet_click_cell (GtkSheet *sheet, gint row, gint column, gboolean *veto)
4825 {
4826   *veto = TRUE;
4827
4828   if (row >= yyy_row_count (sheet) || column >= xxx_column_count (sheet))
4829     {
4830       *veto = FALSE;
4831       return;
4832     }
4833
4834   if (column >= 0 && row >= 0)
4835     if (! xxx_column_is_visible (sheet, column) || !yyy_row_is_visible (sheet, row))
4836       {
4837         *veto = FALSE;
4838         return;
4839       }
4840
4841   _gtkextra_signal_emit (GTK_OBJECT (sheet), sheet_signals[TRAVERSE],
4842                          sheet->active_cell.row, sheet->active_cell.col,
4843                          &row, &column, veto);
4844
4845   if (!*veto)
4846     {
4847       if (sheet->state == GTK_STATE_NORMAL) return;
4848
4849       row = sheet->active_cell.row;
4850       column = sheet->active_cell.col;
4851
4852       gtk_sheet_activate_cell (sheet, row, column);
4853       return;
4854     }
4855
4856   if (row == -1 && column >= 0)
4857     {
4858       if (gtk_sheet_autoscroll (sheet))
4859         gtk_sheet_move_query (sheet, row, column);
4860       gtk_sheet_select_column (sheet, column);
4861       return;
4862     }
4863   if (column == -1 && row >= 0)
4864     {
4865       if (gtk_sheet_autoscroll (sheet))
4866         gtk_sheet_move_query (sheet, row, column);
4867       gtk_sheet_select_row (sheet, row);
4868       return;
4869     }
4870
4871   if (row == - 1 && column == - 1)
4872     {
4873       sheet->range.row0 = 0;
4874       sheet->range.col0 = 0;
4875       sheet->range.rowi = yyy_row_count (sheet) - 1;
4876       sheet->range.coli = xxx_column_count (sheet) - 1;
4877       sheet->active_cell.row = 0;
4878       sheet->active_cell.col = 0;
4879       gtk_sheet_select_range (sheet, NULL);
4880       return;
4881     }
4882
4883   if (row != -1 && column != -1)
4884     {
4885       if (sheet->state != GTK_SHEET_NORMAL)
4886         {
4887           sheet->state = GTK_SHEET_NORMAL;
4888           gtk_sheet_real_unselect_range (sheet, NULL);
4889         }
4890       else
4891         {
4892           gtk_sheet_deactivate_cell (sheet);
4893           gtk_sheet_activate_cell (sheet, row, column);
4894         }
4895
4896       if (gtk_sheet_autoscroll (sheet))
4897         gtk_sheet_move_query (sheet, row, column);
4898       sheet->active_cell.row = row;
4899       sheet->active_cell.col = column;
4900       sheet->selection_cell.row = row;
4901       sheet->selection_cell.col = column;
4902       sheet->range.row0 = row;
4903       sheet->range.col0 = column;
4904       sheet->range.rowi = row;
4905       sheet->range.coli = column;
4906       sheet->state = GTK_SHEET_NORMAL;
4907       GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
4908       gtk_sheet_draw_active_cell (sheet);
4909       return;
4910     }
4911
4912   g_assert_not_reached ();
4913   gtk_sheet_activate_cell (sheet, sheet->active_cell.row,
4914                            sheet->active_cell.col);
4915 }
4916
4917 static gint
4918 gtk_sheet_button_release (GtkWidget * widget,
4919                           GdkEventButton * event)
4920 {
4921   GtkSheet *sheet;
4922   gint x, y;
4923
4924   sheet = GTK_SHEET (widget);
4925
4926   /* release on resize windows */
4927   if (GTK_SHEET_IN_XDRAG (sheet))
4928     {
4929       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_XDRAG);
4930       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
4931       gtk_widget_get_pointer (widget, &x, NULL);
4932       gdk_pointer_ungrab (event->time);
4933       draw_xor_vline (sheet);
4934
4935       gtk_sheet_set_column_width (sheet, sheet->drag_cell.col,
4936                                   new_column_width (sheet, sheet->drag_cell.col, &x));
4937       sheet->old_hadjustment = -1.;
4938       g_signal_emit_by_name (sheet->hadjustment, "value_changed");
4939       return TRUE;
4940     }
4941
4942   if (GTK_SHEET_IN_YDRAG (sheet))
4943     {
4944       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_YDRAG);
4945       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
4946       gtk_widget_get_pointer (widget, NULL, &y);
4947       gdk_pointer_ungrab (event->time);
4948       draw_xor_hline (sheet);
4949
4950       gtk_sheet_set_row_height (sheet, sheet->drag_cell.row, new_row_height (sheet, sheet->drag_cell.row, &y));
4951       sheet->old_vadjustment = -1.;
4952       g_signal_emit_by_name (sheet->vadjustment, "value_changed");
4953       return TRUE;
4954     }
4955
4956
4957   if (GTK_SHEET_IN_DRAG (sheet))
4958     {
4959       GtkSheetRange old_range;
4960       draw_xor_rectangle (sheet, sheet->drag_range);
4961       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_DRAG);
4962       gdk_pointer_ungrab (event->time);
4963
4964       gtk_sheet_real_unselect_range (sheet, NULL);
4965
4966       sheet->active_cell.row = sheet->active_cell.row +
4967         (sheet->drag_range.row0 - sheet->range.row0);
4968       sheet->active_cell.col = sheet->active_cell.col +
4969         (sheet->drag_range.col0 - sheet->range.col0);
4970       sheet->selection_cell.row = sheet->selection_cell.row +
4971         (sheet->drag_range.row0 - sheet->range.row0);
4972       sheet->selection_cell.col = sheet->selection_cell.col +
4973         (sheet->drag_range.col0 - sheet->range.col0);
4974       old_range = sheet->range;
4975       sheet->range = sheet->drag_range;
4976       sheet->drag_range = old_range;
4977       g_signal_emit (sheet, sheet_signals[MOVE_RANGE], 0,
4978                      &sheet->drag_range, &sheet->range);
4979       gtk_sheet_select_range (sheet, &sheet->range);
4980     }
4981
4982   if (GTK_SHEET_IN_RESIZE (sheet))
4983     {
4984       GtkSheetRange old_range;
4985       draw_xor_rectangle (sheet, sheet->drag_range);
4986       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_RESIZE);
4987       gdk_pointer_ungrab (event->time);
4988
4989       gtk_sheet_real_unselect_range (sheet, NULL);
4990
4991       sheet->active_cell.row = sheet->active_cell.row +
4992         (sheet->drag_range.row0 - sheet->range.row0);
4993       sheet->active_cell.col = sheet->active_cell.col +
4994         (sheet->drag_range.col0 - sheet->range.col0);
4995       if (sheet->drag_range.row0 < sheet->range.row0)
4996         sheet->selection_cell.row = sheet->drag_range.row0;
4997       if (sheet->drag_range.rowi >= sheet->range.rowi)
4998         sheet->selection_cell.row = sheet->drag_range.rowi;
4999       if (sheet->drag_range.col0 < sheet->range.col0)
5000         sheet->selection_cell.col = sheet->drag_range.col0;
5001       if (sheet->drag_range.coli >= sheet->range.coli)
5002         sheet->selection_cell.col = sheet->drag_range.coli;
5003       old_range = sheet->range;
5004       sheet->range = sheet->drag_range;
5005       sheet->drag_range = old_range;
5006
5007       if (sheet->state == GTK_STATE_NORMAL) sheet->state = GTK_SHEET_RANGE_SELECTED;
5008       g_signal_emit (sheet, sheet_signals[RESIZE_RANGE], 0,
5009                      &sheet->drag_range, &sheet->range);
5010       gtk_sheet_select_range (sheet, &sheet->range);
5011     }
5012
5013   if (sheet->state == GTK_SHEET_NORMAL && GTK_SHEET_IN_SELECTION (sheet))
5014     {
5015       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
5016       gdk_pointer_ungrab (event->time);
5017       gtk_sheet_activate_cell (sheet, sheet->active_cell.row,
5018                                sheet->active_cell.col);
5019     }
5020
5021   if (GTK_SHEET_IN_SELECTION)
5022     gdk_pointer_ungrab (event->time);
5023   gtk_grab_remove (GTK_WIDGET (sheet));
5024
5025   GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
5026
5027   return TRUE;
5028 }
5029
5030 /* Shamelessly lifted from gtktooltips */
5031 static gboolean
5032 gtk_sheet_subtitle_paint_window (GtkWidget *tip_window)
5033 {
5034   GtkRequisition req;
5035
5036   gtk_widget_size_request (tip_window, &req);
5037   gtk_paint_flat_box (tip_window->style, tip_window->window,
5038                       GTK_STATE_NORMAL, GTK_SHADOW_OUT,
5039                       NULL, GTK_WIDGET(tip_window), "tooltip",
5040                       0, 0, req.width, req.height);
5041
5042   return FALSE;
5043 }
5044
5045 static GtkSheetHoverTitle *
5046 create_hover_window (void)
5047 {
5048   GtkSheetHoverTitle *hw = malloc (sizeof (*hw));
5049
5050   hw->window = gtk_window_new (GTK_WINDOW_POPUP);
5051
5052 #if GTK_CHECK_VERSION (2, 9, 0)
5053   gtk_window_set_type_hint (GTK_WINDOW (hw->window),
5054                             GDK_WINDOW_TYPE_HINT_TOOLTIP);
5055 #endif
5056
5057   gtk_widget_set_app_paintable (hw->window, TRUE);
5058   gtk_window_set_resizable (GTK_WINDOW (hw->window), FALSE);
5059   gtk_widget_set_name (hw->window, "gtk-tooltips");
5060   gtk_container_set_border_width (GTK_CONTAINER (hw->window), 4);
5061
5062   g_signal_connect (hw->window,
5063                     "expose_event",
5064                     G_CALLBACK (gtk_sheet_subtitle_paint_window),
5065                     NULL);
5066
5067   hw->label = gtk_label_new (NULL);
5068
5069
5070   gtk_label_set_line_wrap (GTK_LABEL (hw->label), TRUE);
5071   gtk_misc_set_alignment (GTK_MISC (hw->label), 0.5, 0.5);
5072
5073   gtk_container_add (GTK_CONTAINER (hw->window), hw->label);
5074
5075   gtk_widget_show (hw->label);
5076
5077   g_signal_connect (hw->window,
5078                     "destroy",
5079                     G_CALLBACK (gtk_widget_destroyed),
5080                     &hw->window);
5081
5082   return hw;
5083 }
5084
5085 #define HOVER_WINDOW_Y_OFFSET 2
5086
5087 static void
5088 show_subtitle (GtkSheet *sheet, gint row, gint column, const gchar *subtitle)
5089 {
5090   gint x, y;
5091   gint px, py;
5092   gint width;
5093
5094   if ( ! subtitle )
5095     return;
5096
5097   if ( ! sheet->hover_window)
5098     {
5099       sheet->hover_window = create_hover_window ();
5100       gtk_widget_add_events (GTK_WIDGET (sheet), GDK_LEAVE_NOTIFY_MASK);
5101
5102       g_signal_connect_swapped (sheet, "leave-notify-event",
5103                                 G_CALLBACK (gtk_widget_hide),
5104                                 sheet->hover_window->window);
5105     }
5106
5107   gtk_label_set_text (GTK_LABEL (sheet->hover_window->label),
5108                       subtitle);
5109
5110
5111   sheet->hover_window->row = row;
5112   sheet->hover_window->column = column;
5113
5114   gdk_window_get_origin (GTK_WIDGET (sheet)->window, &x, &y);
5115
5116   gtk_widget_get_pointer (GTK_WIDGET (sheet), &px, &py);
5117
5118   gtk_widget_show (sheet->hover_window->window);
5119
5120   width = GTK_WIDGET (sheet->hover_window->label)->allocation.width;
5121
5122   if (row == -1 )
5123     {
5124       x += px;
5125       x -= width / 2;
5126       y += sheet->column_title_area.y;
5127       y += sheet->column_title_area.height;
5128       y += HOVER_WINDOW_Y_OFFSET;
5129     }
5130
5131   if ( column == -1 )
5132     {
5133       y += py;
5134       x += sheet->row_title_area.x;
5135       x += sheet->row_title_area.width * 2 / 3.0;
5136     }
5137
5138   gtk_window_move (GTK_WINDOW (sheet->hover_window->window),
5139                    x, y);
5140 }
5141
5142 static gboolean
5143 motion_timeout_callback (gpointer data)
5144 {
5145   GtkSheet *sheet = GTK_SHEET (data);
5146   gint x, y;
5147   gint row, column;
5148   gtk_widget_get_pointer (GTK_WIDGET (sheet), &x, &y);
5149
5150   if ( gtk_sheet_get_pixel_info (sheet, x, y, &row, &column) )
5151     {
5152       if ( column == -1 && row == -1 )
5153         return FALSE;
5154
5155       if ( column == -1)
5156         {
5157           GSheetRow *row_geo = sheet->row_geometry;
5158           gchar *text;
5159
5160           text = g_sheet_row_get_subtitle (row_geo, row);
5161
5162           show_subtitle (sheet, row, column, text);
5163           g_free (text);
5164         }
5165
5166       if ( row == -1)
5167         {
5168           GSheetColumn *col_geo = sheet->column_geometry;
5169           gchar *text;
5170
5171           text = g_sheet_column_get_subtitle (col_geo, column);
5172
5173           show_subtitle (sheet, row, column, text );
5174
5175           g_free (text);
5176         }
5177     }
5178
5179   return FALSE;
5180 }
5181
5182 static gint
5183 gtk_sheet_motion (GtkWidget * widget,
5184                   GdkEventMotion * event)
5185 {
5186   GtkSheet *sheet;
5187   GdkModifierType mods;
5188   GdkCursorType new_cursor;
5189   gint x, y;
5190   gint row, column;
5191
5192   g_return_val_if_fail (widget != NULL, FALSE);
5193   g_return_val_if_fail (GTK_IS_SHEET (widget), FALSE);
5194   g_return_val_if_fail (event != NULL, FALSE);
5195
5196   sheet = GTK_SHEET (widget);
5197
5198   /* selections on the sheet */
5199   x = event->x;
5200   y = event->y;
5201
5202   if (!sheet->hover_window || ! GTK_WIDGET_VISIBLE (sheet->hover_window->window))
5203     {
5204       if ( sheet->motion_timer > 0 )
5205         g_source_remove (sheet->motion_timer);
5206       sheet->motion_timer = g_timeout_add (TIMEOUT_HOVER, motion_timeout_callback, sheet);
5207     }
5208   else
5209     {
5210       gint row, column;
5211       gint wx, wy;
5212       gtk_widget_get_pointer (widget, &wx, &wy);
5213
5214       if ( gtk_sheet_get_pixel_info (sheet, wx, wy, &row, &column) )
5215         {
5216           if ( row != sheet->hover_window->row || column != sheet->hover_window->column)
5217             {
5218               gtk_widget_hide (sheet->hover_window->window);
5219             }
5220         }
5221     }
5222
5223   if (event->window == sheet->column_title_window &&
5224       gtk_sheet_columns_resizable (sheet))
5225     {
5226       gtk_widget_get_pointer (widget, &x, &y);
5227       if (!GTK_SHEET_IN_SELECTION (sheet) &&
5228           POSSIBLE_XDRAG (sheet, x, &column))
5229         {
5230           new_cursor = GDK_SB_H_DOUBLE_ARROW;
5231           if (new_cursor != sheet->cursor_drag->type)
5232             {
5233               gdk_cursor_unref (sheet->cursor_drag);
5234               sheet->cursor_drag = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW);
5235               gdk_window_set_cursor (sheet->column_title_window,
5236                                      sheet->cursor_drag);
5237             }
5238         }
5239       else
5240         {
5241           new_cursor = GDK_TOP_LEFT_ARROW;
5242           if (!GTK_SHEET_IN_XDRAG (sheet) &&
5243               new_cursor != sheet->cursor_drag->type)
5244             {
5245               gdk_cursor_unref (sheet->cursor_drag);
5246               sheet->cursor_drag = gdk_cursor_new (GDK_TOP_LEFT_ARROW);
5247               gdk_window_set_cursor (sheet->column_title_window,
5248                                      sheet->cursor_drag);
5249             }
5250         }
5251     }
5252
5253   if (event->window == sheet->row_title_window &&
5254       gtk_sheet_rows_resizable (sheet))
5255     {
5256       gtk_widget_get_pointer (widget, &x, &y);
5257       if (!GTK_SHEET_IN_SELECTION (sheet) && POSSIBLE_YDRAG (sheet, y, &column))
5258         {
5259           new_cursor = GDK_SB_V_DOUBLE_ARROW;
5260           if (new_cursor != sheet->cursor_drag->type)
5261             {
5262               gdk_cursor_unref (sheet->cursor_drag);
5263               sheet->cursor_drag = gdk_cursor_new (GDK_SB_V_DOUBLE_ARROW);
5264               gdk_window_set_cursor (sheet->row_title_window, sheet->cursor_drag);
5265             }
5266         }
5267       else
5268         {
5269           new_cursor = GDK_TOP_LEFT_ARROW;
5270           if (!GTK_SHEET_IN_YDRAG (sheet) &&
5271               new_cursor != sheet->cursor_drag->type)
5272             {
5273               gdk_cursor_unref (sheet->cursor_drag);
5274               sheet->cursor_drag = gdk_cursor_new (GDK_TOP_LEFT_ARROW);
5275               gdk_window_set_cursor (sheet->row_title_window, sheet->cursor_drag);
5276             }
5277         }
5278     }
5279
5280   new_cursor = GDK_PLUS;
5281   if ( event->window == sheet->sheet_window &&
5282        !POSSIBLE_DRAG (sheet, x, y, &row, &column) &&
5283        !GTK_SHEET_IN_DRAG (sheet) &&
5284        !POSSIBLE_RESIZE (sheet, x, y, &row, &column) &&
5285        !GTK_SHEET_IN_RESIZE (sheet) &&
5286        new_cursor != sheet->cursor_drag->type)
5287     {
5288       gdk_cursor_unref (sheet->cursor_drag);
5289       sheet->cursor_drag = gdk_cursor_new (GDK_PLUS);
5290       gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
5291     }
5292
5293   new_cursor = GDK_TOP_LEFT_ARROW;
5294   if ( event->window == sheet->sheet_window &&
5295        ! (POSSIBLE_RESIZE (sheet, x, y, &row, &column) || GTK_SHEET_IN_RESIZE (sheet)) && (POSSIBLE_DRAG (sheet, x, y, &row, &column) || GTK_SHEET_IN_DRAG (sheet)) &&
5296
5297        new_cursor != sheet->cursor_drag->type)
5298     {
5299       gdk_cursor_unref (sheet->cursor_drag);
5300       sheet->cursor_drag = gdk_cursor_new (GDK_TOP_LEFT_ARROW);
5301       gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
5302     }
5303
5304   new_cursor = GDK_SIZING;
5305   if ( event->window == sheet->sheet_window &&
5306        sheet->selection_mode != GTK_SELECTION_NONE &&
5307        !GTK_SHEET_IN_DRAG (sheet) &&
5308        (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
5309         GTK_SHEET_IN_RESIZE (sheet)) &&
5310        new_cursor != sheet->cursor_drag->type)
5311     {
5312       gdk_cursor_unref (sheet->cursor_drag);
5313       sheet->cursor_drag = gdk_cursor_new (GDK_SIZING);
5314       gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
5315     }
5316
5317
5318   gdk_window_get_pointer (widget->window, &x, &y, &mods);
5319   if (! (mods & GDK_BUTTON1_MASK)) return FALSE;
5320
5321   if (GTK_SHEET_IN_XDRAG (sheet))
5322     {
5323       if (event->is_hint || event->window != widget->window)
5324         gtk_widget_get_pointer (widget, &x, NULL);
5325       else
5326         x = event->x;
5327
5328       new_column_width (sheet, sheet->drag_cell.col, &x);
5329       if (x != sheet->x_drag)
5330         {
5331           draw_xor_vline (sheet);
5332           sheet->x_drag = x;
5333           draw_xor_vline (sheet);
5334         }
5335       return TRUE;
5336     }
5337
5338   if (GTK_SHEET_IN_YDRAG (sheet))
5339     {
5340       if (event->is_hint || event->window != widget->window)
5341         gtk_widget_get_pointer (widget, NULL, &y);
5342       else
5343         y = event->y;
5344
5345       new_row_height (sheet, sheet->drag_cell.row, &y);
5346       if (y != sheet->y_drag)
5347         {
5348           draw_xor_hline (sheet);
5349           sheet->y_drag = y;
5350           draw_xor_hline (sheet);
5351         }
5352       return TRUE;
5353     }
5354
5355   if (GTK_SHEET_IN_DRAG (sheet))
5356     {
5357       GtkSheetRange aux;
5358       column = COLUMN_FROM_XPIXEL (sheet, x)- sheet->drag_cell.col;
5359       row = ROW_FROM_YPIXEL (sheet, y)- sheet->drag_cell.row;
5360       if (sheet->state == GTK_SHEET_COLUMN_SELECTED) row = 0;
5361       if (sheet->state == GTK_SHEET_ROW_SELECTED) column = 0;
5362       sheet->x_drag = x;
5363       sheet->y_drag = y;
5364       aux = sheet->range;
5365       if (aux.row0 + row >= 0 && aux.rowi + row < yyy_row_count (sheet) &&
5366           aux.col0 + column >= 0 && aux.coli + column < xxx_column_count (sheet))
5367         {
5368           aux = sheet->drag_range;
5369           sheet->drag_range.row0 = sheet->range.row0 + row;
5370           sheet->drag_range.col0 = sheet->range.col0 + column;
5371           sheet->drag_range.rowi = sheet->range.rowi + row;
5372           sheet->drag_range.coli = sheet->range.coli + column;
5373           if (aux.row0 != sheet->drag_range.row0 ||
5374               aux.col0 != sheet->drag_range.col0)
5375             {
5376               draw_xor_rectangle (sheet, aux);
5377               draw_xor_rectangle (sheet, sheet->drag_range);
5378             }
5379         }
5380       return TRUE;
5381     }
5382
5383   if (GTK_SHEET_IN_RESIZE (sheet))
5384     {
5385       GtkSheetRange aux;
5386       gint v_h, current_col, current_row, col_threshold, row_threshold;
5387       v_h = 1;
5388
5389       if (abs (x - COLUMN_LEFT_XPIXEL (sheet, sheet->drag_cell.col)) >
5390           abs (y - ROW_TOP_YPIXEL (sheet, sheet->drag_cell.row))) v_h = 2;
5391
5392       current_col = COLUMN_FROM_XPIXEL (sheet, x);
5393       current_row = ROW_FROM_YPIXEL (sheet, y);
5394       column = current_col - sheet->drag_cell.col;
5395       row = current_row - sheet->drag_cell.row;
5396
5397       /*use half of column width resp. row height as threshold to
5398         expand selection*/
5399       col_threshold = COLUMN_LEFT_XPIXEL (sheet, current_col) +
5400         xxx_column_width (sheet, current_col) / 2;
5401       if (column > 0)
5402         {
5403           if (x < col_threshold)
5404             column -= 1;
5405         }
5406       else if (column < 0)
5407         {
5408           if (x > col_threshold)
5409             column +=1;
5410         }
5411       row_threshold = ROW_TOP_YPIXEL (sheet, current_row) +
5412         yyy_row_height (sheet, current_row)/2;
5413       if (row > 0)
5414         {
5415           if (y < row_threshold)
5416             row -= 1;
5417         }
5418       else if (row < 0)
5419         {
5420           if (y > row_threshold)
5421             row +=1;
5422         }
5423
5424       if (sheet->state == GTK_SHEET_COLUMN_SELECTED) row = 0;
5425       if (sheet->state == GTK_SHEET_ROW_SELECTED) column = 0;
5426       sheet->x_drag = x;
5427       sheet->y_drag = y;
5428       aux = sheet->range;
5429
5430       if (v_h == 1)
5431         column = 0;
5432       else
5433         row = 0;
5434
5435       if (aux.row0 + row >= 0 && aux.rowi + row < yyy_row_count (sheet) &&
5436           aux.col0 + column >= 0 && aux.coli + column < xxx_column_count (sheet))
5437         {
5438           aux = sheet->drag_range;
5439           sheet->drag_range = sheet->range;
5440
5441           if (row < 0) sheet->drag_range.row0 = sheet->range.row0 + row;
5442           if (row > 0) sheet->drag_range.rowi = sheet->range.rowi + row;
5443           if (column < 0) sheet->drag_range.col0 = sheet->range.col0 + column;
5444           if (column > 0) sheet->drag_range.coli = sheet->range.coli + column;
5445
5446           if (aux.row0 != sheet->drag_range.row0 ||
5447               aux.rowi != sheet->drag_range.rowi ||
5448               aux.col0 != sheet->drag_range.col0 ||
5449               aux.coli != sheet->drag_range.coli)
5450             {
5451               draw_xor_rectangle (sheet, aux);
5452               draw_xor_rectangle (sheet, sheet->drag_range);
5453             }
5454         }
5455       return TRUE;
5456     }
5457
5458   gtk_sheet_get_pixel_info (sheet, x, y, &row, &column);
5459
5460   if (sheet->state == GTK_SHEET_NORMAL && row == sheet->active_cell.row &&
5461       column == sheet->active_cell.col) return TRUE;
5462
5463   if (GTK_SHEET_IN_SELECTION (sheet) && mods&GDK_BUTTON1_MASK)
5464     gtk_sheet_extend_selection (sheet, row, column);
5465
5466   return TRUE;
5467 }
5468
5469 static gboolean
5470 gtk_sheet_move_query (GtkSheet *sheet, gint row, gint column)
5471 {
5472   gint row_move, column_move;
5473   gfloat row_align, col_align;
5474   guint height, width;
5475   gint new_row = row;
5476   gint new_col = column;
5477
5478   row_move = FALSE;
5479   column_move = FALSE;
5480   row_align = -1.;
5481   col_align = -1.;
5482
5483   height = sheet->sheet_window_height;
5484   width = sheet->sheet_window_width;
5485
5486   if (row >= MAX_VISIBLE_ROW (sheet) && sheet->state != GTK_SHEET_COLUMN_SELECTED)
5487     {
5488       row_align = 1.;
5489       new_row = MIN (yyy_row_count (sheet) - 1, row + 1);
5490       row_move = TRUE;
5491       if (MAX_VISIBLE_ROW (sheet) == yyy_row_count (sheet) - 1 &&
5492           ROW_TOP_YPIXEL (sheet, yyy_row_count (sheet)- 1) +
5493           yyy_row_height (sheet, yyy_row_count (sheet)- 1) < height)
5494         {
5495           row_move = FALSE;
5496           row_align = -1.;
5497         }
5498     }
5499   if (row < MIN_VISIBLE_ROW (sheet) && sheet->state != GTK_SHEET_COLUMN_SELECTED)
5500     {
5501       row_align= 0.;
5502       row_move = TRUE;
5503     }
5504   if (column >= MAX_VISIBLE_COLUMN (sheet) && sheet->state != GTK_SHEET_ROW_SELECTED)
5505     {
5506       col_align = 1.;
5507       new_col = MIN (xxx_column_count (sheet) - 1, column + 1);
5508       column_move = TRUE;
5509       if (MAX_VISIBLE_COLUMN (sheet) == (xxx_column_count (sheet) - 1) &&
5510           COLUMN_LEFT_XPIXEL (sheet, xxx_column_count (sheet) - 1) +
5511           xxx_column_width (sheet, xxx_column_count (sheet) - 1) < width)
5512         {
5513           column_move = FALSE;
5514           col_align = -1.;
5515         }
5516     }
5517   if (column < MIN_VISIBLE_COLUMN (sheet) && sheet->state != GTK_SHEET_ROW_SELECTED)
5518     {
5519       col_align = 0.;
5520       column_move = TRUE;
5521     }
5522
5523   if (row_move || column_move)
5524     {
5525       gtk_sheet_moveto (sheet, new_row, new_col, row_align, col_align);
5526     }
5527
5528   return (row_move || column_move);
5529 }
5530
5531 static void
5532 gtk_sheet_extend_selection (GtkSheet *sheet, gint row, gint column)
5533 {
5534   GtkSheetRange range;
5535   gint state;
5536   gint r, c;
5537
5538   if (row == sheet->selection_cell.row && column == sheet->selection_cell.col)
5539     return;
5540
5541   if (sheet->selection_mode == GTK_SELECTION_SINGLE) return;
5542
5543   gtk_sheet_move_query (sheet, row, column);
5544   gtk_widget_grab_focus (GTK_WIDGET (sheet));
5545
5546   if (GTK_SHEET_IN_DRAG (sheet)) return;
5547
5548   state = sheet->state;
5549
5550   switch (sheet->state)
5551     {
5552     case GTK_SHEET_ROW_SELECTED:
5553       column = xxx_column_count (sheet) - 1;
5554       break;
5555     case GTK_SHEET_COLUMN_SELECTED:
5556       row = yyy_row_count (sheet) - 1;
5557       break;
5558     case GTK_SHEET_NORMAL:
5559       sheet->state = GTK_SHEET_RANGE_SELECTED;
5560       r = sheet->active_cell.row;
5561       c = sheet->active_cell.col;
5562       sheet->range.col0 = c;
5563       sheet->range.row0 = r;
5564       sheet->range.coli = c;
5565       sheet->range.rowi = r;
5566       gdk_draw_drawable (sheet->sheet_window,
5567                        GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
5568                        sheet->pixmap,
5569                        COLUMN_LEFT_XPIXEL (sheet, c)- 1,
5570                        ROW_TOP_YPIXEL (sheet, r)- 1,
5571                        COLUMN_LEFT_XPIXEL (sheet, c)- 1,
5572                        ROW_TOP_YPIXEL (sheet, r)- 1,
5573                        xxx_column_width (sheet, c)+4,
5574                        yyy_row_height (sheet, r)+4);
5575       gtk_sheet_range_draw_selection (sheet, sheet->range);
5576     case GTK_SHEET_RANGE_SELECTED:
5577       sheet->state = GTK_SHEET_RANGE_SELECTED;
5578     }
5579
5580   sheet->selection_cell.row = row;
5581   sheet->selection_cell.col = column;
5582
5583   range.col0 = MIN (column, sheet->active_cell.col);
5584   range.coli = MAX (column, sheet->active_cell.col);
5585   range.row0 = MIN (row, sheet->active_cell.row);
5586   range.rowi = MAX (row, sheet->active_cell.row);
5587
5588   if (range.row0 != sheet->range.row0 || range.rowi != sheet->range.rowi ||
5589       range.col0 != sheet->range.col0 || range.coli != sheet->range.coli ||
5590       state == GTK_SHEET_NORMAL)
5591     gtk_sheet_real_select_range (sheet, &range);
5592
5593 }
5594
5595 static gint
5596 gtk_sheet_entry_key_press (GtkWidget *widget,
5597                            GdkEventKey *key)
5598 {
5599   gboolean focus;
5600   g_signal_emit_by_name (widget, "key_press_event", key, &focus);
5601   return focus;
5602 }
5603
5604 static gint
5605 gtk_sheet_key_press (GtkWidget *widget,
5606                      GdkEventKey *key)
5607 {
5608   GtkSheet *sheet;
5609   gint row, col;
5610   gint state;
5611   gboolean extend_selection = FALSE;
5612   gboolean force_move = FALSE;
5613   gboolean in_selection = FALSE;
5614   gboolean veto = TRUE;
5615   gint scroll = 1;
5616
5617   sheet = GTK_SHEET (widget);
5618
5619   if (key->state & GDK_CONTROL_MASK || key->keyval == GDK_Control_L ||
5620       key->keyval == GDK_Control_R) return FALSE;
5621
5622   extend_selection = (key->state & GDK_SHIFT_MASK) || key->keyval == GDK_Shift_L
5623     || key->keyval == GDK_Shift_R;
5624
5625   state = sheet->state;
5626   in_selection = GTK_SHEET_IN_SELECTION (sheet);
5627   GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
5628
5629   switch (key->keyval)
5630     {
5631     case GDK_Return: case GDK_KP_Enter:
5632       if (sheet->state == GTK_SHEET_NORMAL &&
5633           !GTK_SHEET_IN_SELECTION (sheet))
5634         g_signal_stop_emission_by_name (gtk_sheet_get_entry (sheet),
5635                                         "key-press-event");
5636       row = sheet->active_cell.row;
5637       col = sheet->active_cell.col;
5638       if (sheet->state == GTK_SHEET_COLUMN_SELECTED)
5639         row = MIN_VISIBLE_ROW (sheet)- 1;
5640       if (sheet->state == GTK_SHEET_ROW_SELECTED)
5641         col = MIN_VISIBLE_COLUMN (sheet);
5642       if (row < yyy_row_count (sheet) - 1)
5643         {
5644           row = row + scroll;
5645           while (!yyy_row_is_visible (sheet, row) && row < yyy_row_count (sheet)- 1)
5646             row++;
5647         }
5648       gtk_sheet_click_cell (sheet, row, col, &veto);
5649       extend_selection = FALSE;
5650       break;
5651     case GDK_ISO_Left_Tab:
5652       row = sheet->active_cell.row;
5653       col = sheet->active_cell.col;
5654       if (sheet->state == GTK_SHEET_ROW_SELECTED)
5655         col = MIN_VISIBLE_COLUMN (sheet)- 1;
5656       if (sheet->state == GTK_SHEET_COLUMN_SELECTED)
5657         row = MIN_VISIBLE_ROW (sheet);
5658       if (col > 0)
5659         {
5660           col = col - scroll;
5661           while (! xxx_column_is_visible (sheet, col) && col > 0) col--;
5662           col = MAX (0, col);
5663         }
5664       gtk_sheet_click_cell (sheet, row, col, &veto);
5665       extend_selection = FALSE;
5666       break;
5667     case GDK_Tab:
5668       row = sheet->active_cell.row;
5669       col = sheet->active_cell.col;
5670       if (sheet->state == GTK_SHEET_ROW_SELECTED)
5671         col = MIN_VISIBLE_COLUMN (sheet)- 1;
5672       if (sheet->state == GTK_SHEET_COLUMN_SELECTED)
5673         row = MIN_VISIBLE_ROW (sheet);
5674       if (col < xxx_column_count (sheet) - 1)
5675         {
5676           col = col + scroll;
5677           while (! xxx_column_is_visible (sheet, col) &&
5678                  col < xxx_column_count (sheet) - 1)
5679             col++;
5680         }
5681       gtk_sheet_click_cell (sheet, row, col, &veto);
5682       extend_selection = FALSE;
5683       break;
5684     case GDK_Page_Up:
5685       scroll = MAX_VISIBLE_ROW (sheet)- MIN_VISIBLE_ROW (sheet)+1;
5686     case GDK_Up:
5687       if (extend_selection)
5688         {
5689           if (state == GTK_STATE_NORMAL)
5690             {
5691               row = sheet->active_cell.row;
5692               col = sheet->active_cell.col;
5693               gtk_sheet_click_cell (sheet, row, col, &veto);
5694               if (!veto) break;
5695             }
5696           if (sheet->selection_cell.row > 0)
5697             {
5698               row = sheet->selection_cell.row - scroll;
5699               while (!yyy_row_is_visible (sheet, row) && row > 0) row--;
5700               row = MAX (0, row);
5701               gtk_sheet_extend_selection (sheet, row, sheet->selection_cell.col);
5702             }
5703           return TRUE;
5704         }
5705       col = sheet->active_cell.col;
5706       row = sheet->active_cell.row;
5707       if (state == GTK_SHEET_COLUMN_SELECTED)
5708         row = MIN_VISIBLE_ROW (sheet);
5709       if (state == GTK_SHEET_ROW_SELECTED)
5710         col = MIN_VISIBLE_COLUMN (sheet);
5711       row = row - scroll;
5712       while (!yyy_row_is_visible (sheet, row) && row > 0) row--;
5713       row = MAX (0, row);
5714       gtk_sheet_click_cell (sheet, row, col, &veto);
5715       extend_selection = FALSE;
5716       break;
5717     case GDK_Page_Down:
5718       scroll = MAX_VISIBLE_ROW (sheet)- MIN_VISIBLE_ROW (sheet)+1;
5719     case GDK_Down:
5720       if (extend_selection)
5721         {
5722           if (state == GTK_STATE_NORMAL)
5723             {
5724               row = sheet->active_cell.row;
5725               col = sheet->active_cell.col;
5726               gtk_sheet_click_cell (sheet, row, col, &veto);
5727               if (!veto) break;
5728             }
5729           if (sheet->selection_cell.row < yyy_row_count (sheet)- 1)
5730             {
5731               row = sheet->selection_cell.row + scroll;
5732               while (!yyy_row_is_visible (sheet, row) && row < yyy_row_count (sheet)- 1) row++;
5733               row = MIN (yyy_row_count (sheet)- 1, row);
5734               gtk_sheet_extend_selection (sheet, row, sheet->selection_cell.col);
5735             }
5736           return TRUE;
5737         }
5738       col = sheet->active_cell.col;
5739       row = sheet->active_cell.row;
5740       if (sheet->active_cell.row < yyy_row_count (sheet)- 1)
5741         {
5742           if (state == GTK_SHEET_COLUMN_SELECTED)
5743             row = MIN_VISIBLE_ROW (sheet)- 1;
5744           if (state == GTK_SHEET_ROW_SELECTED)
5745             col = MIN_VISIBLE_COLUMN (sheet);
5746           row = row + scroll;
5747           while (!yyy_row_is_visible (sheet, row) && row < yyy_row_count (sheet)- 1) row++;
5748           row = MIN (yyy_row_count (sheet)- 1, row);
5749         }
5750       gtk_sheet_click_cell (sheet, row, col, &veto);
5751       extend_selection = FALSE;
5752       break;
5753     case GDK_Right:
5754       if (extend_selection)
5755         {
5756           if (state == GTK_STATE_NORMAL)
5757             {
5758               row = sheet->active_cell.row;
5759               col = sheet->active_cell.col;
5760               gtk_sheet_click_cell (sheet, row, col, &veto);
5761               if (!veto) break;
5762             }
5763           if (sheet->selection_cell.col < xxx_column_count (sheet) - 1)
5764             {
5765               col = sheet->selection_cell.col + 1;
5766               while (! xxx_column_is_visible (sheet, col) && col < xxx_column_count (sheet) - 1)
5767                 col++;
5768               gtk_sheet_extend_selection (sheet, sheet->selection_cell.row, col);
5769             }
5770           return TRUE;
5771         }
5772       col = sheet->active_cell.col;
5773       row = sheet->active_cell.row;
5774       if (sheet->active_cell.col < xxx_column_count (sheet) - 1)
5775         {
5776           col ++;
5777           if (state == GTK_SHEET_ROW_SELECTED)
5778             col = MIN_VISIBLE_COLUMN (sheet)- 1;
5779           if (state == GTK_SHEET_COLUMN_SELECTED)
5780             row = MIN_VISIBLE_ROW (sheet);
5781           while (! xxx_column_is_visible (sheet, col) && col < xxx_column_count (sheet) - 1) col++;
5782           if (strlen (gtk_entry_get_text (GTK_ENTRY (gtk_sheet_get_entry (sheet)))) == 0
5783               || force_move)
5784             {
5785               gtk_sheet_click_cell (sheet, row, col, &veto);
5786             }
5787           else
5788             return FALSE;
5789         }
5790       extend_selection = FALSE;
5791       break;
5792     case GDK_Left:
5793       if (extend_selection)
5794         {
5795           if (state == GTK_STATE_NORMAL)
5796             {
5797               row = sheet->active_cell.row;
5798               col = sheet->active_cell.col;
5799               gtk_sheet_click_cell (sheet, row, col, &veto);
5800               if (!veto) break;
5801             }
5802           if (sheet->selection_cell.col > 0)
5803             {
5804               col = sheet->selection_cell.col - 1;
5805               while (! xxx_column_is_visible (sheet, col) && col > 0) col--;
5806               gtk_sheet_extend_selection (sheet, sheet->selection_cell.row, col);
5807             }
5808           return TRUE;
5809         }
5810       col = sheet->active_cell.col - 1;
5811       row = sheet->active_cell.row;
5812       if (state == GTK_SHEET_ROW_SELECTED)
5813         col = MIN_VISIBLE_COLUMN (sheet)- 1;
5814       if (state == GTK_SHEET_COLUMN_SELECTED)
5815         row = MIN_VISIBLE_ROW (sheet);
5816       while (! xxx_column_is_visible (sheet, col) && col > 0) col--;
5817       col = MAX (0, col);
5818
5819       if (strlen (gtk_entry_get_text (GTK_ENTRY (gtk_sheet_get_entry (sheet)))) == 0
5820           || force_move)
5821         {
5822           gtk_sheet_click_cell (sheet, row, col, &veto);
5823         }
5824       else
5825         return FALSE;
5826       extend_selection = FALSE;
5827       break;
5828     case GDK_Home:
5829       row = 0;
5830       while (!yyy_row_is_visible (sheet, row) && row < yyy_row_count (sheet) - 1) row++;
5831       gtk_sheet_click_cell (sheet, row, sheet->active_cell.col, &veto);
5832       extend_selection = FALSE;
5833       break;
5834     case GDK_End:
5835       row = yyy_row_count (sheet) - 1;
5836       while (!yyy_row_is_visible (sheet, row) && row > 0) row--;
5837       gtk_sheet_click_cell (sheet, row, sheet->active_cell.col, &veto);
5838       extend_selection = FALSE;
5839       break;
5840     default:
5841       if (in_selection)
5842         {
5843           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
5844           if (extend_selection) return TRUE;
5845         }
5846       if (state == GTK_SHEET_ROW_SELECTED)
5847         sheet->active_cell.col = MIN_VISIBLE_COLUMN (sheet);
5848       if (state == GTK_SHEET_COLUMN_SELECTED)
5849         sheet->active_cell.row = MIN_VISIBLE_ROW (sheet);
5850       return FALSE;
5851     }
5852
5853   if (extend_selection) return TRUE;
5854
5855   gtk_sheet_activate_cell (sheet, sheet->active_cell.row,
5856                            sheet->active_cell.col);
5857
5858   return TRUE;
5859 }
5860
5861 static void
5862 gtk_sheet_size_request (GtkWidget * widget,
5863                         GtkRequisition * requisition)
5864 {
5865   GtkSheet *sheet;
5866   GList *children;
5867   GtkSheetChild *child;
5868   GtkRequisition child_requisition;
5869
5870   g_return_if_fail (widget != NULL);
5871   g_return_if_fail (GTK_IS_SHEET (widget));
5872   g_return_if_fail (requisition != NULL);
5873
5874   sheet = GTK_SHEET (widget);
5875
5876   requisition->width = 3*DEFAULT_COLUMN_WIDTH;
5877   requisition->height = 3*DEFAULT_ROW_HEIGHT (widget);
5878
5879   /* compute the size of the column title area */
5880   if (sheet->column_titles_visible)
5881     requisition->height += sheet->column_title_area.height;
5882
5883   /* compute the size of the row title area */
5884   if (sheet->row_titles_visible)
5885     requisition->width += sheet->row_title_area.width;
5886
5887   children = sheet->children;
5888   while (children)
5889     {
5890       child = children->data;
5891       children = children->next;
5892
5893       gtk_widget_size_request (child->widget, &child_requisition);
5894     }
5895 }
5896
5897
5898 static void
5899 gtk_sheet_size_allocate (GtkWidget * widget,
5900                          GtkAllocation * allocation)
5901 {
5902   GtkSheet *sheet;
5903   GtkAllocation sheet_allocation;
5904   gint border_width;
5905
5906   g_return_if_fail (widget != NULL);
5907   g_return_if_fail (GTK_IS_SHEET (widget));
5908   g_return_if_fail (allocation != NULL);
5909
5910   sheet = GTK_SHEET (widget);
5911   widget->allocation = *allocation;
5912   border_width = GTK_CONTAINER (widget)->border_width;
5913
5914   if (GTK_WIDGET_REALIZED (widget))
5915     gdk_window_move_resize (widget->window,
5916                             allocation->x + border_width,
5917                             allocation->y + border_width,
5918                             allocation->width - 2 * border_width,
5919                             allocation->height - 2 * border_width);
5920
5921   /* use internal allocation structure for all the math
5922    * because it's easier than always subtracting the container
5923    * border width */
5924   sheet->internal_allocation.x = 0;
5925   sheet->internal_allocation.y = 0;
5926   sheet->internal_allocation.width = allocation->width - 2 * border_width;
5927   sheet->internal_allocation.height = allocation->height - 2 * border_width;
5928
5929   sheet_allocation.x = 0;
5930   sheet_allocation.y = 0;
5931   sheet_allocation.width = allocation->width - 2 * border_width;
5932   sheet_allocation.height = allocation->height - 2 * border_width;
5933
5934   sheet->sheet_window_width = sheet_allocation.width;
5935   sheet->sheet_window_height = sheet_allocation.height;
5936
5937   if (GTK_WIDGET_REALIZED (widget))
5938     gdk_window_move_resize (sheet->sheet_window,
5939                             sheet_allocation.x,
5940                             sheet_allocation.y,
5941                             sheet_allocation.width,
5942                             sheet_allocation.height);
5943
5944   /* position the window which holds the column title buttons */
5945   sheet->column_title_area.x = 0;
5946   sheet->column_title_area.y = 0;
5947   if (sheet->row_titles_visible)
5948     sheet->column_title_area.x = sheet->row_title_area.width;
5949   sheet->column_title_area.width = sheet_allocation.width -
5950     sheet->column_title_area.x;
5951   if (GTK_WIDGET_REALIZED (widget) && sheet->column_titles_visible)
5952     gdk_window_move_resize (sheet->column_title_window,
5953                             sheet->column_title_area.x,
5954                             sheet->column_title_area.y,
5955                             sheet->column_title_area.width,
5956                             sheet->column_title_area.height);
5957
5958   sheet->sheet_window_width = sheet_allocation.width;
5959   sheet->sheet_window_height = sheet_allocation.height;
5960
5961   /* column button allocation */
5962   size_allocate_column_title_buttons (sheet);
5963
5964   /* position the window which holds the row title buttons */
5965   sheet->row_title_area.x = 0;
5966   sheet->row_title_area.y = 0;
5967   if (sheet->column_titles_visible)
5968     sheet->row_title_area.y = sheet->column_title_area.height;
5969   sheet->row_title_area.height = sheet_allocation.height -
5970     sheet->row_title_area.y;
5971
5972   if (GTK_WIDGET_REALIZED (widget) && sheet->row_titles_visible)
5973     gdk_window_move_resize (sheet->row_title_window,
5974                             sheet->row_title_area.x,
5975                             sheet->row_title_area.y,
5976                             sheet->row_title_area.width,
5977                             sheet->row_title_area.height);
5978
5979
5980   /* row button allocation */
5981   size_allocate_row_title_buttons (sheet);
5982   size_allocate_column_title_buttons (sheet);
5983
5984   /* re - scale backing pixmap */
5985   gtk_sheet_make_backing_pixmap (sheet, 0, 0);
5986   gtk_sheet_position_children (sheet);
5987
5988   /* set the scrollbars adjustments */
5989   adjust_scrollbars (sheet);
5990 }
5991
5992 static void
5993 size_allocate_column_title_buttons (GtkSheet * sheet)
5994 {
5995   gint i;
5996   gint x, width;
5997
5998   if (!sheet->column_titles_visible) return;
5999   if (!GTK_WIDGET_REALIZED (sheet))
6000     return;
6001
6002   width = sheet->sheet_window_width;
6003   x = 0;
6004
6005   if (sheet->row_titles_visible)
6006     {
6007       width -= sheet->row_title_area.width;
6008       x = sheet->row_title_area.width;
6009     }
6010
6011   if (sheet->column_title_area.width != width || sheet->column_title_area.x != x)
6012     {
6013       sheet->column_title_area.width = width;
6014       sheet->column_title_area.x = x;
6015       gdk_window_move_resize (sheet->column_title_window,
6016                               sheet->column_title_area.x,
6017                               sheet->column_title_area.y,
6018                               sheet->column_title_area.width,
6019                               sheet->column_title_area.height);
6020     }
6021
6022
6023   if (MAX_VISIBLE_COLUMN (sheet) == xxx_column_count (sheet) - 1)
6024     gdk_window_clear_area (sheet->column_title_window,
6025                            0, 0,
6026                            sheet->column_title_area.width,
6027                            sheet->column_title_area.height);
6028
6029   if (!GTK_WIDGET_DRAWABLE (sheet)) return;
6030
6031   size_allocate_global_button (sheet);
6032
6033   for (i = MIN_VISIBLE_COLUMN (sheet); i <= MAX_VISIBLE_COLUMN (sheet); i++)
6034     gtk_sheet_column_title_button_draw (sheet, i);
6035 }
6036
6037 static void
6038 size_allocate_row_title_buttons (GtkSheet * sheet)
6039 {
6040   gint i;
6041   gint y, height;
6042
6043   if (!sheet->row_titles_visible) return;
6044   if (!GTK_WIDGET_REALIZED (sheet))
6045     return;
6046
6047   height = sheet->sheet_window_height;
6048   y = 0;
6049
6050   if (sheet->column_titles_visible)
6051     {
6052       height -= sheet->column_title_area.height;
6053       y = sheet->column_title_area.height;
6054     }
6055
6056   if (sheet->row_title_area.height != height || sheet->row_title_area.y != y)
6057     {
6058       sheet->row_title_area.y = y;
6059       sheet->row_title_area.height = height;
6060       gdk_window_move_resize (sheet->row_title_window,
6061                               sheet->row_title_area.x,
6062                               sheet->row_title_area.y,
6063                               sheet->row_title_area.width,
6064                               sheet->row_title_area.height);
6065     }
6066   if (MAX_VISIBLE_ROW (sheet) == yyy_row_count (sheet)- 1)
6067     gdk_window_clear_area (sheet->row_title_window,
6068                            0, 0,
6069                            sheet->row_title_area.width,
6070                            sheet->row_title_area.height);
6071
6072   if (!GTK_WIDGET_DRAWABLE (sheet)) return;
6073
6074   size_allocate_global_button (sheet);
6075
6076   for (i = MIN_VISIBLE_ROW (sheet); i <= MAX_VISIBLE_ROW (sheet); i++)
6077     {
6078       if ( i >= yyy_row_count (sheet))
6079         break;
6080       gtk_sheet_row_title_button_draw (sheet, i);
6081     }
6082 }
6083
6084
6085 static void
6086 gtk_sheet_size_allocate_entry (GtkSheet *sheet)
6087 {
6088   GtkAllocation shentry_allocation;
6089   GtkSheetCellAttr attributes = { 0 };
6090   GtkEntry *sheet_entry;
6091   GtkStyle *style = NULL, *previous_style = NULL;
6092   gint row, col;
6093   gint size, max_size, text_size, column_width;
6094   const gchar *text;
6095
6096   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
6097   if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
6098
6099   sheet_entry = GTK_ENTRY (gtk_sheet_get_entry (sheet));
6100
6101   if ( ! gtk_sheet_get_attributes (sheet, sheet->active_cell.row,
6102                                    sheet->active_cell.col,
6103                                    &attributes) )
6104     return ;
6105
6106   if ( GTK_WIDGET_REALIZED (sheet->entry_widget) )
6107     {
6108       if (!GTK_WIDGET (sheet_entry)->style)
6109         gtk_widget_ensure_style (GTK_WIDGET (sheet_entry));
6110
6111       previous_style = GTK_WIDGET (sheet_entry)->style;
6112
6113       style = gtk_style_copy (previous_style);
6114       style->bg[GTK_STATE_NORMAL] = attributes.background;
6115       style->fg[GTK_STATE_NORMAL] = attributes.foreground;
6116       style->text[GTK_STATE_NORMAL] = attributes.foreground;
6117       style->bg[GTK_STATE_ACTIVE] = attributes.background;
6118       style->fg[GTK_STATE_ACTIVE] = attributes.foreground;
6119       style->text[GTK_STATE_ACTIVE] = attributes.foreground;
6120
6121       pango_font_description_free (style->font_desc);
6122       g_assert (attributes.font_desc);
6123       style->font_desc = pango_font_description_copy (attributes.font_desc);
6124
6125       GTK_WIDGET (sheet_entry)->style = style;
6126       gtk_widget_size_request (sheet->entry_widget, NULL);
6127       GTK_WIDGET (sheet_entry)->style = previous_style;
6128
6129       if (style != previous_style)
6130         {
6131           if (!GTK_IS_ITEM_ENTRY (sheet->entry_widget))
6132             {
6133               style->bg[GTK_STATE_NORMAL] = previous_style->bg[GTK_STATE_NORMAL];
6134               style->fg[GTK_STATE_NORMAL] = previous_style->fg[GTK_STATE_NORMAL];
6135               style->bg[GTK_STATE_ACTIVE] = previous_style->bg[GTK_STATE_ACTIVE];
6136               style->fg[GTK_STATE_ACTIVE] = previous_style->fg[GTK_STATE_ACTIVE];
6137             }
6138           gtk_widget_set_style (GTK_WIDGET (sheet_entry), style);
6139           g_object_unref (style);
6140         }
6141     }
6142
6143   if (GTK_IS_ITEM_ENTRY (sheet_entry))
6144     max_size = GTK_ITEM_ENTRY (sheet_entry)->text_max_size;
6145   else
6146     max_size = 0;
6147
6148   text_size = 0;
6149   text = gtk_entry_get_text (GTK_ENTRY (sheet_entry));
6150   if (text && strlen (text) > 0)
6151     text_size = STRING_WIDTH (GTK_WIDGET (sheet), attributes.font_desc, text);
6152
6153   column_width = xxx_column_width (sheet, sheet->active_cell.col);
6154
6155   size = MIN (text_size, max_size);
6156   size = MAX (size, column_width - 2 * CELLOFFSET);
6157
6158   row = sheet->active_cell.row;
6159   col = sheet->active_cell.col;
6160
6161   shentry_allocation.x = COLUMN_LEFT_XPIXEL (sheet, sheet->active_cell.col);
6162   shentry_allocation.y = ROW_TOP_YPIXEL (sheet, sheet->active_cell.row);
6163   shentry_allocation.width = column_width;
6164   shentry_allocation.height = yyy_row_height (sheet, sheet->active_cell.row);
6165
6166   if (GTK_IS_ITEM_ENTRY (sheet->entry_widget))
6167     {
6168       shentry_allocation.height -= 2 * CELLOFFSET;
6169       shentry_allocation.y += CELLOFFSET;
6170       shentry_allocation.width = size;
6171
6172       switch (GTK_ITEM_ENTRY (sheet_entry)->justification)
6173         {
6174         case GTK_JUSTIFY_CENTER:
6175           shentry_allocation.x += column_width / 2 - size / 2;
6176           break;
6177         case GTK_JUSTIFY_RIGHT:
6178           shentry_allocation.x += column_width - size - CELLOFFSET;
6179           break;
6180         case GTK_JUSTIFY_LEFT:
6181         case GTK_JUSTIFY_FILL:
6182           shentry_allocation.x += CELLOFFSET;
6183           break;
6184         }
6185     }
6186
6187   if (!GTK_IS_ITEM_ENTRY (sheet->entry_widget))
6188     {
6189       shentry_allocation.x += 2;
6190       shentry_allocation.y += 2;
6191       shentry_allocation.width -= MIN (shentry_allocation.width, 3);
6192       shentry_allocation.height -= MIN (shentry_allocation.height, 3);
6193     }
6194
6195   gtk_widget_size_allocate (sheet->entry_widget, &shentry_allocation);
6196
6197   if (previous_style == style) g_object_unref (previous_style);
6198 }
6199
6200 static void
6201 gtk_sheet_entry_set_max_size (GtkSheet *sheet)
6202 {
6203   gint i;
6204   gint size = 0;
6205   gint sizel = 0, sizer = 0;
6206   gint row, col;
6207   GtkJustification justification;
6208   gchar *s = NULL;
6209
6210   row = sheet->active_cell.row;
6211   col = sheet->active_cell.col;
6212
6213   if ( ! GTK_IS_ITEM_ENTRY (sheet->entry_widget) )
6214     return;
6215
6216   justification = GTK_ITEM_ENTRY (sheet->entry_widget)->justification;
6217
6218   switch (justification)
6219     {
6220     case GTK_JUSTIFY_FILL:
6221     case GTK_JUSTIFY_LEFT:
6222       for (i = col + 1; i <= MAX_VISIBLE_COLUMN (sheet); i++)
6223         {
6224           if ((s = gtk_sheet_cell_get_text (sheet, row, i)))
6225             {
6226               g_free (s);
6227               break;
6228             }
6229           size +=xxx_column_width (sheet, i);
6230         }
6231       size = MIN (size, sheet->sheet_window_width - COLUMN_LEFT_XPIXEL (sheet, col));
6232       break;
6233     case GTK_JUSTIFY_RIGHT:
6234       for (i = col - 1; i >= MIN_VISIBLE_COLUMN (sheet); i--)
6235         {
6236           if ((s = gtk_sheet_cell_get_text (sheet, row, i)))
6237             {
6238               g_free (s);
6239               break;
6240             }
6241           size +=xxx_column_width (sheet, i);
6242         }
6243       break;
6244     case GTK_JUSTIFY_CENTER:
6245       for (i = col + 1; i <= MAX_VISIBLE_COLUMN (sheet); i++)
6246         {
6247           sizer += xxx_column_width (sheet, i);
6248         }
6249       for (i = col - 1; i >= MIN_VISIBLE_COLUMN (sheet); i--)
6250         {
6251           if ((s = gtk_sheet_cell_get_text (sheet, row, i)))
6252             {
6253               g_free (s);
6254               break;
6255             }
6256           sizel +=xxx_column_width (sheet, i);
6257         }
6258       size = 2 * MIN (sizel, sizer);
6259       break;
6260     }
6261
6262   if (size != 0)
6263     size += xxx_column_width (sheet, col);
6264   GTK_ITEM_ENTRY (sheet->entry_widget)->text_max_size = size;
6265 }
6266
6267
6268 static void
6269 create_sheet_entry (GtkSheet *sheet)
6270 {
6271   if (sheet->entry_widget)
6272     {
6273       gtk_widget_unparent (sheet->entry_widget);
6274     }
6275
6276   if (sheet->entry_type)
6277     {
6278       sheet->entry_container = g_object_new (sheet->entry_type, NULL);
6279       g_object_ref_sink (sheet->entry_container);
6280       sheet->entry_widget = gtk_sheet_get_entry (sheet);
6281
6282       if  ( NULL == sheet->entry_widget)
6283         {
6284           g_warning ("Entry type is %s. It must be GtkEntry subclass, or a widget containing one. "
6285                      "Using default", g_type_name (sheet->entry_type));
6286           g_object_unref (sheet->entry_container);
6287           sheet->entry_widget = sheet->entry_container = gtk_item_entry_new ();
6288         }
6289       else
6290         {
6291           sheet->entry_widget = sheet->entry_container ;
6292         }
6293     }
6294   else
6295     {
6296       sheet->entry_widget = sheet->entry_container = gtk_item_entry_new ();
6297       g_object_ref_sink (sheet->entry_container);
6298     }
6299
6300   gtk_widget_size_request (sheet->entry_widget, NULL);
6301
6302   if (GTK_WIDGET_REALIZED (sheet))
6303     {
6304       gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
6305       gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
6306       gtk_widget_realize (sheet->entry_widget);
6307     }
6308
6309   g_signal_connect_swapped (sheet->entry_widget, "key_press_event",
6310                             G_CALLBACK (gtk_sheet_entry_key_press),
6311                             sheet);
6312
6313   gtk_widget_show (sheet->entry_widget);
6314 }
6315
6316
6317 /* Finds the last child widget that happens to be of type GtkEntry */
6318 static void
6319 find_entry (GtkWidget *w, gpointer user_data)
6320 {
6321   GtkWidget **entry = user_data;
6322   if ( GTK_IS_ENTRY (w))
6323     {
6324       *entry = w;
6325     }
6326 }
6327
6328 GtkWidget *
6329 gtk_sheet_get_entry (GtkSheet *sheet)
6330 {
6331   GtkWidget *parent;
6332   GtkWidget *entry = NULL;
6333   GtkTableChild *table_child;
6334   GtkBoxChild *box_child;
6335   GList *children = NULL;
6336
6337   g_return_val_if_fail (sheet != NULL, NULL);
6338   g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);
6339   g_return_val_if_fail (sheet->entry_widget != NULL, NULL);
6340
6341   if (GTK_IS_ENTRY (sheet->entry_container))
6342     return (sheet->entry_container);
6343
6344   parent = sheet->entry_container;
6345
6346   if (GTK_IS_TABLE (parent)) children = GTK_TABLE (parent)->children;
6347   if (GTK_IS_BOX (parent)) children = GTK_BOX (parent)->children;
6348
6349   if (GTK_IS_CONTAINER (parent))
6350     {
6351       gtk_container_forall (GTK_CONTAINER (parent), find_entry, &entry);
6352
6353       if (GTK_IS_ENTRY (entry))
6354         return entry;
6355     }
6356
6357   if (!children) return NULL;
6358
6359   while (children)
6360     {
6361       if (GTK_IS_TABLE (parent))
6362         {
6363           table_child = children->data;
6364           entry = table_child->widget;
6365         }
6366       if (GTK_IS_BOX (parent))
6367         {
6368           box_child = children->data;
6369           entry = box_child->widget;
6370         }
6371
6372       if (GTK_IS_ENTRY (entry))
6373         break;
6374       children = children->next;
6375     }
6376
6377
6378   if (!GTK_IS_ENTRY (entry)) return NULL;
6379
6380   return (entry);
6381
6382 }
6383
6384 GtkWidget *
6385 gtk_sheet_get_entry_widget (GtkSheet *sheet)
6386 {
6387   g_return_val_if_fail (sheet != NULL, NULL);
6388   g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);
6389   g_return_val_if_fail (sheet->entry_widget != NULL, NULL);
6390
6391   return (sheet->entry_widget);
6392 }
6393
6394
6395 static void
6396 gtk_sheet_button_draw (GtkSheet *sheet, GdkWindow *window,
6397                        GtkSheetButton *button, gboolean is_sensitive,
6398                        GdkRectangle allocation)
6399 {
6400   GtkShadowType shadow_type;
6401   gint text_width = 0, text_height = 0;
6402   GtkSheetChild *child = NULL;
6403   PangoAlignment align = PANGO_ALIGN_LEFT;
6404
6405   gboolean rtl ;
6406
6407   gint state = 0;
6408   gint len = 0;
6409   gchar *line = 0;
6410
6411   g_return_if_fail (sheet != NULL);
6412   g_return_if_fail (button != NULL);
6413
6414   rtl = gtk_widget_get_direction (GTK_WIDGET (sheet)) == GTK_TEXT_DIR_RTL;
6415
6416   gdk_window_clear_area (window,
6417                          allocation.x, allocation.y,
6418                          allocation.width, allocation.height);
6419
6420   gtk_paint_box (sheet->button->style, window,
6421                  GTK_STATE_NORMAL, GTK_SHADOW_OUT,
6422                  &allocation, GTK_WIDGET (sheet->button),
6423                  "buttondefault",
6424                  allocation.x, allocation.y,
6425                  allocation.width, allocation.height);
6426
6427   state = button->state;
6428   if (!is_sensitive) state = GTK_STATE_INSENSITIVE;
6429
6430   if (state == GTK_STATE_ACTIVE)
6431     shadow_type = GTK_SHADOW_IN;
6432   else
6433     shadow_type = GTK_SHADOW_OUT;
6434
6435   if (state != GTK_STATE_NORMAL && state != GTK_STATE_INSENSITIVE)
6436     gtk_paint_box (sheet->button->style, window,
6437                    button->state, shadow_type,
6438                    &allocation, GTK_WIDGET (sheet->button),
6439                    "button",
6440                    allocation.x, allocation.y,
6441                    allocation.width, allocation.height);
6442
6443   if (button->label_visible)
6444     {
6445
6446       text_height = DEFAULT_ROW_HEIGHT (GTK_WIDGET (sheet))- 2 * CELLOFFSET;
6447
6448       gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
6449                                  &allocation);
6450       gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc, &allocation);
6451
6452       allocation.y += 2 * sheet->button->style->ythickness;
6453
6454
6455       if (button->label && strlen (button->label)>0)
6456         {
6457           gchar *words = 0;
6458           PangoLayout *layout = NULL;
6459           gint real_x = allocation.x, real_y = allocation.y;
6460
6461           words = button->label;
6462           line = g_new (gchar, 1);
6463           line[0]='\0';
6464
6465           while (words && *words != '\0')
6466             {
6467               if (*words != '\n')
6468                 {
6469                   len = strlen (line);
6470                   line = g_realloc (line, len + 2);
6471                   line[len]=*words;
6472                   line[len + 1]='\0';
6473                 }
6474               if (*words == '\n' || * (words + 1) == '\0')
6475                 {
6476                   text_width = STRING_WIDTH (GTK_WIDGET (sheet), GTK_WIDGET (sheet)->style->font_desc, line);
6477
6478                   layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), line);
6479                   switch (button->justification)
6480                     {
6481                     case GTK_JUSTIFY_LEFT:
6482                       real_x = allocation.x + CELLOFFSET;
6483                       align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
6484                       break;
6485                     case GTK_JUSTIFY_RIGHT:
6486                       real_x = allocation.x + allocation.width - text_width - CELLOFFSET;
6487                       align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
6488                       break;
6489                     case GTK_JUSTIFY_CENTER:
6490                     default:
6491                       real_x = allocation.x + (allocation.width - text_width)/2;
6492                       align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
6493                       pango_layout_set_justify (layout, TRUE);
6494                     }
6495                   pango_layout_set_alignment (layout, align);
6496                   gtk_paint_layout (GTK_WIDGET (sheet)->style,
6497                                     window,
6498                                     state,
6499                                     FALSE,
6500                                     &allocation,
6501                                     GTK_WIDGET (sheet),
6502                                     "label",
6503                                     real_x, real_y,
6504                                     layout);
6505                   g_object_unref (layout);
6506
6507                   real_y += text_height + 2;
6508
6509                   g_free (line);
6510                   line = g_new (gchar, 1);
6511                   line[0]='\0';
6512                 }
6513               words++;
6514             }
6515           g_free (line);
6516         }
6517
6518       gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
6519                                  NULL);
6520       gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc, NULL);
6521
6522     }
6523
6524   if ((child = button->child) && (child->widget))
6525     {
6526       child->x = allocation.x;
6527       child->y = allocation.y;
6528
6529       child->x += (allocation.width - child->widget->requisition.width) / 2;
6530       child->y += (allocation.height - child->widget->requisition.height) / 2;
6531       allocation.x = child->x;
6532       allocation.y = child->y;
6533       allocation.width = child->widget->requisition.width;
6534       allocation.height = child->widget->requisition.height;
6535
6536       allocation.x = child->x;
6537       allocation.y = child->y;
6538
6539       gtk_widget_set_state (child->widget, button->state);
6540
6541       if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) &&
6542           GTK_WIDGET_MAPPED (child->widget))
6543         {
6544           gtk_widget_size_allocate (child->widget,
6545                                     &allocation);
6546           gtk_widget_queue_draw (child->widget);
6547         }
6548     }
6549
6550   gtk_sheet_button_free (button);
6551 }
6552
6553
6554 /* COLUMN value of - 1 indicates that the area to the right of the rightmost
6555    button should be redrawn */
6556 static void
6557 gtk_sheet_column_title_button_draw (GtkSheet *sheet, gint column)
6558 {
6559   GdkWindow *window = NULL;
6560   GdkRectangle allocation;
6561
6562   gboolean is_sensitive = FALSE;
6563
6564   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
6565
6566   if (column >= 0 && ! xxx_column_is_visible (sheet, column)) return;
6567   if (column >= 0 && !sheet->column_titles_visible) return;
6568   if (column >= 0 && column < MIN_VISIBLE_COLUMN (sheet)) return;
6569   if (column >= 0 && column > MAX_VISIBLE_COLUMN (sheet)) return;
6570
6571   window = sheet->column_title_window;
6572   allocation.y = 0;
6573   allocation.height = sheet->column_title_area.height;
6574
6575   if ( column == -1 )
6576     {
6577       const gint cols = xxx_column_count (sheet) ;
6578       allocation.x = COLUMN_LEFT_XPIXEL (sheet, cols - 1)
6579         ;
6580       allocation.width = sheet->column_title_area.width
6581         + sheet->column_title_area.x
6582         - allocation.x;
6583
6584       gdk_window_clear_area (window,
6585                              allocation.x, allocation.y,
6586                              allocation.width, allocation.height);
6587     }
6588   else
6589     {
6590       GtkSheetButton *button = xxx_column_button (sheet, column);
6591       allocation.x = COLUMN_LEFT_XPIXEL (sheet, column) + CELL_SPACING;
6592       if (sheet->row_titles_visible)
6593         allocation.x -= sheet->row_title_area.width;
6594
6595       allocation.width = xxx_column_width (sheet, column);
6596
6597       is_sensitive = xxx_column_is_sensitive (sheet, column);
6598       gtk_sheet_button_draw (sheet, window, button,
6599                              is_sensitive, allocation);
6600
6601       /* FIXME: Not freeing this button is correct (sort of),
6602          because in PSPP the model always returns a static copy */
6603
6604       /* gtk_sheet_button_free (button); */
6605
6606     }
6607 }
6608
6609 static void
6610 gtk_sheet_row_title_button_draw (GtkSheet *sheet, gint row)
6611 {
6612   GdkWindow *window = NULL;
6613   GdkRectangle allocation;
6614   GtkSheetButton *button = NULL;
6615   gboolean is_sensitive = FALSE;
6616
6617
6618   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
6619
6620   if (row >= 0 && !yyy_row_is_visible (sheet, row)) return;
6621   if (row >= 0 && !sheet->row_titles_visible) return;
6622   if (row >= 0 && row < MIN_VISIBLE_ROW (sheet)) return;
6623   if (row >= 0 && row > MAX_VISIBLE_ROW (sheet)) return;
6624
6625
6626   window = sheet->row_title_window;
6627   button = yyy_row_button (sheet, row);
6628   allocation.x = 0;
6629   allocation.y = ROW_TOP_YPIXEL (sheet, row) + CELL_SPACING;
6630   if (sheet->column_titles_visible)
6631     allocation.y -= sheet->column_title_area.height;
6632   allocation.width = sheet->row_title_area.width;
6633   allocation.height = yyy_row_height (sheet, row);
6634   is_sensitive = yyy_row_is_sensitive (sheet, row);
6635
6636   gtk_sheet_button_draw (sheet, window, button, is_sensitive, allocation);
6637 }
6638
6639 /* SCROLLBARS
6640  *
6641  * functions:
6642  * adjust_scrollbars
6643  * vadjustment_value_changed
6644  * hadjustment_value_changed */
6645
6646 static void
6647 adjust_scrollbars (GtkSheet * sheet)
6648 {
6649   if (sheet->vadjustment)
6650     {
6651       sheet->vadjustment->page_size = sheet->sheet_window_height;
6652       sheet->vadjustment->page_increment = sheet->sheet_window_height / 2;
6653       sheet->vadjustment->step_increment = DEFAULT_ROW_HEIGHT (GTK_WIDGET (sheet));
6654       sheet->vadjustment->lower = 0;
6655       sheet->vadjustment->upper = SHEET_HEIGHT (sheet) + 80;
6656       g_signal_emit_by_name (sheet->vadjustment, "changed");
6657
6658     }
6659
6660   if (sheet->hadjustment)
6661     {
6662       sheet->hadjustment->page_size = sheet->sheet_window_width;
6663       sheet->hadjustment->page_increment = sheet->sheet_window_width / 2;
6664       sheet->hadjustment->step_increment = DEFAULT_COLUMN_WIDTH;
6665       sheet->hadjustment->lower = 0;
6666       sheet->hadjustment->upper = SHEET_WIDTH (sheet)+ 80;
6667       g_signal_emit_by_name (sheet->hadjustment, "changed");
6668
6669     }
6670 }
6671
6672 static void
6673 vadjustment_value_changed (GtkAdjustment * adjustment,
6674                            gpointer data)
6675 {
6676   GtkSheet *sheet;
6677   gint diff, value, old_value;
6678   gint row, new_row;
6679   gint y = 0;
6680
6681   g_return_if_fail (adjustment != NULL);
6682   g_return_if_fail (data != NULL);
6683   g_return_if_fail (GTK_IS_SHEET (data));
6684
6685   sheet = GTK_SHEET (data);
6686
6687   if (GTK_SHEET_IS_FROZEN (sheet)) return;
6688
6689   row = ROW_FROM_YPIXEL (sheet, CELL_SPACING);
6690
6691   old_value = - sheet->voffset;
6692
6693   new_row = g_sheet_row_pixel_to_row (sheet->row_geometry,
6694                                       adjustment->value);
6695
6696   y = g_sheet_row_start_pixel (sheet->row_geometry, new_row);
6697
6698   if (adjustment->value > sheet->old_vadjustment && sheet->old_vadjustment > 0. &&
6699       yyy_row_height (sheet, row) > sheet->vadjustment->step_increment)
6700     {
6701       /* This avoids embarrassing twitching */
6702       if (row == new_row && row != yyy_row_count (sheet) - 1 &&
6703           adjustment->value - sheet->old_vadjustment >=
6704           sheet->vadjustment->step_increment &&
6705           new_row + 1 != MIN_VISIBLE_ROW (sheet))
6706         {
6707           new_row +=1;
6708           y = y+yyy_row_height (sheet, row);
6709         }
6710     }
6711
6712   /* Negative old_adjustment enforces the redraw, otherwise avoid
6713      spureous redraw */
6714   if (sheet->old_vadjustment >= 0. && row == new_row)
6715     {
6716       sheet->old_vadjustment = sheet->vadjustment->value;
6717       return;
6718     }
6719
6720   sheet->old_vadjustment = sheet->vadjustment->value;
6721   adjustment->value = y;
6722
6723
6724   if (new_row == 0)
6725     {
6726       sheet->vadjustment->step_increment = yyy_row_height (sheet, 0);
6727     }
6728   else
6729     {
6730       sheet->vadjustment->step_increment =
6731         MIN (yyy_row_height (sheet, new_row), yyy_row_height (sheet, new_row - 1));
6732     }
6733
6734   sheet->vadjustment->value = adjustment->value;
6735
6736   value = adjustment->value;
6737
6738   if (value >= - sheet->voffset)
6739     {
6740       /* scroll down */
6741       diff = value + sheet->voffset;
6742     }
6743   else
6744     {
6745       /* scroll up */
6746       diff = - sheet->voffset - value;
6747     }
6748
6749   sheet->voffset = - value;
6750
6751   if (GTK_WIDGET_REALIZED (sheet->entry_widget) &&
6752       sheet->state == GTK_SHEET_NORMAL &&
6753       sheet->active_cell.row >= 0 && sheet->active_cell.col >= 0 &&
6754       !gtk_sheet_cell_isvisible (sheet, sheet->active_cell.row,
6755                                  sheet->active_cell.col))
6756     {
6757       const gchar *text;
6758
6759       text = gtk_entry_get_text (GTK_ENTRY (gtk_sheet_get_entry (sheet)));
6760
6761       if (!text || strlen (text) == 0)
6762         gtk_sheet_cell_clear (sheet,
6763                               sheet->active_cell.row,
6764                               sheet->active_cell.col);
6765       gtk_widget_unmap (sheet->entry_widget);
6766     }
6767
6768   gtk_sheet_position_children (sheet);
6769
6770   gtk_sheet_range_draw (sheet, NULL);
6771   size_allocate_row_title_buttons (sheet);
6772   size_allocate_global_button (sheet);
6773 }
6774
6775 static void
6776 hadjustment_value_changed (GtkAdjustment * adjustment,
6777                            gpointer data)
6778 {
6779   GtkSheet *sheet;
6780   gint i, diff, value, old_value;
6781   gint column, new_column;
6782   gint x = 0;
6783
6784   g_return_if_fail (adjustment != NULL);
6785   g_return_if_fail (data != NULL);
6786   g_return_if_fail (GTK_IS_SHEET (data));
6787
6788   sheet = GTK_SHEET (data);
6789
6790   if (GTK_SHEET_IS_FROZEN (sheet)) return;
6791
6792   column = COLUMN_FROM_XPIXEL (sheet, CELL_SPACING);
6793
6794   old_value = - sheet->hoffset;
6795
6796   for (i = 0; i < xxx_column_count (sheet); i++)
6797     {
6798       if (xxx_column_is_visible (sheet, i)) x += xxx_column_width (sheet, i);
6799       if (x > adjustment->value) break;
6800     }
6801   x -= xxx_column_width (sheet, i);
6802   new_column = i;
6803
6804   if (adjustment->value > sheet->old_hadjustment && sheet->old_hadjustment > 0 &&
6805       xxx_column_width (sheet, i) > sheet->hadjustment->step_increment)
6806     {
6807       /* This avoids embarrassing twitching */
6808       if (column == new_column && column != xxx_column_count (sheet) - 1 &&
6809           adjustment->value - sheet->old_hadjustment >=
6810           sheet->hadjustment->step_increment &&
6811           new_column + 1 != MIN_VISIBLE_COLUMN (sheet))
6812         {
6813           new_column += 1;
6814           x += xxx_column_width (sheet, column);
6815         }
6816     }
6817
6818   /* Negative old_adjustment enforces the redraw, otherwise avoid spureous redraw */
6819   if (sheet->old_hadjustment >= 0. && new_column == column)
6820     {
6821       sheet->old_hadjustment = sheet->hadjustment->value;
6822       return;
6823     }
6824
6825   sheet->old_hadjustment = sheet->hadjustment->value;
6826   adjustment->value = x;
6827
6828   if (new_column == 0)
6829     {
6830       sheet->hadjustment->step_increment = xxx_column_width (sheet, 0);
6831     }
6832   else
6833     {
6834       sheet->hadjustment->step_increment =
6835         MIN (xxx_column_width (sheet, new_column), xxx_column_width (sheet, new_column - 1));
6836     }
6837
6838
6839   sheet->hadjustment->value = adjustment->value;
6840
6841   value = adjustment->value;
6842
6843   if (value >= - sheet->hoffset)
6844     {
6845       /* scroll right */
6846       diff = value + sheet->hoffset;
6847     }
6848   else
6849     {
6850       /* scroll left */
6851       diff = - sheet->hoffset - value;
6852     }
6853
6854   sheet->hoffset = - value;
6855   if (GTK_WIDGET_REALIZED (sheet->entry_widget) &&
6856       sheet->state == GTK_SHEET_NORMAL &&
6857       sheet->active_cell.row >= 0 && sheet->active_cell.col >= 0 &&
6858       !gtk_sheet_cell_isvisible (sheet, sheet->active_cell.row,
6859                                  sheet->active_cell.col))
6860     {
6861       const gchar *text;
6862
6863       text = gtk_entry_get_text (GTK_ENTRY (gtk_sheet_get_entry (sheet)));
6864       if (!text || strlen (text) == 0)
6865         gtk_sheet_cell_clear (sheet,
6866                               sheet->active_cell.row,
6867                               sheet->active_cell.col);
6868
6869       gtk_widget_unmap (sheet->entry_widget);
6870     }
6871
6872   gtk_sheet_position_children (sheet);
6873
6874   gtk_sheet_range_draw (sheet, NULL);
6875   size_allocate_column_title_buttons (sheet);
6876 }
6877
6878
6879 /* COLUMN RESIZING */
6880 static void
6881 draw_xor_vline (GtkSheet * sheet)
6882 {
6883   GtkWidget *widget;
6884
6885   g_return_if_fail (sheet != NULL);
6886
6887   widget = GTK_WIDGET (sheet);
6888
6889   gdk_draw_line (widget->window, sheet->xor_gc,
6890                  sheet->x_drag,
6891                  sheet->column_title_area.height,
6892                  sheet->x_drag,
6893                  sheet->sheet_window_height + 1);
6894 }
6895
6896 /* ROW RESIZING */
6897 static void
6898 draw_xor_hline (GtkSheet * sheet)
6899 {
6900   GtkWidget *widget;
6901
6902   g_return_if_fail (sheet != NULL);
6903
6904   widget = GTK_WIDGET (sheet);
6905
6906   gdk_draw_line (widget->window, sheet->xor_gc,
6907                  sheet->row_title_area.width,
6908                  sheet->y_drag,
6909
6910                  sheet->sheet_window_width + 1,
6911                  sheet->y_drag);
6912 }
6913
6914 /* SELECTED RANGE */
6915 static void
6916 draw_xor_rectangle (GtkSheet *sheet, GtkSheetRange range)
6917 {
6918   gint i;
6919   GdkRectangle clip_area, area;
6920   GdkGCValues values;
6921
6922   area.x = COLUMN_LEFT_XPIXEL (sheet, range.col0);
6923   area.y = ROW_TOP_YPIXEL (sheet, range.row0);
6924   area.width = COLUMN_LEFT_XPIXEL (sheet, range.coli)- area.x+
6925     xxx_column_width (sheet, range.coli);
6926   area.height = ROW_TOP_YPIXEL (sheet, range.rowi)- area.y+
6927     yyy_row_height (sheet, range.rowi);
6928
6929   clip_area.x = sheet->row_title_area.width;
6930   clip_area.y = sheet->column_title_area.height;
6931   clip_area.width = sheet->sheet_window_width;
6932   clip_area.height = sheet->sheet_window_height;
6933
6934   if (!sheet->row_titles_visible) clip_area.x = 0;
6935   if (!sheet->column_titles_visible) clip_area.y = 0;
6936
6937   if (area.x < 0)
6938     {
6939       area.width = area.width + area.x;
6940       area.x = 0;
6941     }
6942   if (area.width > clip_area.width) area.width = clip_area.width + 10;
6943   if (area.y < 0)
6944     {
6945       area.height = area.height + area.y;
6946       area.y = 0;
6947     }
6948   if (area.height > clip_area.height) area.height = clip_area.height + 10;
6949
6950   clip_area.x--;
6951   clip_area.y--;
6952   clip_area.width += 3;
6953   clip_area.height += 3;
6954
6955   gdk_gc_get_values (sheet->xor_gc, &values);
6956
6957   gdk_gc_set_clip_rectangle (sheet->xor_gc, &clip_area);
6958
6959   for (i = -1; i <= 1; ++i)
6960     gdk_draw_rectangle (sheet->sheet_window,
6961                         sheet->xor_gc,
6962                         FALSE,
6963                         area.x + i, area.y + i,
6964                         area.width - 2 * i, area.height - 2 * i);
6965
6966
6967   gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
6968
6969   gdk_gc_set_foreground (sheet->xor_gc, &values.foreground);
6970
6971 }
6972
6973
6974 /* this function returns the new width of the column being resized given
6975  * the column and x position of the cursor; the x cursor position is passed
6976  * in as a pointer and automaticaly corrected if it's beyond min / max limits */
6977 static guint
6978 new_column_width (GtkSheet * sheet,
6979                   gint column,
6980                   gint * x)
6981 {
6982   gint cx, width;
6983   guint min_width;
6984
6985   cx = *x;
6986
6987   min_width = sheet->column_requisition;
6988
6989   /* you can't shrink a column to less than its minimum width */
6990   if (cx < COLUMN_LEFT_XPIXEL (sheet, column) + min_width)
6991     {
6992       *x = cx = COLUMN_LEFT_XPIXEL (sheet, column) + min_width;
6993     }
6994
6995   /* calculate new column width making sure it doesn't end up
6996    * less than the minimum width */
6997   width = cx - COLUMN_LEFT_XPIXEL (sheet, column);
6998   if (width < min_width)
6999     width = min_width;
7000
7001   xxx_set_column_width (sheet, column, width);
7002   size_allocate_column_title_buttons (sheet);
7003
7004   return width;
7005 }
7006
7007 /* this function returns the new height of the row being resized given
7008  * the row and y position of the cursor; the y cursor position is passed
7009  * in as a pointer and automaticaly corrected if it's beyond min / max limits */
7010 static guint
7011 new_row_height (GtkSheet * sheet,
7012                 gint row,
7013                 gint * y)
7014 {
7015   gint cy, height;
7016   guint min_height;
7017
7018   cy = *y;
7019   min_height = sheet->row_requisition;
7020
7021   /* you can't shrink a row to less than its minimum height */
7022   if (cy < ROW_TOP_YPIXEL (sheet, row) + min_height)
7023
7024     {
7025       *y = cy = ROW_TOP_YPIXEL (sheet, row) + min_height;
7026     }
7027
7028   /* calculate new row height making sure it doesn't end up
7029    * less than the minimum height */
7030   height = (cy - ROW_TOP_YPIXEL (sheet, row));
7031   if (height < min_height)
7032     height = min_height;
7033
7034   yyy_set_row_height (sheet, row, height);
7035   size_allocate_row_title_buttons (sheet);
7036
7037   return height;
7038 }
7039
7040 static void
7041 gtk_sheet_set_column_width (GtkSheet * sheet,
7042                             gint column,
7043                             guint width)
7044 {
7045   guint min_width;
7046
7047   g_return_if_fail (sheet != NULL);
7048   g_return_if_fail (GTK_IS_SHEET (sheet));
7049
7050   if (column < 0 || column >= xxx_column_count (sheet))
7051     return;
7052
7053   gtk_sheet_column_size_request (sheet, column, &min_width);
7054   if (width < min_width) return;
7055
7056   xxx_set_column_width (sheet, column, width);
7057
7058   if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) && !GTK_SHEET_IS_FROZEN (sheet))
7059     {
7060       size_allocate_column_title_buttons (sheet);
7061       adjust_scrollbars (sheet);
7062       gtk_sheet_size_allocate_entry (sheet);
7063       gtk_sheet_range_draw (sheet, NULL);
7064     }
7065
7066   g_signal_emit (sheet, sheet_signals[CHANGED], 0, -1, column);
7067 }
7068
7069
7070
7071 void
7072 gtk_sheet_set_row_height (GtkSheet * sheet,
7073                           gint row,
7074                           guint height)
7075 {
7076   guint min_height;
7077
7078   g_return_if_fail (sheet != NULL);
7079   g_return_if_fail (GTK_IS_SHEET (sheet));
7080
7081   if (row < 0 || row >= yyy_row_count (sheet))
7082     return;
7083
7084   gtk_sheet_row_size_request (sheet, row, &min_height);
7085   if (height < min_height) return;
7086
7087   yyy_set_row_height (sheet, row, height);
7088
7089   if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) && !GTK_SHEET_IS_FROZEN (sheet))
7090     {
7091       size_allocate_row_title_buttons (sheet);
7092       adjust_scrollbars (sheet);
7093       gtk_sheet_size_allocate_entry (sheet);
7094       gtk_sheet_range_draw (sheet, NULL);
7095     }
7096
7097   g_signal_emit (sheet, sheet_signals[CHANGED], 0, row, - 1);
7098 }
7099 gboolean
7100 gtk_sheet_get_attributes (const GtkSheet *sheet, gint row, gint col,
7101                           GtkSheetCellAttr *attributes)
7102 {
7103   const GdkColor *fg, *bg;
7104   const GtkJustification *j ;
7105   const PangoFontDescription *font_desc ;
7106   const GtkSheetCellBorder *border ;
7107
7108   g_return_val_if_fail (sheet != NULL, FALSE);
7109   g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE);
7110
7111   if (row < 0 || col < 0) return FALSE;
7112
7113   init_attributes (sheet, col, attributes);
7114
7115   if ( !sheet->model)
7116     return FALSE;
7117
7118   attributes->is_editable = g_sheet_model_is_editable (sheet->model, row, col);
7119   attributes->is_visible = g_sheet_model_is_visible (sheet->model, row, col);
7120
7121   fg = g_sheet_model_get_foreground (sheet->model, row, col);
7122   if ( fg )
7123     attributes->foreground = *fg;
7124
7125   bg = g_sheet_model_get_background (sheet->model, row, col);
7126   if ( bg )
7127     attributes->background = *bg;
7128
7129   j = g_sheet_model_get_justification (sheet->model, row, col);
7130   if (j) attributes->justification = *j;
7131
7132   font_desc = g_sheet_model_get_font_desc (sheet->model, row, col);
7133   if ( font_desc ) attributes->font_desc = font_desc;
7134
7135   border = g_sheet_model_get_cell_border (sheet->model, row, col);
7136
7137   if ( border ) attributes->border = *border;
7138
7139   return TRUE;
7140 }
7141
7142 static void
7143 init_attributes (const GtkSheet *sheet, gint col, GtkSheetCellAttr *attributes)
7144 {
7145   /* DEFAULT VALUES */
7146   attributes->foreground = GTK_WIDGET (sheet)->style->black;
7147   attributes->background = sheet->bg_color;
7148   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
7149     {
7150       GdkColormap *colormap;
7151       colormap = gdk_colormap_get_system ();
7152       attributes->background = sheet->bg_color;
7153     }
7154   attributes->justification = xxx_column_justification (sheet, col);
7155   attributes->border.width = 0;
7156   attributes->border.line_style = GDK_LINE_SOLID;
7157   attributes->border.cap_style = GDK_CAP_NOT_LAST;
7158   attributes->border.join_style = GDK_JOIN_MITER;
7159   attributes->border.mask = 0;
7160   attributes->border.color = GTK_WIDGET (sheet)->style->black;
7161   attributes->is_editable = TRUE;
7162   attributes->is_visible = TRUE;
7163   attributes->font_desc = GTK_WIDGET (sheet)->style->font_desc;
7164 }
7165
7166
7167 /********************************************************************
7168  * Container Functions:
7169  * gtk_sheet_add
7170  * gtk_sheet_put
7171  * gtk_sheet_attach
7172  * gtk_sheet_remove
7173  * gtk_sheet_move_child
7174  * gtk_sheet_position_child
7175  * gtk_sheet_position_children
7176  * gtk_sheet_realize_child
7177  * gtk_sheet_get_child_at
7178  ********************************************************************/
7179
7180 GtkSheetChild *
7181 gtk_sheet_put (GtkSheet *sheet, GtkWidget *child, gint x, gint y)
7182 {
7183   GtkRequisition child_requisition;
7184   GtkSheetChild *child_info;
7185
7186   g_return_val_if_fail (sheet != NULL, NULL);
7187   g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);
7188   g_return_val_if_fail (child != NULL, NULL);
7189   g_return_val_if_fail (child->parent == NULL, NULL);
7190
7191   child_info = g_new (GtkSheetChild, 1);
7192   child_info->widget = child;
7193   child_info->x = x;
7194   child_info->y = y;
7195   child_info->attached_to_cell = FALSE;
7196   child_info->floating = TRUE;
7197   child_info->xpadding = child_info->ypadding = 0;
7198   child_info->xexpand = child_info->yexpand = FALSE;
7199   child_info->xshrink = child_info->yshrink = FALSE;
7200   child_info->xfill = child_info->yfill = FALSE;
7201
7202   sheet->children = g_list_append (sheet->children, child_info);
7203
7204   gtk_widget_set_parent (child, GTK_WIDGET (sheet));
7205
7206   gtk_widget_size_request (child, &child_requisition);
7207
7208   if (GTK_WIDGET_VISIBLE (GTK_WIDGET (sheet)))
7209     {
7210       if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) &&
7211           (!GTK_WIDGET_REALIZED (child) || GTK_WIDGET_NO_WINDOW (child)))
7212         gtk_sheet_realize_child (sheet, child_info);
7213
7214       if (GTK_WIDGET_MAPPED (GTK_WIDGET (sheet)) &&
7215           !GTK_WIDGET_MAPPED (child))
7216         gtk_widget_map (child);
7217     }
7218
7219   gtk_sheet_position_child (sheet, child_info);
7220
7221   /* This will avoid drawing on the titles */
7222
7223   if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
7224     {
7225       if (sheet->row_titles_visible)
7226         gdk_window_show (sheet->row_title_window);
7227       if (sheet->column_titles_visible)
7228         gdk_window_show (sheet->column_title_window);
7229     }
7230
7231   return (child_info);
7232 }
7233
7234 void
7235 gtk_sheet_attach_floating (GtkSheet *sheet,
7236                            GtkWidget *widget,
7237                            gint row, gint col)
7238 {
7239   GdkRectangle area;
7240   GtkSheetChild *child;
7241
7242   if (row < 0 || col < 0)
7243     {
7244       gtk_sheet_button_attach (sheet, widget, row, col);
7245       return;
7246     }
7247
7248   gtk_sheet_get_cell_area (sheet, row, col, &area);
7249   child = gtk_sheet_put (sheet, widget, area.x, area.y);
7250   child->attached_to_cell = TRUE;
7251   child->row = row;
7252   child->col = col;
7253 }
7254
7255 void
7256 gtk_sheet_attach_default (GtkSheet *sheet,
7257                           GtkWidget *widget,
7258                           gint row, gint col)
7259 {
7260   if (row < 0 || col < 0)
7261     {
7262       gtk_sheet_button_attach (sheet, widget, row, col);
7263       return;
7264     }
7265
7266   gtk_sheet_attach (sheet, widget, row, col,
7267                     GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
7268 }
7269
7270 void
7271 gtk_sheet_attach (GtkSheet *sheet,
7272                   GtkWidget *widget,
7273                   gint row, gint col,
7274                   gint xoptions,
7275                   gint yoptions,
7276                   gint xpadding,
7277                   gint ypadding)
7278 {
7279   GdkRectangle area;
7280   GtkSheetChild *child = NULL;
7281
7282   if (row < 0 || col < 0)
7283     {
7284       gtk_sheet_button_attach (sheet, widget, row, col);
7285       return;
7286     }
7287
7288   child = g_new0 (GtkSheetChild, 1);
7289   child->attached_to_cell = TRUE;
7290   child->floating = FALSE;
7291   child->widget = widget;
7292   child->row = row;
7293   child->col = col;
7294   child->xpadding = xpadding;
7295   child->ypadding = ypadding;
7296   child->xexpand = (xoptions & GTK_EXPAND) != 0;
7297   child->yexpand = (yoptions & GTK_EXPAND) != 0;
7298   child->xshrink = (xoptions & GTK_SHRINK) != 0;
7299   child->yshrink = (yoptions & GTK_SHRINK) != 0;
7300   child->xfill = (xoptions & GTK_FILL) != 0;
7301   child->yfill = (yoptions & GTK_FILL) != 0;
7302
7303   sheet->children = g_list_append (sheet->children, child);
7304
7305   gtk_sheet_get_cell_area (sheet, row, col, &area);
7306
7307   child->x = area.x + child->xpadding;
7308   child->y = area.y + child->ypadding;
7309
7310   if (GTK_WIDGET_VISIBLE (GTK_WIDGET (sheet)))
7311     {
7312       if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) &&
7313           (!GTK_WIDGET_REALIZED (widget) || GTK_WIDGET_NO_WINDOW (widget)))
7314         gtk_sheet_realize_child (sheet, child);
7315
7316       if (GTK_WIDGET_MAPPED (GTK_WIDGET (sheet)) &&
7317           !GTK_WIDGET_MAPPED (widget))
7318         gtk_widget_map (widget);
7319     }
7320
7321   gtk_sheet_position_child (sheet, child);
7322
7323   /* This will avoid drawing on the titles */
7324
7325   if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
7326     {
7327       if (GTK_SHEET_ROW_TITLES_VISIBLE (sheet))
7328         gdk_window_show (sheet->row_title_window);
7329       if (GTK_SHEET_COL_TITLES_VISIBLE (sheet))
7330         gdk_window_show (sheet->column_title_window);
7331     }
7332
7333 }
7334
7335 void
7336 gtk_sheet_button_attach          (GtkSheet *sheet,
7337                                   GtkWidget *widget,
7338                                   gint row, gint col)
7339 {
7340   GtkSheetButton *button = 0;
7341   GtkSheetChild *child;
7342   GtkRequisition button_requisition;
7343
7344   if (row >= 0 && col >= 0) return;
7345   if (row < 0 && col < 0) return;
7346
7347   child = g_new (GtkSheetChild, 1);
7348   child->widget = widget;
7349   child->x = 0;
7350   child->y = 0;
7351   child->attached_to_cell = TRUE;
7352   child->floating = FALSE;
7353   child->row = row;
7354   child->col = col;
7355   child->xpadding = child->ypadding = 0;
7356   child->xshrink = child->yshrink = FALSE;
7357   child->xfill = child->yfill = FALSE;
7358
7359
7360   sheet->children = g_list_append (sheet->children, child);
7361
7362   gtk_sheet_button_size_request (sheet, button, &button_requisition);
7363
7364
7365   if (GTK_WIDGET_VISIBLE (GTK_WIDGET (sheet)))
7366     {
7367       if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) &&
7368           (!GTK_WIDGET_REALIZED (widget) || GTK_WIDGET_NO_WINDOW (widget)))
7369         gtk_sheet_realize_child (sheet, child);
7370
7371       if (GTK_WIDGET_MAPPED (GTK_WIDGET (sheet)) &&
7372           !GTK_WIDGET_MAPPED (widget))
7373         gtk_widget_map (widget);
7374     }
7375
7376   if (row == -1) size_allocate_column_title_buttons (sheet);
7377   if (col == -1) size_allocate_row_title_buttons (sheet);
7378
7379 }
7380
7381 static void
7382 label_size_request (GtkSheet *sheet, gchar *label, GtkRequisition *req)
7383 {
7384   gchar *words;
7385   gchar word[1000];
7386   gint n = 0;
7387   gint row_height = DEFAULT_ROW_HEIGHT (GTK_WIDGET (sheet)) - 2 * CELLOFFSET + 2;
7388
7389   req->height = 0;
7390   req->width = 0;
7391   words = label;
7392
7393   while (words && *words != '\0')
7394     {
7395       if (*words == '\n' || * (words + 1) == '\0')
7396         {
7397           req->height += row_height;
7398
7399           word[n] = '\0';
7400           req->width = MAX (req->width, STRING_WIDTH (GTK_WIDGET (sheet), GTK_WIDGET (sheet)->style->font_desc, word));
7401           n = 0;
7402         }
7403       else
7404         {
7405           word[n++] = *words;
7406         }
7407       words++;
7408     }
7409
7410   if (n > 0) req->height -= 2;
7411 }
7412
7413 static void
7414 gtk_sheet_button_size_request    (GtkSheet *sheet,
7415                                   const GtkSheetButton *button,
7416                                   GtkRequisition *button_requisition)
7417 {
7418   GtkRequisition requisition;
7419   GtkRequisition label_requisition;
7420
7421   if (gtk_sheet_autoresize (sheet) && button->label && strlen (button->label) > 0)
7422     {
7423       label_size_request (sheet, button->label, &label_requisition);
7424       label_requisition.width += 2 * CELLOFFSET;
7425       label_requisition.height += 2 * CELLOFFSET;
7426     }
7427   else
7428     {
7429       label_requisition.height = DEFAULT_ROW_HEIGHT (GTK_WIDGET (sheet));
7430       label_requisition.width = COLUMN_MIN_WIDTH;
7431     }
7432
7433   if (button->child)
7434     {
7435       gtk_widget_size_request (button->child->widget, &requisition);
7436       requisition.width += 2 * button->child->xpadding;
7437       requisition.height += 2 * button->child->ypadding;
7438       requisition.width += 2 * sheet->button->style->xthickness;
7439       requisition.height += 2 * sheet->button->style->ythickness;
7440     }
7441   else
7442     {
7443       requisition.height = DEFAULT_ROW_HEIGHT (GTK_WIDGET (sheet));
7444       requisition.width = COLUMN_MIN_WIDTH;
7445     }
7446
7447   *button_requisition = requisition;
7448   button_requisition->width = MAX (requisition.width, label_requisition.width);
7449   button_requisition->height = MAX (requisition.height, label_requisition.height);
7450
7451 }
7452
7453 static void
7454 gtk_sheet_row_size_request (GtkSheet *sheet,
7455                             gint row,
7456                             guint *requisition)
7457 {
7458   GtkRequisition button_requisition;
7459   GList *children;
7460
7461   gtk_sheet_button_size_request (sheet,
7462                                  yyy_row_button (sheet, row),
7463                                  &button_requisition);
7464
7465   *requisition = button_requisition.height;
7466
7467   children = sheet->children;
7468   while (children)
7469     {
7470       GtkSheetChild *child = (GtkSheetChild *)children->data;
7471       GtkRequisition child_requisition;
7472
7473       if (child->attached_to_cell && child->row == row && child->col != -1 && !child->floating && !child->yshrink)
7474         {
7475           gtk_widget_get_child_requisition (child->widget, &child_requisition);
7476
7477           if (child_requisition.height + 2 * child->ypadding > *requisition)
7478             *requisition = child_requisition.height + 2 * child->ypadding;
7479         }
7480       children = children->next;
7481     }
7482
7483   sheet->row_requisition = * requisition;
7484 }
7485
7486 static void
7487 gtk_sheet_column_size_request (GtkSheet *sheet,
7488                                gint col,
7489                                guint *requisition)
7490 {
7491   GtkRequisition button_requisition;
7492   GList *children;
7493   GtkSheetButton *button = xxx_column_button (sheet, col);
7494
7495   gtk_sheet_button_size_request (sheet,
7496                                  button,
7497                                  &button_requisition);
7498
7499   gtk_sheet_button_free (button);
7500
7501   *requisition = button_requisition.width;
7502
7503   children = sheet->children;
7504   while (children)
7505     {
7506       GtkSheetChild *child = (GtkSheetChild *)children->data;
7507       GtkRequisition child_requisition;
7508
7509       if (child->attached_to_cell && child->col == col && child->row != -1 && !child->floating && !child->xshrink)
7510         {
7511           gtk_widget_get_child_requisition (child->widget, &child_requisition);
7512
7513           if (child_requisition.width + 2 * child->xpadding > *requisition)
7514             *requisition = child_requisition.width + 2 * child->xpadding;
7515         }
7516       children = children->next;
7517     }
7518
7519   sheet->column_requisition = *requisition;
7520 }
7521
7522 void
7523 gtk_sheet_move_child (GtkSheet *sheet, GtkWidget *widget, gint x, gint y)
7524 {
7525   GtkSheetChild *child;
7526   GList *children;
7527
7528   g_return_if_fail (sheet != NULL);
7529   g_return_if_fail (GTK_IS_SHEET (sheet));
7530
7531   children = sheet->children;
7532   while (children)
7533     {
7534       child = children->data;
7535
7536       if (child->widget == widget)
7537         {
7538           child->x = x;
7539           child->y = y;
7540           child->row = ROW_FROM_YPIXEL (sheet, y);
7541           child->col = COLUMN_FROM_XPIXEL (sheet, x);
7542           gtk_sheet_position_child (sheet, child);
7543           return;
7544         }
7545
7546       children = children->next;
7547     }
7548
7549   g_warning ("Widget must be a GtkSheet child");
7550
7551 }
7552
7553 static void
7554 gtk_sheet_position_child (GtkSheet *sheet, GtkSheetChild *child)
7555 {
7556   GtkRequisition child_requisition;
7557   GtkAllocation child_allocation;
7558   gint xoffset = 0;
7559   gint yoffset = 0;
7560   gint x = 0, y = 0;
7561   GdkRectangle area;
7562
7563   gtk_widget_get_child_requisition (child->widget, &child_requisition);
7564
7565   if (sheet->column_titles_visible)
7566     yoffset = sheet->column_title_area.height;
7567
7568   if (sheet->row_titles_visible)
7569     xoffset = sheet->row_title_area.width;
7570
7571   if (child->attached_to_cell)
7572     {
7573       gtk_sheet_get_cell_area (sheet, child->row, child->col, &area);
7574       child->x = area.x + child->xpadding;
7575       child->y = area.y + child->ypadding;
7576
7577       if (!child->floating)
7578         {
7579           if (child_requisition.width + 2 * child->xpadding <= xxx_column_width (sheet, child->col))
7580             {
7581               if (child->xfill)
7582                 {
7583                   child_requisition.width = child_allocation.width = xxx_column_width (sheet, child->col) - 2 * child->xpadding;
7584                 }
7585               else
7586                 {
7587                   if (child->xexpand)
7588                     {
7589                       child->x = area.x + xxx_column_width (sheet, child->col) / 2 -
7590                         child_requisition.width / 2;
7591                     }
7592                   child_allocation.width = child_requisition.width;
7593                 }
7594             }
7595           else
7596             {
7597               if (!child->xshrink)
7598                 {
7599                   gtk_sheet_set_column_width (sheet, child->col, child_requisition.width + 2 * child->xpadding);
7600                 }
7601               child_allocation.width = xxx_column_width (sheet, child->col) - 2 * child->xpadding;
7602             }
7603
7604           if (child_requisition.height +
7605               2 * child->ypadding <= yyy_row_height (sheet, child->row))
7606             {
7607               if (child->yfill)
7608                 {
7609                   child_requisition.height = child_allocation.height =
7610                     yyy_row_height (sheet, child->row) - 2 * child->ypadding;
7611                 }
7612               else
7613                 {
7614                   if (child->yexpand)
7615                     {
7616                       child->y = area.y + yyy_row_height (sheet, child->row) / 2
7617                         - child_requisition.height / 2;
7618                     }
7619                   child_allocation.height = child_requisition.height;
7620                 }
7621             }
7622           else
7623             {
7624               if (!child->yshrink)
7625                 {
7626                   gtk_sheet_set_row_height (sheet, child->row, child_requisition.height + 2 * child->ypadding);
7627                 }
7628               child_allocation.height = yyy_row_height (sheet, child->row) -
7629                 2 * child->ypadding;
7630             }
7631         }
7632       else
7633         {
7634           child_allocation.width = child_requisition.width;
7635           child_allocation.height = child_requisition.height;
7636         }
7637
7638       x = child_allocation.x = child->x + xoffset;
7639       y = child_allocation.y = child->y + yoffset;
7640     }
7641   else
7642     {
7643       x = child_allocation.x = child->x + sheet->hoffset + xoffset;
7644       x = child_allocation.x = child->x + xoffset;
7645       y = child_allocation.y = child->y + sheet->voffset + yoffset;
7646       y = child_allocation.y = child->y + yoffset;
7647       child_allocation.width = child_requisition.width;
7648       child_allocation.height = child_requisition.height;
7649     }
7650
7651   gtk_widget_size_allocate (child->widget, &child_allocation);
7652   gtk_widget_queue_draw (child->widget);
7653 }
7654
7655 static void
7656 gtk_sheet_forall (GtkContainer *container,
7657                   gboolean include_internals,
7658                   GtkCallback callback,
7659                   gpointer callback_data)
7660 {
7661   GtkSheet *sheet;
7662   GtkSheetChild *child;
7663   GList *children;
7664
7665   g_return_if_fail (GTK_IS_SHEET (container));
7666   g_return_if_fail (callback != NULL);
7667
7668   sheet = GTK_SHEET (container);
7669   children = sheet->children;
7670   while (children)
7671     {
7672       child = children->data;
7673       children = children->next;
7674
7675       (* callback) (child->widget, callback_data);
7676     }
7677
7678   if (sheet->button && sheet->button->parent)
7679     (* callback) (sheet->button, callback_data);
7680
7681   if (sheet->entry_container && GTK_IS_CONTAINER (sheet->entry_container))
7682     (* callback) (sheet->entry_container, callback_data);
7683 }
7684
7685
7686 static void
7687 gtk_sheet_position_children (GtkSheet *sheet)
7688 {
7689   GList *children;
7690   GtkSheetChild *child;
7691
7692   children = sheet->children;
7693
7694   while (children)
7695     {
7696       child = (GtkSheetChild *)children->data;
7697
7698       if (child->col != -1 && child->row != -1)
7699         gtk_sheet_position_child (sheet, child);
7700
7701       if (child->row == -1)
7702         {
7703           if (child->col < MIN_VISIBLE_COLUMN (sheet) ||
7704               child->col > MAX_VISIBLE_COLUMN (sheet))
7705             gtk_sheet_child_hide (child);
7706           else
7707             gtk_sheet_child_show (child);
7708         }
7709       if (child->col == -1)
7710         {
7711           if (child->row < MIN_VISIBLE_ROW (sheet) ||
7712               child->row > MAX_VISIBLE_ROW (sheet))
7713             gtk_sheet_child_hide (child);
7714           else
7715             gtk_sheet_child_show (child);
7716         }
7717
7718       children = children->next;
7719     }
7720 }
7721
7722 static void
7723 gtk_sheet_remove (GtkContainer *container, GtkWidget *widget)
7724 {
7725   GtkSheet *sheet;
7726   GList *children;
7727   GtkSheetChild *child = 0;
7728
7729   g_return_if_fail (container != NULL);
7730   g_return_if_fail (GTK_IS_SHEET (container));
7731
7732   sheet = GTK_SHEET (container);
7733
7734   children = sheet->children;
7735
7736   while (children)
7737     {
7738       child = (GtkSheetChild *)children->data;
7739
7740       if (child->widget == widget) break;
7741
7742       children = children->next;
7743     }
7744
7745   if (children)
7746     {
7747       gtk_widget_unparent (widget);
7748       child->widget = NULL;
7749
7750       sheet->children = g_list_remove_link (sheet->children, children);
7751       g_list_free_1 (children);
7752       g_free (child);
7753     }
7754
7755   gtk_widget_unparent (sheet->button);
7756 }
7757
7758 static void
7759 gtk_sheet_realize_child (GtkSheet *sheet, GtkSheetChild *child)
7760 {
7761   GtkWidget *widget;
7762
7763   widget = GTK_WIDGET (sheet);
7764
7765   if (GTK_WIDGET_REALIZED (widget))
7766     {
7767       if (child->row == -1)
7768         gtk_widget_set_parent_window (child->widget, sheet->column_title_window);
7769       else if (child->col == -1)
7770         gtk_widget_set_parent_window (child->widget, sheet->row_title_window);
7771       else
7772         gtk_widget_set_parent_window (child->widget, sheet->sheet_window);
7773     }
7774
7775   gtk_widget_set_parent (child->widget, widget);
7776 }
7777
7778
7779
7780 GtkSheetChild *
7781 gtk_sheet_get_child_at (GtkSheet *sheet, gint row, gint col)
7782 {
7783   GList *children;
7784   GtkSheetChild *child = 0;
7785
7786   g_return_val_if_fail (sheet != NULL, NULL);
7787   g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);
7788
7789   children = sheet->children;
7790
7791   while (children)
7792     {
7793       child = (GtkSheetChild *)children->data;
7794
7795       if (child->attached_to_cell)
7796         if (child->row == row && child->col == col) break;
7797
7798       children = children->next;
7799     }
7800
7801   if (children) return child;
7802
7803   return NULL;
7804 }
7805
7806 static void
7807 gtk_sheet_child_hide (GtkSheetChild *child)
7808 {
7809   g_return_if_fail (child != NULL);
7810   gtk_widget_hide (child->widget);
7811 }
7812
7813 static void
7814 gtk_sheet_child_show (GtkSheetChild *child)
7815 {
7816   g_return_if_fail (child != NULL);
7817
7818   gtk_widget_show (child->widget);
7819 }
7820
7821 GSheetModel *
7822 gtk_sheet_get_model (const GtkSheet *sheet)
7823 {
7824   g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);
7825
7826   return sheet->model;
7827 }
7828
7829
7830 GtkSheetButton *
7831 gtk_sheet_button_new (void)
7832 {
7833   GtkSheetButton *button = g_malloc (sizeof (GtkSheetButton));
7834
7835   button->state = GTK_STATE_NORMAL;
7836   button->label = NULL;
7837   button->label_visible = TRUE;
7838   button->child = NULL;
7839   button->justification = GTK_JUSTIFY_FILL;
7840
7841   return button;
7842 }
7843
7844
7845 void
7846 gtk_sheet_button_free (GtkSheetButton *button)
7847 {
7848   if (!button) return ;
7849
7850   g_free (button->label);
7851   g_free (button);
7852 }
7853
7854
7855 static void
7856 append_cell_text (GString *string, const GtkSheet *sheet, gint r, gint c)
7857 {
7858   gchar *celltext = gtk_sheet_cell_get_text (sheet, r, c);
7859
7860   if ( NULL == celltext)
7861     return;
7862
7863   g_string_append (string, celltext);
7864   g_free (celltext);
7865 }
7866
7867 static GString *
7868 range_to_text (const GtkSheet *sheet)
7869 {
7870   gint r, c;
7871   GString *string;
7872
7873   if ( !gtk_sheet_range_isvisible (sheet, sheet->range))
7874     return NULL;
7875
7876   string = g_string_sized_new (80);
7877
7878   for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
7879     {
7880       for (c = sheet->range.col0; c < sheet->range.coli; ++c)
7881         {
7882           append_cell_text (string, sheet, r, c);
7883           g_string_append (string, "\t");
7884         }
7885       append_cell_text (string, sheet, r, c);
7886       if ( r < sheet->range.rowi)
7887         g_string_append (string, "\n");
7888     }
7889
7890   return string;
7891 }
7892
7893 static GString *
7894 range_to_html (const GtkSheet *sheet)
7895 {
7896   gint r, c;
7897   GString *string;
7898
7899   if ( !gtk_sheet_range_isvisible (sheet, sheet->range))
7900     return NULL;
7901
7902   string = g_string_sized_new (480);
7903
7904   g_string_append (string, "<html>\n");
7905   g_string_append (string, "<body>\n");
7906   g_string_append (string, "<table>\n");
7907   for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
7908     {
7909       g_string_append (string, "<tr>\n");
7910       for (c = sheet->range.col0; c <= sheet->range.coli; ++c)
7911         {
7912           g_string_append (string, "<td>");
7913           append_cell_text (string, sheet, r, c);
7914           g_string_append (string, "</td>\n");
7915         }
7916       g_string_append (string, "</tr>\n");
7917     }
7918   g_string_append (string, "</table>\n");
7919   g_string_append (string, "</body>\n");
7920   g_string_append (string, "</html>\n");
7921
7922   return string;
7923 }
7924
7925 enum {
7926   SELECT_FMT_NULL,
7927   SELECT_FMT_TEXT,
7928   SELECT_FMT_HTML
7929 };
7930
7931 static void
7932 primary_get_cb (GtkClipboard     *clipboard,
7933                 GtkSelectionData *selection_data,
7934                 guint             info,
7935                 gpointer          data)
7936 {
7937   GtkSheet *sheet = GTK_SHEET (data);
7938   GString *string = NULL;
7939
7940   switch (info)
7941     {
7942     case SELECT_FMT_TEXT:
7943       string = range_to_text (sheet);
7944       break;
7945     case SELECT_FMT_HTML:
7946       string = range_to_html (sheet);
7947       break;
7948     default:
7949       g_assert_not_reached ();
7950     }
7951
7952   gtk_selection_data_set (selection_data, selection_data->target,
7953                           8,
7954                           (const guchar *) string->str, string->len);
7955   g_string_free (string, TRUE);
7956 }
7957
7958 static void
7959 primary_clear_cb (GtkClipboard *clipboard,
7960                   gpointer      data)
7961 {
7962   GtkSheet *sheet = GTK_SHEET (data);
7963   if ( ! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
7964     return;
7965
7966   gtk_sheet_real_unselect_range (sheet, NULL);
7967 }
7968
7969 static void
7970 gtk_sheet_update_primary_selection (GtkSheet *sheet)
7971 {
7972   static const GtkTargetEntry targets[] = {
7973     { "UTF8_STRING",   0, SELECT_FMT_TEXT },
7974     { "STRING",        0, SELECT_FMT_TEXT },
7975     { "TEXT",          0, SELECT_FMT_TEXT },
7976     { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
7977     { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
7978     { "text/plain",    0, SELECT_FMT_TEXT },
7979     { "text/html",     0, SELECT_FMT_HTML }
7980   };
7981
7982   GtkClipboard *clipboard;
7983
7984   if (!GTK_WIDGET_REALIZED (sheet))
7985     return;
7986
7987   clipboard = gtk_widget_get_clipboard (GTK_WIDGET (sheet),
7988                                         GDK_SELECTION_PRIMARY);
7989
7990   if (gtk_sheet_range_isvisible (sheet, sheet->range))
7991     {
7992       if (!gtk_clipboard_set_with_owner (clipboard, targets,
7993                                          G_N_ELEMENTS (targets),
7994                                          primary_get_cb, primary_clear_cb,
7995                                          G_OBJECT (sheet)))
7996         primary_clear_cb (clipboard, sheet);
7997     }
7998   else
7999     {
8000       if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (sheet))
8001         gtk_clipboard_clear (clipboard);
8002     }
8003 }
8004