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