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