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