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