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