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