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