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