0e91dd0cb4ddbc661deff15c925f748c61e16355
[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->sheet_entry = NULL;
1267   sheet->pixmap = NULL;
1268
1269   sheet->range.row0 = 0;
1270   sheet->range.rowi = 0;
1271   sheet->range.col0 = 0;
1272   sheet->range.coli = 0;
1273
1274   sheet->state = GTK_SHEET_NORMAL;
1275
1276   sheet->sheet_window = NULL;
1277   sheet->sheet_window_width = 0;
1278   sheet->sheet_window_height = 0;
1279   sheet->sheet_entry = 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->sheet_entry && GTK_WIDGET_MAPPED (sheet->sheet_entry))
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   /* destroy the entry */
2419   if (sheet->sheet_entry && GTK_IS_WIDGET (sheet->sheet_entry))
2420     {
2421       gtk_widget_destroy (sheet->sheet_entry);
2422       sheet->sheet_entry = NULL;
2423     }
2424
2425   g_object_unref (sheet->button);
2426
2427   /* unref adjustments */
2428   if (sheet->hadjustment)
2429     {
2430       g_signal_handlers_disconnect_matched (sheet->hadjustment,
2431                                             G_SIGNAL_MATCH_DATA,
2432                                             0, 0, 0, 0,
2433                                             sheet);
2434
2435       g_object_unref (sheet->hadjustment);
2436       sheet->hadjustment = NULL;
2437     }
2438
2439   if (sheet->vadjustment)
2440     {
2441       g_signal_handlers_disconnect_matched (sheet->vadjustment,
2442                                             G_SIGNAL_MATCH_DATA,
2443                                             0, 0, 0, 0,
2444                                             sheet);
2445
2446       g_object_unref (sheet->vadjustment);
2447
2448       sheet->vadjustment = NULL;
2449     }
2450
2451   children = sheet->children;
2452   while (children)
2453     {
2454       GtkSheetChild *child = (GtkSheetChild *)children->data;
2455       if (child && child->widget)
2456         gtk_sheet_remove (GTK_CONTAINER (sheet), child->widget);
2457       children = sheet->children;
2458     }
2459   sheet->children = NULL;
2460
2461   if (G_OBJECT_CLASS (parent_class)->dispose)
2462     (*G_OBJECT_CLASS (parent_class)->dispose) (object);
2463 }
2464
2465 static void
2466 gtk_sheet_style_set (GtkWidget *widget,
2467                      GtkStyle *previous_style)
2468 {
2469   GtkSheet *sheet;
2470
2471   g_return_if_fail (widget != NULL);
2472   g_return_if_fail (GTK_IS_SHEET (widget));
2473
2474   if (GTK_WIDGET_CLASS (parent_class)->style_set)
2475     (*GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous_style);
2476
2477   sheet = GTK_SHEET (widget);
2478
2479   if (GTK_WIDGET_REALIZED (widget))
2480     {
2481       gtk_style_set_background (widget->style, widget->window, widget->state);
2482     }
2483
2484 }
2485
2486 static void
2487 gtk_sheet_realize (GtkWidget * widget)
2488 {
2489   GtkSheet *sheet;
2490   GdkWindowAttr attributes;
2491   gint attributes_mask;
2492   GdkGCValues values, auxvalues;
2493   GdkColormap *colormap;
2494   GtkSheetChild *child;
2495   GList *children;
2496
2497   g_return_if_fail (widget != NULL);
2498   g_return_if_fail (GTK_IS_SHEET (widget));
2499
2500   sheet = GTK_SHEET (widget);
2501
2502   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
2503
2504   attributes.window_type = GDK_WINDOW_CHILD;
2505   attributes.x = widget->allocation.x;
2506   attributes.y = widget->allocation.y;
2507   attributes.width = widget->allocation.width;
2508   attributes.height = widget->allocation.height;
2509   attributes.wclass = GDK_INPUT_OUTPUT;
2510
2511   attributes.visual = gtk_widget_get_visual (widget);
2512   attributes.colormap = gtk_widget_get_colormap (widget);
2513
2514   attributes.event_mask = gtk_widget_get_events (widget);
2515   attributes.event_mask |= (GDK_EXPOSURE_MASK |
2516                             GDK_BUTTON_PRESS_MASK |
2517                             GDK_BUTTON_RELEASE_MASK |
2518                             GDK_KEY_PRESS_MASK |
2519                             GDK_POINTER_MOTION_MASK |
2520                             GDK_POINTER_MOTION_HINT_MASK);
2521   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP |
2522     GDK_WA_CURSOR;
2523
2524   attributes.cursor = gdk_cursor_new (GDK_TOP_LEFT_ARROW);
2525
2526   /* main window */
2527   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
2528
2529   gdk_window_set_user_data (widget->window, sheet);
2530
2531   widget->style = gtk_style_attach (widget->style, widget->window);
2532
2533   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
2534
2535   attributes.x = 0;
2536   if (sheet->row_titles_visible)
2537     attributes.x = sheet->row_title_area.width;
2538   attributes.y = 0;
2539   attributes.width = sheet->column_title_area.width;
2540   attributes.height = sheet->column_title_area.height;
2541
2542   /* column - title window */
2543   sheet->column_title_window = gdk_window_new (widget->window, &attributes, attributes_mask);
2544   gdk_window_set_user_data (sheet->column_title_window, sheet);
2545   gtk_style_set_background (widget->style, sheet->column_title_window, GTK_STATE_NORMAL);
2546
2547   attributes.x = 0;
2548   attributes.y = 0;
2549   if (sheet->column_titles_visible)
2550     attributes.y = sheet->column_title_area.height;
2551   attributes.width = sheet->row_title_area.width;
2552   attributes.height = sheet->row_title_area.height;
2553
2554   /* row - title window */
2555   sheet->row_title_window = gdk_window_new (widget->window, &attributes, attributes_mask);
2556   gdk_window_set_user_data (sheet->row_title_window, sheet);
2557   gtk_style_set_background (widget->style, sheet->row_title_window, GTK_STATE_NORMAL);
2558
2559   /* sheet - window */
2560   attributes.cursor = gdk_cursor_new (GDK_PLUS);
2561
2562   attributes.x = 0;
2563   attributes.y = 0;
2564   attributes.width = sheet->sheet_window_width,
2565     attributes.height = sheet->sheet_window_height;
2566
2567   sheet->sheet_window = gdk_window_new (widget->window, &attributes, attributes_mask);
2568   gdk_window_set_user_data (sheet->sheet_window, sheet);
2569
2570   gdk_cursor_unref (attributes.cursor);
2571
2572   gdk_window_set_background (sheet->sheet_window, &widget->style->white);
2573   gdk_window_show (sheet->sheet_window);
2574
2575   /* backing_pixmap */
2576   gtk_sheet_make_backing_pixmap (sheet, 0, 0);
2577
2578   /* GCs */
2579   if (sheet->fg_gc)
2580     gdk_gc_unref (sheet->fg_gc);
2581   if (sheet->bg_gc)
2582     gdk_gc_unref (sheet->bg_gc);
2583   sheet->fg_gc = gdk_gc_new (widget->window);
2584   sheet->bg_gc = gdk_gc_new (widget->window);
2585
2586   colormap = gtk_widget_get_colormap (widget);
2587
2588   gdk_color_white (colormap, &widget->style->white);
2589   gdk_color_black (colormap, &widget->style->black);
2590
2591   gdk_gc_get_values (sheet->fg_gc, &auxvalues);
2592
2593   values.foreground = widget->style->white;
2594   values.function = GDK_INVERT;
2595   values.subwindow_mode = GDK_INCLUDE_INFERIORS;
2596   if (sheet->xor_gc)
2597     gdk_gc_unref (sheet->xor_gc);
2598   sheet->xor_gc = gdk_gc_new_with_values (widget->window,
2599                                           &values,
2600                                           GDK_GC_FOREGROUND |
2601                                           GDK_GC_FUNCTION |
2602                                           GDK_GC_SUBWINDOW);
2603
2604   if (sheet->sheet_entry->parent)
2605     {
2606       gtk_widget_ref (sheet->sheet_entry);
2607       gtk_widget_unparent (sheet->sheet_entry);
2608     }
2609   gtk_widget_set_parent_window (sheet->sheet_entry, sheet->sheet_window);
2610   gtk_widget_set_parent (sheet->sheet_entry, GTK_WIDGET (sheet));
2611
2612   gtk_widget_set_parent_window (sheet->button, sheet->sheet_window);
2613   gtk_widget_set_parent (sheet->button, GTK_WIDGET (sheet));
2614
2615   if (!sheet->cursor_drag)
2616     sheet->cursor_drag = gdk_cursor_new (GDK_PLUS);
2617
2618   if (sheet->column_titles_visible)
2619     gdk_window_show (sheet->column_title_window);
2620   if (sheet->row_titles_visible)
2621     gdk_window_show (sheet->row_title_window);
2622
2623   size_allocate_row_title_buttons (sheet);
2624   size_allocate_column_title_buttons (sheet);
2625
2626   children = sheet->children;
2627   while (children)
2628     {
2629       child = children->data;
2630       children = children->next;
2631
2632       gtk_sheet_realize_child (sheet, child);
2633     }
2634
2635   gtk_sheet_update_primary_selection (sheet);
2636 }
2637
2638 static void
2639 create_global_button (GtkSheet *sheet)
2640 {
2641   sheet->button = gtk_button_new_with_label (" ");
2642
2643   g_object_ref_sink (sheet->button);
2644
2645   g_signal_connect (sheet->button,
2646                     "pressed",
2647                     G_CALLBACK (global_button_clicked),
2648                     sheet);
2649 }
2650
2651 static void
2652 size_allocate_global_button (GtkSheet *sheet)
2653 {
2654   GtkAllocation allocation;
2655
2656   if (!sheet->column_titles_visible) return;
2657   if (!sheet->row_titles_visible) return;
2658
2659   gtk_widget_size_request (sheet->button, NULL);
2660
2661   allocation.x = 0;
2662   allocation.y = 0;
2663   allocation.width = sheet->row_title_area.width;
2664   allocation.height = sheet->column_title_area.height;
2665
2666   gtk_widget_size_allocate (sheet->button, &allocation);
2667   gtk_widget_show (sheet->button);
2668 }
2669
2670 static void
2671 global_button_clicked (GtkWidget *widget, gpointer data)
2672 {
2673   gboolean veto;
2674
2675   gtk_sheet_click_cell (GTK_SHEET (data), - 1, - 1, &veto);
2676   gtk_widget_grab_focus (GTK_WIDGET (data));
2677 }
2678
2679
2680 static void
2681 gtk_sheet_unrealize (GtkWidget * widget)
2682 {
2683   GtkSheet *sheet;
2684
2685   g_return_if_fail (widget != NULL);
2686   g_return_if_fail (GTK_IS_SHEET (widget));
2687
2688   sheet = GTK_SHEET (widget);
2689
2690   gdk_cursor_destroy (sheet->cursor_drag);
2691
2692   gdk_gc_destroy (sheet->xor_gc);
2693   gdk_gc_destroy (sheet->fg_gc);
2694   gdk_gc_destroy (sheet->bg_gc);
2695
2696   gdk_window_destroy (sheet->sheet_window);
2697   gdk_window_destroy (sheet->column_title_window);
2698   gdk_window_destroy (sheet->row_title_window);
2699
2700   if (sheet->pixmap)
2701     {
2702       g_object_unref (sheet->pixmap);
2703       sheet->pixmap = NULL;
2704     }
2705
2706   sheet->column_title_window = NULL;
2707   sheet->sheet_window = NULL;
2708   sheet->cursor_drag = NULL;
2709   sheet->xor_gc = NULL;
2710   sheet->fg_gc = NULL;
2711   sheet->bg_gc = NULL;
2712
2713   if (GTK_WIDGET_CLASS (parent_class)->unrealize)
2714     (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
2715 }
2716
2717 static void
2718 gtk_sheet_map (GtkWidget * widget)
2719 {
2720   GtkSheet *sheet = GTK_SHEET (widget);
2721   GtkSheetChild *child;
2722   GList *children;
2723
2724   g_return_if_fail (widget != NULL);
2725   g_return_if_fail (GTK_IS_SHEET (widget));
2726
2727   if (!GTK_WIDGET_MAPPED (widget))
2728     {
2729       GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
2730
2731       if (!sheet->cursor_drag) sheet->cursor_drag = gdk_cursor_new (GDK_PLUS);
2732
2733       gdk_window_show (widget->window);
2734
2735       gdk_window_show (sheet->sheet_window);
2736
2737       if (sheet->column_titles_visible)
2738         {
2739           size_allocate_column_title_buttons (sheet);
2740           gdk_window_show (sheet->column_title_window);
2741         }
2742       if (sheet->row_titles_visible)
2743         {
2744           size_allocate_row_title_buttons (sheet);
2745           gdk_window_show (sheet->row_title_window);
2746         }
2747
2748       if (!GTK_WIDGET_MAPPED (sheet->sheet_entry)
2749           && sheet->active_cell.row >= 0
2750           && sheet->active_cell.col >= 0 )
2751         {
2752           gtk_widget_show (sheet->sheet_entry);
2753           gtk_widget_map (sheet->sheet_entry);
2754         }
2755
2756       if (GTK_WIDGET_VISIBLE (sheet->button) &&
2757           !GTK_WIDGET_MAPPED (sheet->button))
2758         {
2759           gtk_widget_show (sheet->button);
2760           gtk_widget_map (sheet->button);
2761         }
2762
2763       if (GTK_BIN (sheet->button)->child)
2764         if (GTK_WIDGET_VISIBLE (GTK_BIN (sheet->button)->child) &&
2765             !GTK_WIDGET_MAPPED (GTK_BIN (sheet->button)->child))
2766           gtk_widget_map (GTK_BIN (sheet->button)->child);
2767
2768       gtk_sheet_range_draw (sheet, NULL);
2769       gtk_sheet_activate_cell (sheet,
2770                                sheet->active_cell.row,
2771                                sheet->active_cell.col);
2772
2773       children = sheet->children;
2774       while (children)
2775         {
2776           child = children->data;
2777           children = children->next;
2778
2779           if (GTK_WIDGET_VISIBLE (child->widget) &&
2780               !GTK_WIDGET_MAPPED (child->widget))
2781             {
2782               gtk_widget_map (child->widget);
2783               gtk_sheet_position_child (sheet, child);
2784             }
2785         }
2786
2787     }
2788 }
2789
2790 static void
2791 gtk_sheet_unmap (GtkWidget * widget)
2792 {
2793   GtkSheet *sheet;
2794   GtkSheetChild *child;
2795   GList *children;
2796
2797   g_return_if_fail (widget != NULL);
2798   g_return_if_fail (GTK_IS_SHEET (widget));
2799
2800   sheet = GTK_SHEET (widget);
2801
2802   if (GTK_WIDGET_MAPPED (widget))
2803     {
2804       GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
2805
2806       gdk_window_hide (sheet->sheet_window);
2807       if (sheet->column_titles_visible)
2808         gdk_window_hide (sheet->column_title_window);
2809       if (sheet->row_titles_visible)
2810         gdk_window_hide (sheet->row_title_window);
2811       gdk_window_hide (widget->window);
2812
2813       if (GTK_WIDGET_MAPPED (sheet->sheet_entry))
2814         gtk_widget_unmap (sheet->sheet_entry);
2815
2816       if (GTK_WIDGET_MAPPED (sheet->button))
2817         gtk_widget_unmap (sheet->button);
2818
2819       children = sheet->children;
2820       while (children)
2821         {
2822           child = children->data;
2823           children = children->next;
2824
2825           if (GTK_WIDGET_VISIBLE (child->widget) &&
2826               GTK_WIDGET_MAPPED (child->widget))
2827             {
2828               gtk_widget_unmap (child->widget);
2829             }
2830         }
2831
2832     }
2833 }
2834
2835
2836 static void
2837 gtk_sheet_cell_draw_default (GtkSheet *sheet, gint row, gint col)
2838 {
2839   GtkWidget *widget;
2840   GdkGC *fg_gc, *bg_gc;
2841   GtkSheetCellAttr attributes;
2842   GdkRectangle area;
2843
2844   g_return_if_fail (sheet != NULL);
2845
2846   /* bail now if we arn't drawable yet */
2847   if (!GTK_WIDGET_DRAWABLE (sheet)) return;
2848
2849   if (row < 0 || row >= yyy_row_count (sheet)) return;
2850   if (col < 0 || col >= xxx_column_count (sheet)) return;
2851   if (! xxx_column_is_visible (sheet, col)) return;
2852   if (! yyy_row_is_visible (sheet, row)) return;
2853
2854   widget = GTK_WIDGET (sheet);
2855
2856   gtk_sheet_get_attributes (sheet, row, col, &attributes);
2857
2858   /* select GC for background rectangle */
2859   gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground);
2860   gdk_gc_set_foreground (sheet->bg_gc, &attributes.background);
2861
2862   fg_gc = sheet->fg_gc;
2863   bg_gc = sheet->bg_gc;
2864
2865   area.x = COLUMN_LEFT_XPIXEL (sheet, col);
2866   area.y = ROW_TOP_YPIXEL (sheet, row);
2867   area.width= xxx_column_width (sheet, col);
2868   area.height = yyy_row_height (sheet, row);
2869
2870   gdk_draw_rectangle (sheet->pixmap,
2871                       bg_gc,
2872                       TRUE,
2873                       area.x,
2874                       area.y,
2875                       area.width,
2876                       area.height);
2877
2878   gdk_gc_set_line_attributes (sheet->fg_gc, 1, 0, 0, 0);
2879
2880   if (sheet->show_grid)
2881     {
2882       gdk_gc_set_foreground (sheet->bg_gc, &sheet->grid_color);
2883
2884       gdk_draw_rectangle (sheet->pixmap,
2885                           sheet->bg_gc,
2886                           FALSE,
2887                           area.x, area.y,
2888                           area.width, area.height);
2889     }
2890 }
2891
2892 static void
2893 gtk_sheet_cell_draw_label (GtkSheet *sheet, gint row, gint col)
2894 {
2895   GtkWidget *widget;
2896   GdkRectangle area;
2897   gint i;
2898   gint text_width, text_height, y;
2899   gint xoffset = 0;
2900   gint size, sizel, sizer;
2901   GdkGC *fg_gc, *bg_gc;
2902   GtkSheetCellAttr attributes;
2903   PangoLayout *layout;
2904   PangoRectangle rect;
2905   PangoRectangle logical_rect;
2906   PangoLayoutLine *line;
2907   PangoFontMetrics *metrics;
2908   PangoContext *context = gtk_widget_get_pango_context (GTK_WIDGET (sheet));
2909   gint ascent, descent, y_pos;
2910
2911   gchar *label;
2912
2913   g_return_if_fail (sheet != NULL);
2914
2915   /* bail now if we aren't drawable yet */
2916   if (!GTK_WIDGET_DRAWABLE (sheet))
2917     return;
2918
2919   label = gtk_sheet_cell_get_text (sheet, row, col);
2920   if (!label)
2921     return;
2922
2923   if (row < 0 || row >= yyy_row_count (sheet)) return;
2924   if (col < 0 || col >= xxx_column_count (sheet)) return;
2925   if (! xxx_column_is_visible (sheet, col)) return;
2926   if (!yyy_row_is_visible (sheet, row)) return;
2927
2928
2929   widget = GTK_WIDGET (sheet);
2930
2931   gtk_sheet_get_attributes (sheet, row, col, &attributes);
2932
2933   /* select GC for background rectangle */
2934   gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground);
2935   gdk_gc_set_foreground (sheet->bg_gc, &attributes.background);
2936
2937   fg_gc = sheet->fg_gc;
2938   bg_gc = sheet->bg_gc;
2939
2940   area.x = COLUMN_LEFT_XPIXEL (sheet, col);
2941   area.y = ROW_TOP_YPIXEL (sheet, row);
2942   area.width = xxx_column_width (sheet, col);
2943   area.height = yyy_row_height (sheet, row);
2944
2945
2946   layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), label);
2947   dispose_string (sheet, label);
2948   pango_layout_set_font_description (layout, attributes.font_desc);
2949
2950   pango_layout_get_pixel_extents (layout, NULL, &rect);
2951
2952   line = pango_layout_get_lines (layout)->data;
2953   pango_layout_line_get_extents (line, NULL, &logical_rect);
2954
2955   metrics = pango_context_get_metrics (context,
2956                                        attributes.font_desc,
2957                                        pango_context_get_language (context));
2958
2959   ascent = pango_font_metrics_get_ascent (metrics) / PANGO_SCALE;
2960   descent = pango_font_metrics_get_descent (metrics) / PANGO_SCALE;
2961
2962   pango_font_metrics_unref (metrics);
2963
2964   /* Align primarily for locale's ascent / descent */
2965
2966   logical_rect.height /= PANGO_SCALE;
2967   logical_rect.y /= PANGO_SCALE;
2968   y_pos = area.height - logical_rect.height;
2969
2970   if (logical_rect.height > area.height)
2971     y_pos = (logical_rect.height - area.height - 2 * CELLOFFSET) / 2;
2972   else if (y_pos < 0)
2973     y_pos = 0;
2974   else if (y_pos + logical_rect.height > area.height)
2975     y_pos = area.height - logical_rect.height;
2976
2977   text_width = rect.width;
2978   text_height = rect.height;
2979   y = area.y + y_pos - CELLOFFSET;
2980
2981   switch (attributes.justification)
2982     {
2983     case GTK_JUSTIFY_RIGHT:
2984       size = area.width;
2985       area.x +=area.width;
2986       {
2987         for (i = col - 1; i >= MIN_VISIBLE_COLUMN (sheet); i--)
2988           {
2989             if ( !gtk_sheet_cell_empty (sheet, row, i)) break;
2990             if (size >= text_width + CELLOFFSET) break;
2991             size +=xxx_column_width (sheet, i);
2992             xxx_column_set_right_column (sheet, i,
2993                                          MAX (col,
2994                                               xxx_column_right_column (sheet, i)));
2995           }
2996         area.width = size;
2997       }
2998       area.x -= size;
2999       xoffset += area.width - text_width - 2 * CELLOFFSET -
3000         attributes.border.width / 2;
3001       break;
3002     case GTK_JUSTIFY_CENTER:
3003       sizel = area.width / 2;
3004       sizer = area.width / 2;
3005       area.x += area.width / 2;
3006       {
3007         for (i = col + 1; i <= MAX_VISIBLE_COLUMN (sheet); i++)
3008           {
3009             if ( ! gtk_sheet_cell_empty (sheet, row, i)) break;
3010             if (sizer >= text_width / 2) break;
3011             sizer += xxx_column_width (sheet, i);
3012             xxx_column_set_left_column (sheet, i,
3013                                         MIN (
3014                                              col,
3015                                              xxx_column_left_column (sheet, i)));
3016           }
3017         for (i = col - 1; i >= MIN_VISIBLE_COLUMN (sheet); i--)
3018           {
3019             if ( ! gtk_sheet_cell_empty (sheet, row, i)) break;
3020             if (sizel >= text_width / 2) break;
3021             sizel +=xxx_column_width (sheet, i);
3022             xxx_column_set_right_column (sheet, i,
3023                                          MAX (col,
3024                                               xxx_column_right_column (sheet, i)));
3025           }
3026         size = MIN (sizel, sizer);
3027       }
3028       area.x -= sizel;
3029       xoffset += sizel - text_width / 2 - CELLOFFSET;
3030       area.width = sizel + sizer;
3031       break;
3032     case GTK_JUSTIFY_LEFT:
3033     default:
3034       size = area.width;
3035       {
3036         for (i = col + 1; i <= MAX_VISIBLE_COLUMN (sheet); i++)
3037           {
3038             if (! gtk_sheet_cell_empty (sheet, row, i)) break;
3039             if (size >= text_width + CELLOFFSET) break;
3040             size +=xxx_column_width (sheet, i);
3041             xxx_column_set_left_column (sheet, i,
3042                                         MIN (
3043                                              col,
3044                                              xxx_column_left_column (sheet, i)));
3045
3046           }
3047         area.width = size;
3048       }
3049       xoffset += attributes.border.width / 2;
3050       break;
3051     }
3052
3053   gdk_gc_set_clip_rectangle (fg_gc, &area);
3054
3055
3056   gdk_draw_layout (sheet->pixmap, fg_gc,
3057                    area.x + xoffset + CELLOFFSET,
3058                    y,
3059                    layout);
3060
3061   gdk_gc_set_clip_rectangle (fg_gc, NULL);
3062   g_object_unref (layout);
3063
3064   gdk_draw_pixmap (sheet->sheet_window,
3065                    GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
3066                    sheet->pixmap,
3067                    area.x,
3068                    area.y,
3069                    area.x,
3070                    area.y,
3071                    area.width,
3072                    area.height);
3073
3074 }
3075
3076 static void
3077 gtk_sheet_range_draw (GtkSheet *sheet, const GtkSheetRange *range)
3078 {
3079   gint i, j;
3080   GtkSheetRange drawing_range;
3081   GdkRectangle area;
3082
3083   g_return_if_fail (sheet != NULL);
3084   g_return_if_fail (GTK_SHEET (sheet));
3085
3086   if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (sheet))) return;
3087   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
3088   if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
3089
3090   if (range == NULL)
3091     {
3092       drawing_range.row0 = MIN_VISIBLE_ROW (sheet);
3093       drawing_range.col0 = MIN_VISIBLE_COLUMN (sheet);
3094       drawing_range.rowi = MIN (MAX_VISIBLE_ROW (sheet),
3095                                 yyy_row_count (sheet) - 1);
3096       drawing_range.coli = MAX_VISIBLE_COLUMN (sheet);
3097
3098
3099       gdk_draw_rectangle (sheet->pixmap,
3100                           GTK_WIDGET (sheet)->style->white_gc,
3101                           TRUE,
3102                           0, 0,
3103                           sheet->sheet_window_width,
3104                           sheet->sheet_window_height);
3105     }
3106   else
3107     {
3108       drawing_range.row0 = MAX (range->row0, MIN_VISIBLE_ROW (sheet));
3109       drawing_range.col0 = MAX (range->col0, MIN_VISIBLE_COLUMN (sheet));
3110       drawing_range.rowi = MIN (range->rowi, MAX_VISIBLE_ROW (sheet));
3111       drawing_range.coli = MIN (range->coli, MAX_VISIBLE_COLUMN (sheet));
3112     }
3113
3114   if (drawing_range.coli == xxx_column_count (sheet) - 1)
3115     {
3116       area.x = COLUMN_LEFT_XPIXEL (sheet,
3117                                    xxx_column_count (sheet) - 1) +
3118         xxx_column_width (sheet, xxx_column_count (sheet) - 1) + 1;
3119
3120       area.y = 0;
3121
3122       gdk_gc_set_foreground (sheet->fg_gc, &sheet->bg_color);
3123
3124       gdk_draw_rectangle (sheet->pixmap,
3125                           sheet->fg_gc,
3126                           TRUE,
3127                           area.x, area.y,
3128                           sheet->sheet_window_width - area.x,
3129                           sheet->sheet_window_height);
3130
3131       gdk_draw_pixmap (sheet->sheet_window,
3132                        GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
3133                        sheet->pixmap,
3134                        area.x,
3135                        area.y,
3136                        area.x,
3137                        area.y,
3138                        sheet->sheet_window_width - area.x,
3139                        sheet->sheet_window_height);
3140     }
3141
3142   if (drawing_range.rowi == yyy_row_count (sheet) - 1)
3143     {
3144       area.x = 0;
3145       area.y = ROW_TOP_YPIXEL (sheet,
3146                                yyy_row_count (sheet) - 1) +
3147         yyy_row_height (sheet, yyy_row_count (sheet) - 1) + 1;
3148
3149       gdk_gc_set_foreground (sheet->fg_gc, &sheet->bg_color);
3150
3151       gdk_draw_rectangle (sheet->pixmap,
3152                           sheet->fg_gc,
3153                           TRUE,
3154                           area.x, area.y,
3155                           sheet->sheet_window_width,
3156                           sheet->sheet_window_height - area.y);
3157
3158       gdk_draw_pixmap (sheet->sheet_window,
3159                        GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
3160                        sheet->pixmap,
3161                        area.x,
3162                        area.y,
3163                        area.x,
3164                        area.y,
3165                        sheet->sheet_window_width,
3166                        sheet->sheet_window_height - area.y);
3167     }
3168
3169   for (i = drawing_range.row0; i <= drawing_range.rowi; i++)
3170     for (j = drawing_range.col0; j <= drawing_range.coli; j++)
3171       {
3172         gtk_sheet_cell_draw_default (sheet, i, j);
3173         gtk_sheet_cell_draw_label (sheet, i, j);
3174       }
3175
3176   gtk_sheet_draw_backing_pixmap (sheet, drawing_range);
3177
3178   if (sheet->state != GTK_SHEET_NORMAL &&
3179       gtk_sheet_range_isvisible (sheet, sheet->range))
3180     gtk_sheet_range_draw_selection (sheet, drawing_range);
3181
3182   if (sheet->state == GTK_STATE_NORMAL &&
3183       sheet->active_cell.row >= drawing_range.row0 &&
3184       sheet->active_cell.row <= drawing_range.rowi &&
3185       sheet->active_cell.col >= drawing_range.col0 &&
3186       sheet->active_cell.col <= drawing_range.coli)
3187     gtk_sheet_show_active_cell (sheet);
3188 }
3189
3190 static void
3191 gtk_sheet_range_draw_selection (GtkSheet *sheet, GtkSheetRange range)
3192 {
3193   GdkRectangle area;
3194   gint i, j;
3195   GtkSheetRange aux;
3196
3197   if (range.col0 > sheet->range.coli || range.coli < sheet->range.col0 ||
3198       range.row0 > sheet->range.rowi || range.rowi < sheet->range.row0)
3199     return;
3200
3201   if (!gtk_sheet_range_isvisible (sheet, range)) return;
3202   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
3203
3204   aux = range;
3205
3206   range.col0 = MAX (sheet->range.col0, range.col0);
3207   range.coli = MIN (sheet->range.coli, range.coli);
3208   range.row0 = MAX (sheet->range.row0, range.row0);
3209   range.rowi = MIN (sheet->range.rowi, range.rowi);
3210
3211   range.col0 = MAX (range.col0, MIN_VISIBLE_COLUMN (sheet));
3212   range.coli = MIN (range.coli, MAX_VISIBLE_COLUMN (sheet));
3213   range.row0 = MAX (range.row0, MIN_VISIBLE_ROW (sheet));
3214   range.rowi = MIN (range.rowi, MAX_VISIBLE_ROW (sheet));
3215
3216   for (i = range.row0; i <= range.rowi; i++)
3217     {
3218       for (j = range.col0; j <= range.coli; j++)
3219         {
3220
3221           if (gtk_sheet_cell_get_state (sheet, i, j) == GTK_STATE_SELECTED &&
3222               xxx_column_is_visible (sheet, j) && yyy_row_is_visible (sheet, i))
3223             {
3224
3225               area.x = COLUMN_LEFT_XPIXEL (sheet, j);
3226               area.y = ROW_TOP_YPIXEL (sheet, i);
3227               area.width= xxx_column_width (sheet, j);
3228               area.height = yyy_row_height (sheet, i);
3229
3230               if (i == sheet->range.row0)
3231                 {
3232                   area.y = area.y + 2;
3233                   area.height = area.height - 2;
3234                 }
3235               if (i == sheet->range.rowi) area.height = area.height - 3;
3236               if (j == sheet->range.col0)
3237                 {
3238                   area.x = area.x + 2;
3239                   area.width = area.width - 2;
3240                 }
3241               if (j == sheet->range.coli) area.width = area.width - 3;
3242
3243               if (i != sheet->active_cell.row || j != sheet->active_cell.col)
3244                 {
3245                   gdk_draw_rectangle (sheet->sheet_window,
3246                                       sheet->xor_gc,
3247                                       TRUE,
3248                                       area.x + 1, area.y + 1,
3249                                       area.width, area.height);
3250                 }
3251             }
3252
3253         }
3254     }
3255
3256   gtk_sheet_draw_border (sheet, sheet->range);
3257 }
3258
3259 static void
3260 gtk_sheet_draw_backing_pixmap (GtkSheet *sheet, GtkSheetRange range)
3261 {
3262   gint x, y, width, height;
3263
3264   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
3265
3266   x = COLUMN_LEFT_XPIXEL (sheet, range.col0);
3267   y = ROW_TOP_YPIXEL (sheet, range.row0);
3268   width = COLUMN_LEFT_XPIXEL (sheet, range.coli) - x +
3269     xxx_column_width (sheet, range.coli);
3270
3271   height = ROW_TOP_YPIXEL (sheet, range.rowi)- y + yyy_row_height (sheet, range.rowi);
3272
3273   if (range.row0 == sheet->range.row0)
3274     {
3275       y = y - 5;
3276       height = height + 5;
3277     }
3278   if (range.rowi == sheet->range.rowi) height = height + 5;
3279   if (range.col0 == sheet->range.col0)
3280     {
3281       x = x - 5;
3282       width = width + 5;
3283     }
3284   if (range.coli == sheet->range.coli) width = width + 5;
3285
3286   width = MIN (width, sheet->sheet_window_width - x);
3287   height = MIN (height, sheet->sheet_window_height - y);
3288
3289   x--;
3290   y--;
3291   width +=2;
3292   height +=2;
3293
3294   x = (sheet->row_titles_visible)
3295     ? MAX (x, sheet->row_title_area.width) : MAX (x, 0);
3296   y = (sheet->column_titles_visible)
3297     ? MAX (y, sheet->column_title_area.height) : MAX (y, 0);
3298
3299   if (range.coli == xxx_column_count (sheet) - 1)
3300     width = sheet->sheet_window_width - x;
3301   if (range.rowi == yyy_row_count (sheet) - 1)
3302     height = sheet->sheet_window_height - y;
3303
3304   gdk_draw_pixmap (sheet->sheet_window,
3305                    GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
3306                    sheet->pixmap,
3307                    x,
3308                    y,
3309                    x,
3310                    y,
3311                    width + 1,
3312                    height + 1);
3313 }
3314
3315
3316 void
3317 gtk_sheet_set_cell_text (GtkSheet *sheet, gint row, gint col, const gchar *text)
3318 {
3319   GtkSheetCellAttr attributes;
3320
3321   g_return_if_fail (sheet != NULL);
3322   g_return_if_fail (GTK_IS_SHEET (sheet));
3323   if (col >= xxx_column_count (sheet) || row >= yyy_row_count (sheet)) return;
3324   if (col < 0 || row < 0) return;
3325
3326   gtk_sheet_get_attributes (sheet, row, col, &attributes);
3327   gtk_sheet_set_cell (sheet, row, col, attributes.justification, text);
3328 }
3329
3330 static inline gint
3331 safe_strcmp (const gchar *s1, const gchar *s2)
3332 {
3333   if ( !s1 && !s2) return 0;
3334   if ( !s1) return - 1;
3335   if ( !s2) return +1;
3336   return strcmp (s1, s2);
3337 }
3338
3339 void
3340 gtk_sheet_set_cell (GtkSheet *sheet, gint row, gint col,
3341                     GtkJustification justification,
3342                     const gchar *text)
3343 {
3344   GSheetModel *model ;
3345   gboolean changed ;
3346   gchar *old_text ;
3347
3348   GtkSheetRange range;
3349   gint text_width;
3350   GtkSheetCellAttr attributes;
3351
3352   g_return_if_fail (sheet != NULL);
3353   g_return_if_fail (GTK_IS_SHEET (sheet));
3354   if (col >= xxx_column_count (sheet) || row >= yyy_row_count (sheet)) return;
3355   if (col < 0 || row < 0) return;
3356
3357   gtk_sheet_get_attributes (sheet, row, col, &attributes);
3358
3359   attributes.justification = justification;
3360
3361   model = gtk_sheet_get_model (sheet);
3362
3363   old_text = g_sheet_model_get_string (model, row, col);
3364
3365   changed = FALSE;
3366
3367   if (0 != safe_strcmp (old_text, text))
3368     changed = g_sheet_model_set_string (model, text, row, col);
3369
3370   if ( g_sheet_model_free_strings (model))
3371     g_free (old_text);
3372
3373
3374   if (changed && attributes.is_visible)
3375     {
3376       gchar *s = gtk_sheet_cell_get_text (sheet, row, col);
3377       text_width = 0;
3378       if (s && strlen (s) > 0)
3379         {
3380           text_width = STRING_WIDTH (GTK_WIDGET (sheet),
3381                                      attributes.font_desc, text);
3382         }
3383       dispose_string (sheet, s);
3384
3385       range.row0 = row;
3386       range.rowi = row;
3387       range.col0 = MIN_VISIBLE_COLUMN (sheet);
3388       range.coli = MAX_VISIBLE_COLUMN (sheet);
3389
3390       if (gtk_sheet_autoresize (sheet) &&
3391           text_width > xxx_column_width (sheet, col) -
3392           2 * CELLOFFSET- attributes.border.width)
3393         {
3394           gtk_sheet_set_column_width (sheet, col, text_width + 2 * CELLOFFSET
3395                                       + attributes.border.width);
3396           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_REDRAW_PENDING);
3397         }
3398       else
3399         if (!GTK_SHEET_IS_FROZEN (sheet))
3400           gtk_sheet_range_draw (sheet, &range);
3401     }
3402
3403   if ( changed )
3404     g_signal_emit (sheet, sheet_signals[CHANGED], 0, row, col);
3405
3406 }
3407
3408
3409 void
3410 gtk_sheet_cell_clear (GtkSheet *sheet, gint row, gint column)
3411 {
3412   GtkSheetRange range;
3413
3414   g_return_if_fail (sheet != NULL);
3415   g_return_if_fail (GTK_IS_SHEET (sheet));
3416   if (column >= xxx_column_count (sheet) ||
3417       row >= yyy_row_count (sheet)) return;
3418
3419   if (column < 0 || row < 0) return;
3420
3421   range.row0 = row;
3422   range.rowi = row;
3423   range.col0 = MIN_VISIBLE_COLUMN (sheet);
3424   range.coli = MAX_VISIBLE_COLUMN (sheet);
3425
3426   gtk_sheet_real_cell_clear (sheet, row, column);
3427
3428   if (!GTK_SHEET_IS_FROZEN (sheet))
3429     {
3430       gtk_sheet_range_draw (sheet, &range);
3431     }
3432 }
3433
3434 static void
3435 gtk_sheet_real_cell_clear (GtkSheet *sheet, gint row, gint column)
3436 {
3437   GSheetModel *model = gtk_sheet_get_model (sheet);
3438
3439   gchar *old_text = gtk_sheet_cell_get_text (sheet, row, column);
3440
3441   if (old_text && strlen (old_text) > 0 )
3442     {
3443       g_sheet_model_datum_clear (model, row, column);
3444     }
3445
3446   dispose_string (sheet, old_text);
3447 }
3448
3449 void
3450 gtk_sheet_range_clear (GtkSheet *sheet, const GtkSheetRange *range)
3451 {
3452   g_return_if_fail (sheet != NULL);
3453   g_return_if_fail (GTK_IS_SHEET (sheet));
3454
3455   gtk_sheet_real_range_clear (sheet, range);
3456 }
3457
3458 static void
3459 gtk_sheet_real_range_clear (GtkSheet *sheet, const GtkSheetRange *range)
3460 {
3461   gint i, j;
3462   GtkSheetRange clear;
3463
3464   if (!range)
3465     {
3466       clear.row0 = 0;
3467       clear.rowi = yyy_row_count (sheet) - 1;
3468       clear.col0 = 0;
3469       clear.coli = xxx_column_count (sheet) - 1;
3470     }
3471   else
3472     clear=*range;
3473
3474   clear.row0 = MAX (clear.row0, 0);
3475   clear.col0 = MAX (clear.col0, 0);
3476   clear.rowi = MIN (clear.rowi, yyy_row_count (sheet) - 1 );
3477   clear.coli = MIN (clear.coli, xxx_column_count (sheet) - 1 );
3478
3479   for (i = clear.row0; i <= clear.rowi; i++)
3480     for (j = clear.col0; j <= clear.coli; j++)
3481       {
3482         gtk_sheet_real_cell_clear (sheet, i, j);
3483       }
3484
3485   gtk_sheet_range_draw (sheet, NULL);
3486 }
3487
3488
3489 static gboolean
3490 gtk_sheet_cell_empty (const GtkSheet *sheet, gint row, gint col)
3491 {
3492   gboolean empty;
3493   char *text = gtk_sheet_cell_get_text (sheet, row, col);
3494   empty = (text == NULL );
3495
3496   dispose_string (sheet, text);
3497
3498   return empty;
3499 }
3500
3501
3502 gchar *
3503 gtk_sheet_cell_get_text (const GtkSheet *sheet, gint row, gint col)
3504 {
3505   GSheetModel *model;
3506   g_return_val_if_fail (sheet != NULL, NULL);
3507   g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);
3508
3509   if (col >= xxx_column_count (sheet) || row >= yyy_row_count (sheet))
3510     return NULL;
3511   if (col < 0 || row < 0) return NULL;
3512
3513   model = gtk_sheet_get_model (sheet);
3514
3515   if ( !model )
3516     return NULL;
3517
3518   return g_sheet_model_get_string (model, row, col);
3519 }
3520
3521
3522 GtkStateType
3523 gtk_sheet_cell_get_state (GtkSheet *sheet, gint row, gint col)
3524 {
3525   gint state;
3526   GtkSheetRange *range;
3527
3528   g_return_val_if_fail (sheet != NULL, 0);
3529   g_return_val_if_fail (GTK_IS_SHEET (sheet), 0);
3530   if (col >= xxx_column_count (sheet) || row >= yyy_row_count (sheet)) return 0;
3531   if (col < 0 || row < 0) return 0;
3532
3533   state = sheet->state;
3534   range = &sheet->range;
3535
3536   switch (state)
3537     {
3538     case GTK_SHEET_NORMAL:
3539       return GTK_STATE_NORMAL;
3540       break;
3541     case GTK_SHEET_ROW_SELECTED:
3542       if (row >= range->row0 && row <= range->rowi)
3543         return GTK_STATE_SELECTED;
3544       break;
3545     case GTK_SHEET_COLUMN_SELECTED:
3546       if (col >= range->col0 && col <= range->coli)
3547         return GTK_STATE_SELECTED;
3548       break;
3549     case GTK_SHEET_RANGE_SELECTED:
3550       if (row >= range->row0 && row <= range->rowi && \
3551           col >= range->col0 && col <= range->coli)
3552         return GTK_STATE_SELECTED;
3553       break;
3554     }
3555   return GTK_STATE_NORMAL;
3556 }
3557
3558 /* Convert X, Y (in pixels) to *ROW, *COLUMN (in cell coords)
3559    -1 indicates the title buttons.
3560    If the function returns FALSE, then the results will be unreliable.
3561 */
3562 gboolean
3563 gtk_sheet_get_pixel_info (GtkSheet *sheet,
3564                           gint x,
3565                           gint y,
3566                           gint *row,
3567                           gint *column)
3568 {
3569   gint trow, tcol;
3570   *row = -G_MAXINT;
3571   *column = -G_MAXINT;
3572
3573   g_return_val_if_fail (sheet != NULL, 0);
3574   g_return_val_if_fail (GTK_IS_SHEET (sheet), 0);
3575
3576   /* bounds checking, return false if the user clicked
3577      on a blank area */
3578   if (y < 0)
3579     return FALSE;
3580
3581   if (x < 0)
3582     return FALSE;
3583
3584   if ( y < sheet->column_title_area.height + sheet->column_title_area.y)
3585     *row = -1;
3586
3587   else
3588     {
3589       trow = ROW_FROM_YPIXEL (sheet, y);
3590       if (trow > yyy_row_count (sheet))
3591         return FALSE;
3592
3593       *row = trow;
3594     }
3595
3596   if ( x < sheet->row_title_area.width + sheet->row_title_area.x)
3597     *column = -1;
3598   else
3599     {
3600       tcol = COLUMN_FROM_XPIXEL (sheet, x);
3601       if (tcol > xxx_column_count (sheet))
3602         return FALSE;
3603
3604       *column = tcol;
3605     }
3606
3607   return TRUE;
3608 }
3609
3610 gboolean
3611 gtk_sheet_get_cell_area (GtkSheet * sheet,
3612                          gint row,
3613                          gint column,
3614                          GdkRectangle *area)
3615 {
3616   g_return_val_if_fail (sheet != NULL, 0);
3617   g_return_val_if_fail (GTK_IS_SHEET (sheet), 0);
3618
3619   if (row >= yyy_row_count (sheet) || column >= xxx_column_count (sheet))
3620     return FALSE;
3621
3622   area->x = (column == -1) ? 0 : (COLUMN_LEFT_XPIXEL (sheet, column) -
3623                                   (sheet->row_titles_visible
3624                                    ? sheet->row_title_area.width
3625                                    : 0));
3626   area->y = (row == -1) ? 0 : (ROW_TOP_YPIXEL (sheet, row) -
3627                                (sheet->column_titles_visible
3628                                 ? sheet->column_title_area.height
3629                                 : 0));
3630   area->width= (column == -1) ? sheet->row_title_area.width
3631     : xxx_column_width (sheet, column);
3632
3633   area->height= (row == -1) ? sheet->column_title_area.height
3634     : yyy_row_height (sheet, row);
3635
3636   return TRUE;
3637 }
3638
3639 gboolean
3640 gtk_sheet_set_active_cell (GtkSheet *sheet, gint row, gint column)
3641 {
3642   g_return_val_if_fail (sheet != NULL, 0);
3643   g_return_val_if_fail (GTK_IS_SHEET (sheet), 0);
3644
3645   if (row < - 1 || column < - 1) return FALSE;
3646   if (row >= yyy_row_count (sheet) || column >= xxx_column_count (sheet))
3647     return FALSE;
3648
3649   if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
3650     gtk_sheet_deactivate_cell (sheet);
3651
3652   sheet->active_cell.row = row;
3653   sheet->active_cell.col = column;
3654
3655   if ( row == -1 || column == -1)
3656     {
3657       gtk_sheet_hide_active_cell (sheet);
3658       return TRUE;
3659     }
3660
3661   if (!gtk_sheet_activate_cell (sheet, row, column)) return FALSE;
3662
3663   if (gtk_sheet_autoscroll (sheet))
3664     gtk_sheet_move_query (sheet, row, column);
3665
3666   return TRUE;
3667 }
3668
3669 void
3670 gtk_sheet_get_active_cell (GtkSheet *sheet, gint *row, gint *column)
3671 {
3672   g_return_if_fail (sheet != NULL);
3673   g_return_if_fail (GTK_IS_SHEET (sheet));
3674
3675   if ( row ) *row = sheet->active_cell.row;
3676   if (column) *column = sheet->active_cell.col;
3677 }
3678
3679 static void
3680 gtk_sheet_entry_changed (GtkWidget *widget, gpointer data)
3681 {
3682   GtkSheet *sheet;
3683   gint row, col;
3684   const char *text;
3685   GtkJustification justification;
3686   GtkSheetCellAttr attributes;
3687
3688   g_return_if_fail (data != NULL);
3689   g_return_if_fail (GTK_IS_SHEET (data));
3690
3691   sheet = GTK_SHEET (data);
3692
3693   if (!GTK_WIDGET_VISIBLE (widget)) return;
3694   if (sheet->state != GTK_STATE_NORMAL) return;
3695
3696   row = sheet->active_cell.row;
3697   col = sheet->active_cell.col;
3698
3699   if (row < 0 || col < 0) return;
3700
3701   sheet->active_cell.row = -1;
3702   sheet->active_cell.col = -1;
3703
3704   text = gtk_entry_get_text (GTK_ENTRY (gtk_sheet_get_entry (sheet)));
3705
3706   GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IS_FROZEN);
3707
3708   if (text && strlen (text) > 0)
3709     {
3710       gtk_sheet_get_attributes (sheet, row, col, &attributes);
3711       justification = attributes.justification;
3712       gtk_sheet_set_cell (sheet, row, col, justification, text);
3713     }
3714
3715   if (sheet->freeze_count == 0)
3716     GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IS_FROZEN);
3717
3718   sheet->active_cell.row = row;;
3719   sheet->active_cell.col = col;
3720 }
3721
3722
3723 static void
3724 gtk_sheet_deactivate_cell (GtkSheet *sheet)
3725 {
3726   g_return_if_fail (sheet != NULL);
3727   g_return_if_fail (GTK_IS_SHEET (sheet));
3728
3729   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return ;
3730   if (sheet->state != GTK_SHEET_NORMAL) return ;
3731
3732   if ( sheet->active_cell.row == -1 || sheet->active_cell.col == -1 )
3733     return ;
3734
3735   g_signal_emit (sheet, sheet_signals[DEACTIVATE], 0,
3736                  sheet->active_cell.row,
3737                  sheet->active_cell.col);
3738
3739
3740   g_signal_handlers_disconnect_by_func (gtk_sheet_get_entry (sheet),
3741                                         G_CALLBACK (gtk_sheet_entry_changed),
3742                                         sheet);
3743
3744   gtk_sheet_hide_active_cell (sheet);
3745   sheet->active_cell.row = -1;
3746   sheet->active_cell.col = -1;
3747
3748   if (GTK_SHEET_REDRAW_PENDING (sheet))
3749     {
3750       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_REDRAW_PENDING);
3751       gtk_sheet_range_draw (sheet, NULL);
3752     }
3753 }
3754
3755 static void
3756 gtk_sheet_hide_active_cell (GtkSheet *sheet)
3757 {
3758   const char *text;
3759   gint row, col;
3760   GtkJustification justification;
3761   GtkSheetCellAttr attributes;
3762
3763   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
3764
3765   row = sheet->active_cell.row;
3766   col = sheet->active_cell.col;
3767
3768   if (row < 0 || col < 0) return;
3769
3770   if (sheet->freeze_count == 0)
3771     GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IS_FROZEN);
3772
3773   text = gtk_entry_get_text (GTK_ENTRY (gtk_sheet_get_entry (sheet)));
3774
3775   gtk_sheet_get_attributes (sheet, row, col, &attributes);
3776   justification = attributes.justification;
3777
3778   row = sheet->active_cell.row;
3779   col = sheet->active_cell.col;
3780
3781   gtk_widget_hide (sheet->sheet_entry);
3782   gtk_widget_unmap (sheet->sheet_entry);
3783
3784   if (row != -1 && col != -1)
3785     gdk_draw_pixmap (sheet->sheet_window,
3786                      GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
3787                      sheet->pixmap,
3788                      COLUMN_LEFT_XPIXEL (sheet, col)- 1,
3789                      ROW_TOP_YPIXEL (sheet, row)- 1,
3790                      COLUMN_LEFT_XPIXEL (sheet, col)- 1,
3791                      ROW_TOP_YPIXEL (sheet, row)- 1,
3792                      xxx_column_width (sheet, col) + 4,
3793                      yyy_row_height (sheet, row)+4);
3794
3795   gtk_widget_grab_focus (GTK_WIDGET (sheet));
3796
3797   GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (sheet->sheet_entry), GTK_VISIBLE);
3798
3799 }
3800
3801 static gboolean
3802 gtk_sheet_activate_cell (GtkSheet *sheet, gint row, gint col)
3803 {
3804   gboolean veto = TRUE;
3805
3806   g_return_val_if_fail (sheet != NULL, FALSE);
3807   g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE);
3808
3809   if (row < 0 || col < 0) return FALSE;
3810
3811   if ( row > yyy_row_count (sheet) || col > xxx_column_count (sheet))
3812     return FALSE;
3813
3814   if (!veto) return FALSE;
3815   if (sheet->state != GTK_SHEET_NORMAL)
3816     {
3817       sheet->state = GTK_SHEET_NORMAL;
3818       gtk_sheet_real_unselect_range (sheet, NULL);
3819     }
3820
3821   sheet->range.row0 = row;
3822   sheet->range.col0 = col;
3823   sheet->range.rowi = row;
3824   sheet->range.coli = col;
3825   sheet->active_cell.row = row;
3826   sheet->active_cell.col = col;
3827   sheet->selection_cell.row = row;
3828   sheet->selection_cell.col = col;
3829
3830   GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
3831
3832   gtk_sheet_show_active_cell (sheet);
3833
3834   g_signal_connect (gtk_sheet_get_entry (sheet),
3835                     "changed",
3836                     G_CALLBACK (gtk_sheet_entry_changed),
3837                     sheet);
3838
3839   _gtkextra_signal_emit (GTK_OBJECT (sheet), sheet_signals [ACTIVATE], row, col, &veto);
3840
3841   return TRUE;
3842 }
3843
3844 static void
3845 gtk_sheet_show_active_cell (GtkSheet *sheet)
3846 {
3847   GtkEntry *sheet_entry;
3848   GtkSheetCellAttr attributes;
3849   gchar *text = NULL;
3850   const gchar *old_text;
3851   GtkJustification justification;
3852   gint row, col;
3853
3854   g_return_if_fail (sheet != NULL);
3855   g_return_if_fail (GTK_IS_SHEET (sheet));
3856
3857   row = sheet->active_cell.row;
3858   col = sheet->active_cell.col;
3859
3860   /* Don't show the active cell, if there is no active cell: */
3861   if (! (row >= 0 && col >= 0)) /* e.g row or coll == -1. */
3862     return;
3863
3864   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
3865   if (sheet->state != GTK_SHEET_NORMAL) return;
3866   if (GTK_SHEET_IN_SELECTION (sheet)) return;
3867
3868   GTK_WIDGET_SET_FLAGS (GTK_WIDGET (sheet->sheet_entry), GTK_VISIBLE);
3869
3870   sheet_entry = GTK_ENTRY (gtk_sheet_get_entry (sheet));
3871
3872   gtk_sheet_get_attributes (sheet, row, col, &attributes);
3873
3874   justification = GTK_JUSTIFY_LEFT;
3875
3876   if (gtk_sheet_justify_entry (sheet))
3877     justification = attributes.justification;
3878
3879   text = gtk_sheet_cell_get_text (sheet, row, col);
3880   if ( ! text )
3881     text = g_strdup ("");
3882
3883   gtk_entry_set_visibility (GTK_ENTRY (sheet_entry), attributes.is_visible);
3884
3885
3886   /*** Added by John Gotts. Mar 25, 2005 *********/
3887   old_text = gtk_entry_get_text (GTK_ENTRY (sheet_entry));
3888   if (strcmp (old_text, text) != 0)
3889     {
3890       if (!GTK_IS_ITEM_ENTRY (sheet_entry))
3891         gtk_entry_set_text (GTK_ENTRY (sheet_entry), text);
3892       else
3893         gtk_item_entry_set_text (GTK_ITEM_ENTRY (sheet_entry), text, justification);
3894     }
3895
3896   gtk_sheet_entry_set_max_size (sheet);
3897   gtk_sheet_size_allocate_entry (sheet);
3898
3899   gtk_widget_map (sheet->sheet_entry);
3900
3901   gtk_widget_grab_focus (GTK_WIDGET (sheet_entry));
3902
3903   dispose_string (sheet, text);
3904 }
3905
3906 static void
3907 gtk_sheet_draw_active_cell (GtkSheet *sheet)
3908 {
3909   gint row, col;
3910   GtkSheetRange range;
3911
3912   if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (sheet))) return;
3913   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
3914
3915   row = sheet->active_cell.row;
3916   col = sheet->active_cell.col;
3917
3918   if (row < 0 || col < 0) return;
3919
3920   if (!gtk_sheet_cell_isvisible (sheet, row, col)) return;
3921
3922   range.col0 = range.coli = col;
3923   range.row0 = range.rowi = row;
3924
3925   gtk_sheet_draw_border (sheet, range);
3926 }
3927
3928
3929 static void
3930 gtk_sheet_make_backing_pixmap (GtkSheet *sheet, guint width, guint height)
3931 {
3932   gint pixmap_width, pixmap_height;
3933
3934   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
3935
3936   if (width == 0 && height == 0)
3937     {
3938       width = sheet->sheet_window_width + 80;
3939       height = sheet->sheet_window_height + 80;
3940     }
3941
3942   if (!sheet->pixmap)
3943     {
3944       /* allocate */
3945       sheet->pixmap = gdk_pixmap_new (sheet->sheet_window,
3946                                       width, height,
3947                                       - 1);
3948       if (!GTK_SHEET_IS_FROZEN (sheet)) gtk_sheet_range_draw (sheet, NULL);
3949     }
3950   else
3951     {
3952       /* reallocate if sizes don't match */
3953       gdk_window_get_size (sheet->pixmap,
3954                            &pixmap_width, &pixmap_height);
3955       if ( (pixmap_width != width) || (pixmap_height != height))
3956         {
3957           g_object_unref (sheet->pixmap);
3958           sheet->pixmap = gdk_pixmap_new (sheet->sheet_window,
3959                                           width, height,
3960                                           - 1);
3961           if (!GTK_SHEET_IS_FROZEN (sheet)) gtk_sheet_range_draw (sheet, NULL);
3962         }
3963     }
3964 }
3965
3966 static void
3967 gtk_sheet_new_selection (GtkSheet *sheet, GtkSheetRange *range)
3968 {
3969   gint i, j, mask1, mask2;
3970   gint state, selected;
3971   gint x, y, width, height;
3972   GtkSheetRange new_range, aux_range;
3973
3974   g_return_if_fail (sheet != NULL);
3975
3976   if (range == NULL) range=&sheet->range;
3977
3978   new_range=*range;
3979
3980   range->row0 = MIN (range->row0, sheet->range.row0);
3981   range->rowi = MAX (range->rowi, sheet->range.rowi);
3982   range->col0 = MIN (range->col0, sheet->range.col0);
3983   range->coli = MAX (range->coli, sheet->range.coli);
3984
3985   range->row0 = MAX (range->row0, MIN_VISIBLE_ROW (sheet));
3986   range->rowi = MIN (range->rowi, MAX_VISIBLE_ROW (sheet));
3987   range->col0 = MAX (range->col0, MIN_VISIBLE_COLUMN (sheet));
3988   range->coli = MIN (range->coli, MAX_VISIBLE_COLUMN (sheet));
3989
3990   aux_range.row0 = MAX (new_range.row0, MIN_VISIBLE_ROW (sheet));
3991   aux_range.rowi = MIN (new_range.rowi, MAX_VISIBLE_ROW (sheet));
3992   aux_range.col0 = MAX (new_range.col0, MIN_VISIBLE_COLUMN (sheet));
3993   aux_range.coli = MIN (new_range.coli, MAX_VISIBLE_COLUMN (sheet));
3994
3995   for (i = range->row0; i <= range->rowi; i++)
3996     {
3997       for (j = range->col0; j <= range->coli; j++)
3998         {
3999
4000           state = gtk_sheet_cell_get_state (sheet, i, j);
4001           selected= (i <= new_range.rowi && i >= new_range.row0 &&
4002                      j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
4003
4004           if (state == GTK_STATE_SELECTED && selected &&
4005               xxx_column_is_visible (sheet, j) && yyy_row_is_visible (sheet, i) &&
4006               (i == sheet->range.row0 || i == sheet->range.rowi ||
4007                j == sheet->range.col0 || j == sheet->range.coli ||
4008                i == new_range.row0 || i == new_range.rowi ||
4009                j == new_range.col0 || j == new_range.coli))
4010             {
4011
4012               mask1 = i == sheet->range.row0 ? 1 : 0;
4013               mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1;
4014               mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1;
4015               mask1 = j == sheet->range.coli ? mask1 + 8 : mask1;
4016
4017               mask2 = i == new_range.row0 ? 1 : 0;
4018               mask2 = i == new_range.rowi ? mask2 + 2 : mask2;
4019               mask2 = j == new_range.col0 ? mask2 + 4 : mask2;
4020               mask2 = j == new_range.coli ? mask2 + 8 : mask2;
4021
4022               if (mask1 != mask2)
4023                 {
4024                   x = COLUMN_LEFT_XPIXEL (sheet, j);
4025                   y = ROW_TOP_YPIXEL (sheet, i);
4026                   width = COLUMN_LEFT_XPIXEL (sheet, j)- x+
4027                     xxx_column_width (sheet, j);
4028                   height = ROW_TOP_YPIXEL (sheet, i)- y + yyy_row_height (sheet, i);
4029
4030                   if (i == sheet->range.row0)
4031                     {
4032                       y = y - 3;
4033                       height = height + 3;
4034                     }
4035                   if (i == sheet->range.rowi) height = height + 3;
4036                   if (j == sheet->range.col0)
4037                     {
4038                       x = x - 3;
4039                       width = width + 3;
4040                     }
4041                   if (j == sheet->range.coli) width = width + 3;
4042
4043                   gdk_draw_pixmap (sheet->sheet_window,
4044                                    GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
4045                                    sheet->pixmap,
4046                                    x + 1,
4047                                    y + 1,
4048                                    x + 1,
4049                                    y + 1,
4050                                    width,
4051                                    height);
4052
4053                   if (i != sheet->active_cell.row || j != sheet->active_cell.col)
4054                     {
4055                       x = COLUMN_LEFT_XPIXEL (sheet, j);
4056                       y = ROW_TOP_YPIXEL (sheet, i);
4057                       width = COLUMN_LEFT_XPIXEL (sheet, j)- x+
4058                         xxx_column_width (sheet, j);
4059
4060                       height = ROW_TOP_YPIXEL (sheet, i)- y + yyy_row_height (sheet, i);
4061
4062                       if (i == new_range.row0)
4063                         {
4064                           y = y+2;
4065                           height = height - 2;
4066                         }
4067                       if (i == new_range.rowi) height = height - 3;
4068                       if (j == new_range.col0)
4069                         {
4070                           x = x+2;
4071                           width = width - 2;
4072                         }
4073                       if (j == new_range.coli) width = width - 3;
4074
4075                       gdk_draw_rectangle (sheet->sheet_window,
4076                                           sheet->xor_gc,
4077                                           TRUE,
4078                                           x + 1, y + 1,
4079                                           width, height);
4080                     }
4081                 }
4082             }
4083         }
4084     }
4085
4086   for (i = range->row0; i <= range->rowi; i++)
4087     {
4088       for (j = range->col0; j <= range->coli; j++)
4089         {
4090
4091           state = gtk_sheet_cell_get_state (sheet, i, j);
4092           selected= (i <= new_range.rowi && i >= new_range.row0 &&
4093                      j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
4094
4095           if (state == GTK_STATE_SELECTED && !selected &&
4096               xxx_column_is_visible (sheet, j) && yyy_row_is_visible (sheet, i))
4097             {
4098
4099               x = COLUMN_LEFT_XPIXEL (sheet, j);
4100               y = ROW_TOP_YPIXEL (sheet, i);
4101               width = COLUMN_LEFT_XPIXEL (sheet, j)- x+ xxx_column_width (sheet, j);
4102               height = ROW_TOP_YPIXEL (sheet, i)- y + yyy_row_height (sheet, i);
4103
4104               if (i == sheet->range.row0)
4105                 {
4106                   y = y - 3;
4107                   height = height + 3;
4108                 }
4109               if (i == sheet->range.rowi) height = height + 3;
4110               if (j == sheet->range.col0)
4111                 {
4112                   x = x - 3;
4113                   width = width + 3;
4114                 }
4115               if (j == sheet->range.coli) width = width + 3;
4116
4117               gdk_draw_pixmap (sheet->sheet_window,
4118                                GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
4119                                sheet->pixmap,
4120                                x + 1,
4121                                y + 1,
4122                                x + 1,
4123                                y + 1,
4124                                width,
4125                                height);
4126             }
4127         }
4128     }
4129
4130   for (i = range->row0; i <= range->rowi; i++)
4131     {
4132       for (j = range->col0; j <= range->coli; j++)
4133         {
4134
4135           state = gtk_sheet_cell_get_state (sheet, i, j);
4136           selected= (i <= new_range.rowi && i >= new_range.row0 &&
4137                      j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
4138
4139           if (state != GTK_STATE_SELECTED && selected &&
4140               xxx_column_is_visible (sheet, j) && yyy_row_is_visible (sheet, i) &&
4141               (i != sheet->active_cell.row || j != sheet->active_cell.col))
4142             {
4143
4144               x = COLUMN_LEFT_XPIXEL (sheet, j);
4145               y = ROW_TOP_YPIXEL (sheet, i);
4146               width = COLUMN_LEFT_XPIXEL (sheet, j)- x+ xxx_column_width (sheet, j);
4147               height = ROW_TOP_YPIXEL (sheet, i)- y + yyy_row_height (sheet, i);
4148
4149               if (i == new_range.row0)
4150                 {
4151                   y = y+2;
4152                   height = height - 2;
4153                 }
4154               if (i == new_range.rowi) height = height - 3;
4155               if (j == new_range.col0)
4156                 {
4157                   x = x+2;
4158                   width = width - 2;
4159                 }
4160               if (j == new_range.coli) width = width - 3;
4161
4162               gdk_draw_rectangle (sheet->sheet_window,
4163                                   sheet->xor_gc,
4164                                   TRUE,
4165                                   x + 1, y + 1,
4166                                   width, height);
4167
4168             }
4169
4170         }
4171     }
4172
4173   for (i = aux_range.row0; i <= aux_range.rowi; i++)
4174     {
4175       for (j = aux_range.col0; j <= aux_range.coli; j++)
4176         {
4177
4178           if (xxx_column_is_visible (sheet, j) && yyy_row_is_visible (sheet, i))
4179             {
4180
4181               state = gtk_sheet_cell_get_state (sheet, i, j);
4182
4183               mask1 = i == sheet->range.row0 ? 1 : 0;
4184               mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1;
4185               mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1;
4186               mask1 = j == sheet->range.coli ? mask1 + 8 : mask1;
4187
4188               mask2 = i == new_range.row0 ? 1 : 0;
4189               mask2 = i == new_range.rowi ? mask2 + 2 : mask2;
4190               mask2 = j == new_range.col0 ? mask2 + 4 : mask2;
4191               mask2 = j == new_range.coli ? mask2 + 8 : mask2;
4192               if (mask2 != mask1 || (mask2 == mask1 && state != GTK_STATE_SELECTED))
4193                 {
4194                   x = COLUMN_LEFT_XPIXEL (sheet, j);
4195                   y = ROW_TOP_YPIXEL (sheet, i);
4196                   width = xxx_column_width (sheet, j);
4197                   height = yyy_row_height (sheet, i);
4198                   if (mask2 & 1)
4199                     gdk_draw_rectangle (sheet->sheet_window,
4200                                         sheet->xor_gc,
4201                                         TRUE,
4202                                         x + 1, y - 1,
4203                                         width, 3);
4204
4205
4206                   if (mask2 & 2)
4207                     gdk_draw_rectangle (sheet->sheet_window,
4208                                         sheet->xor_gc,
4209                                         TRUE,
4210                                         x + 1, y + height - 1,
4211                                         width, 3);
4212
4213                   if (mask2 & 4)
4214                     gdk_draw_rectangle (sheet->sheet_window,
4215                                         sheet->xor_gc,
4216                                         TRUE,
4217                                         x - 1, y + 1,
4218                                         3, height);
4219
4220
4221                   if (mask2 & 8)
4222                     gdk_draw_rectangle (sheet->sheet_window,
4223                                         sheet->xor_gc,
4224                                         TRUE,
4225                                         x + width - 1, y + 1,
4226                                         3, height);
4227
4228
4229
4230                 }
4231
4232             }
4233
4234         }
4235     }
4236
4237
4238   *range = new_range;
4239   gtk_sheet_draw_corners (sheet, new_range);
4240
4241 }
4242
4243 static void
4244 gtk_sheet_draw_border (GtkSheet *sheet, GtkSheetRange new_range)
4245 {
4246   GtkWidget *widget;
4247   GdkRectangle area;
4248   gint i;
4249   gint x, y, width, height;
4250
4251   widget = GTK_WIDGET (sheet);
4252
4253   x = COLUMN_LEFT_XPIXEL (sheet, new_range.col0);
4254   y = ROW_TOP_YPIXEL (sheet, new_range.row0);
4255   width = COLUMN_LEFT_XPIXEL (sheet, new_range.coli) - x +
4256     xxx_column_width (sheet, new_range.coli);
4257
4258   height = ROW_TOP_YPIXEL (sheet, new_range.rowi) - y +
4259     yyy_row_height (sheet, new_range.rowi);
4260
4261   area.x = COLUMN_LEFT_XPIXEL (sheet, MIN_VISIBLE_COLUMN (sheet));
4262   area.y = ROW_TOP_YPIXEL (sheet, MIN_VISIBLE_ROW (sheet));
4263   area.width = sheet->sheet_window_width;
4264   area.height = sheet->sheet_window_height;
4265
4266   if (x < 0)
4267     {
4268       width = width + x;
4269       x = 0;
4270     }
4271   if (width > area.width) width = area.width + 10;
4272   if (y < 0)
4273     {
4274       height = height + y;
4275       y = 0;
4276     }
4277   if (height > area.height) height = area.height + 10;
4278
4279   gdk_gc_set_clip_rectangle (sheet->xor_gc, &area);
4280
4281   for (i = -1; i <= 1; ++i)
4282     gdk_draw_rectangle (sheet->sheet_window,
4283                         sheet->xor_gc,
4284                         FALSE,
4285                         x + i,
4286                         y + i,
4287                         width - 2 * i,
4288                         height - 2 * i);
4289
4290   gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
4291
4292
4293   gtk_sheet_draw_corners (sheet, new_range);
4294 }
4295
4296 static void
4297 gtk_sheet_draw_corners (GtkSheet *sheet, GtkSheetRange range)
4298 {
4299   gint x, y;
4300   guint width = 1;
4301
4302   if (gtk_sheet_cell_isvisible (sheet, range.row0, range.col0))
4303     {
4304       x = COLUMN_LEFT_XPIXEL (sheet, range.col0);
4305       y = ROW_TOP_YPIXEL (sheet, range.row0);
4306       gdk_draw_pixmap (sheet->sheet_window,
4307                        GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
4308                        sheet->pixmap,
4309                        x - 1,
4310                        y - 1,
4311                        x - 1,
4312                        y - 1,
4313                        3,
4314                        3);
4315       gdk_draw_rectangle (sheet->sheet_window,
4316                           sheet->xor_gc,
4317                           TRUE,
4318                           x - 1, y - 1,
4319                           3, 3);
4320     }
4321
4322   if (gtk_sheet_cell_isvisible (sheet, range.row0, range.coli) ||
4323       sheet->state == GTK_SHEET_COLUMN_SELECTED)
4324     {
4325       x = COLUMN_LEFT_XPIXEL (sheet, range.coli)+
4326         xxx_column_width (sheet, range.coli);
4327       y = ROW_TOP_YPIXEL (sheet, range.row0);
4328       width = 1;
4329       if (sheet->state == GTK_SHEET_COLUMN_SELECTED)
4330         {
4331           y = ROW_TOP_YPIXEL (sheet, MIN_VISIBLE_ROW (sheet))+3;
4332           width = 3;
4333         }
4334       gdk_draw_pixmap (sheet->sheet_window,
4335                        GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
4336                        sheet->pixmap,
4337                        x - width,
4338                        y - width,
4339                        x - width,
4340                        y - width,
4341                        2 * width + 1,
4342                        2 * width + 1);
4343       gdk_draw_rectangle (sheet->sheet_window,
4344                           sheet->xor_gc,
4345                           TRUE,
4346                           x - width + width / 2, y - width + width / 2,
4347                           2 + width, 2 + width);
4348     }
4349
4350   if (gtk_sheet_cell_isvisible (sheet, range.rowi, range.col0) ||
4351       sheet->state == GTK_SHEET_ROW_SELECTED)
4352     {
4353       x = COLUMN_LEFT_XPIXEL (sheet, range.col0);
4354       y = ROW_TOP_YPIXEL (sheet, range.rowi)+
4355         yyy_row_height (sheet, range.rowi);
4356       width = 1;
4357       if (sheet->state == GTK_SHEET_ROW_SELECTED)
4358         {
4359           x = COLUMN_LEFT_XPIXEL (sheet, MIN_VISIBLE_COLUMN (sheet))+3;
4360           width = 3;
4361         }
4362       gdk_draw_pixmap (sheet->sheet_window,
4363                        GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
4364                        sheet->pixmap,
4365                        x - width,
4366                        y - width,
4367                        x - width,
4368                        y - width,
4369                        2 * width + 1,
4370                        2 * width + 1);
4371       gdk_draw_rectangle (sheet->sheet_window,
4372                           sheet->xor_gc,
4373                           TRUE,
4374                           x - width + width / 2, y - width + width / 2,
4375                           2 + width, 2 + width);
4376     }
4377
4378   if (gtk_sheet_cell_isvisible (sheet, range.rowi, range.coli))
4379     {
4380       x = COLUMN_LEFT_XPIXEL (sheet, range.coli)+
4381         xxx_column_width (sheet, range.coli);
4382       y = ROW_TOP_YPIXEL (sheet, range.rowi)+
4383         yyy_row_height (sheet, range.rowi);
4384       width = 1;
4385       if (sheet->state == GTK_SHEET_RANGE_SELECTED) width = 3;
4386       if (sheet->state == GTK_SHEET_NORMAL) width = 3;
4387       gdk_draw_pixmap (sheet->sheet_window,
4388                        GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
4389                        sheet->pixmap,
4390                        x - width,
4391                        y - width,
4392                        x - width,
4393                        y - width,
4394                        2 * width + 1,
4395                        2 * width + 1);
4396       gdk_draw_rectangle (sheet->sheet_window,
4397                           sheet->xor_gc,
4398                           TRUE,
4399                           x - width + width / 2, y - width + width / 2,
4400                           2 + width, 2 + width);
4401
4402     }
4403
4404 }
4405
4406
4407 static void
4408 gtk_sheet_real_select_range (GtkSheet * sheet,
4409                              const GtkSheetRange * range)
4410 {
4411   gint state;
4412
4413   g_return_if_fail (sheet != NULL);
4414
4415   if (range == NULL) range = &sheet->range;
4416
4417   memcpy (&sheet->range, range, sizeof (*range));
4418
4419   if (range->row0 < 0 || range->rowi < 0) return;
4420   if (range->col0 < 0 || range->coli < 0) return;
4421
4422   state = sheet->state;
4423
4424   if (range->coli != sheet->range.coli || range->col0 != sheet->range.col0 ||
4425       range->rowi != sheet->range.rowi || range->row0 != sheet->range.row0)
4426     {
4427       gtk_sheet_new_selection (sheet, &sheet->range);
4428     }
4429   else
4430     {
4431       gtk_sheet_draw_backing_pixmap (sheet, sheet->range);
4432       gtk_sheet_range_draw_selection (sheet, sheet->range);
4433     }
4434
4435   gtk_sheet_update_primary_selection (sheet);
4436
4437   g_signal_emit (sheet, sheet_signals[SELECT_RANGE], 0, &sheet->range);
4438 }
4439
4440
4441 void
4442 gtk_sheet_get_selected_range            (GtkSheet *sheet,
4443                                          GtkSheetRange *range)
4444 {
4445   g_return_if_fail (sheet != NULL);
4446   *range = sheet->range;
4447 }
4448
4449
4450 void
4451 gtk_sheet_select_range (GtkSheet * sheet, const GtkSheetRange *range)
4452 {
4453   g_return_if_fail (sheet != NULL);
4454
4455   if (range == NULL) range=&sheet->range;
4456
4457   if (range->row0 < 0 || range->rowi < 0) return;
4458   if (range->col0 < 0 || range->coli < 0) return;
4459
4460
4461   if (sheet->state != GTK_SHEET_NORMAL)
4462     gtk_sheet_real_unselect_range (sheet, NULL);
4463   else
4464     gtk_sheet_deactivate_cell (sheet);
4465
4466   sheet->range.row0 = range->row0;
4467   sheet->range.rowi = range->rowi;
4468   sheet->range.col0 = range->col0;
4469   sheet->range.coli = range->coli;
4470   sheet->active_cell.row = range->row0;
4471   sheet->active_cell.col = range->col0;
4472   sheet->selection_cell.row = range->rowi;
4473   sheet->selection_cell.col = range->coli;
4474
4475   sheet->state = GTK_SHEET_RANGE_SELECTED;
4476   gtk_sheet_real_select_range (sheet, NULL);
4477
4478 }
4479
4480 void
4481 gtk_sheet_unselect_range (GtkSheet * sheet)
4482 {
4483   gtk_sheet_real_unselect_range (sheet, NULL);
4484   sheet->state = GTK_STATE_NORMAL;
4485
4486   gtk_sheet_activate_cell (sheet,
4487                            sheet->active_cell.row, sheet->active_cell.col);
4488 }
4489
4490
4491 static void
4492 gtk_sheet_real_unselect_range (GtkSheet * sheet,
4493                                const GtkSheetRange *range)
4494 {
4495   g_return_if_fail (sheet != NULL);
4496   g_return_if_fail (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)));
4497
4498   if ( range == NULL)
4499     range = &sheet->range;
4500
4501   if (range->row0 < 0 || range->rowi < 0) return;
4502   if (range->col0 < 0 || range->coli < 0) return;
4503
4504   g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, -1);
4505   g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, -1);
4506
4507   if (gtk_sheet_range_isvisible (sheet, *range))
4508     gtk_sheet_draw_backing_pixmap (sheet, *range);
4509
4510   sheet->range.row0 = -1;
4511   sheet->range.rowi = -1;
4512   sheet->range.col0 = -1;
4513   sheet->range.coli = -1;
4514
4515   gtk_sheet_position_children (sheet);
4516 }
4517
4518
4519 static gint
4520 gtk_sheet_expose (GtkWidget * widget,
4521                   GdkEventExpose * event)
4522 {
4523   GtkSheet *sheet;
4524   GtkSheetRange range;
4525
4526   g_return_val_if_fail (widget != NULL, FALSE);
4527   g_return_val_if_fail (GTK_IS_SHEET (widget), FALSE);
4528   g_return_val_if_fail (event != NULL, FALSE);
4529
4530
4531   sheet = GTK_SHEET (widget);
4532
4533   if (GTK_WIDGET_DRAWABLE (widget))
4534     {
4535       range.row0 = ROW_FROM_YPIXEL (sheet, event->area.y);
4536       range.col0 = COLUMN_FROM_XPIXEL (sheet, event->area.x);
4537       range.rowi = ROW_FROM_YPIXEL (sheet, event->area.y + event->area.height);
4538       range.coli = COLUMN_FROM_XPIXEL (sheet, event->area.x + event->area.width);
4539
4540       /* exposure events on the sheet */
4541       if (event->window == sheet->row_title_window &&
4542           sheet->row_titles_visible)
4543         {
4544           gint i;
4545           for (i = MIN_VISIBLE_ROW (sheet); i <= MAX_VISIBLE_ROW (sheet); i++)
4546             gtk_sheet_row_title_button_draw (sheet, i);
4547         }
4548
4549       if (event->window == sheet->column_title_window &&
4550           sheet->column_titles_visible)
4551         {
4552           gint i;
4553           for (i = MIN_VISIBLE_COLUMN (sheet); i <= MAX_VISIBLE_COLUMN (sheet); i++)
4554             gtk_sheet_column_title_button_draw (sheet, i);
4555         }
4556
4557       if (event->window == sheet->sheet_window)
4558         {
4559           gtk_sheet_draw_backing_pixmap (sheet, range);
4560
4561           if (sheet->state != GTK_SHEET_NORMAL)
4562             {
4563               if (gtk_sheet_range_isvisible (sheet, sheet->range))
4564                 gtk_sheet_draw_backing_pixmap (sheet, sheet->range);
4565               if (GTK_SHEET_IN_RESIZE (sheet) || GTK_SHEET_IN_DRAG (sheet))
4566                 gtk_sheet_draw_backing_pixmap (sheet, sheet->drag_range);
4567
4568               if (gtk_sheet_range_isvisible (sheet, sheet->range))
4569                 gtk_sheet_range_draw_selection (sheet, sheet->range);
4570               if (GTK_SHEET_IN_RESIZE (sheet) || GTK_SHEET_IN_DRAG (sheet))
4571                 draw_xor_rectangle (sheet, sheet->drag_range);
4572             }
4573
4574           if ((!GTK_SHEET_IN_XDRAG (sheet)) && (!GTK_SHEET_IN_YDRAG (sheet)))
4575             {
4576               if (sheet->state == GTK_SHEET_NORMAL)
4577                 {
4578                   gtk_sheet_draw_active_cell (sheet);
4579                   if (!GTK_SHEET_IN_SELECTION (sheet))
4580                     gtk_widget_queue_draw (sheet->sheet_entry);
4581                 }
4582             }
4583         }
4584     }
4585
4586   if (sheet->state != GTK_SHEET_NORMAL && GTK_SHEET_IN_SELECTION (sheet))
4587     gtk_widget_grab_focus (GTK_WIDGET (sheet));
4588
4589   (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
4590
4591   return FALSE;
4592 }
4593
4594
4595 static gboolean
4596 gtk_sheet_button_press (GtkWidget * widget,
4597                         GdkEventButton * event)
4598 {
4599   GtkSheet *sheet;
4600   GdkModifierType mods;
4601   gint x, y, row, column;
4602   gboolean veto;
4603
4604   g_return_val_if_fail (widget != NULL, FALSE);
4605   g_return_val_if_fail (GTK_IS_SHEET (widget), FALSE);
4606   g_return_val_if_fail (event != NULL, FALSE);
4607
4608   sheet = GTK_SHEET (widget);
4609
4610   /* Cancel any pending tooltips */
4611   if (sheet->motion_timer)
4612     {
4613       g_source_remove (sheet->motion_timer);
4614       sheet->motion_timer = 0;
4615     }
4616
4617   gtk_widget_get_pointer (widget, &x, &y);
4618   gtk_sheet_get_pixel_info (sheet, x, y, &row, &column);
4619
4620
4621   if (event->window == sheet->column_title_window)
4622     {
4623       g_signal_emit (sheet,
4624                      sheet_signals[BUTTON_EVENT_COLUMN], 0,
4625                      column, event);
4626
4627       if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
4628         g_signal_emit (sheet,
4629                        sheet_signals[DOUBLE_CLICK_COLUMN], 0, column);
4630
4631     }
4632   else if (event->window == sheet->row_title_window)
4633     {
4634       g_signal_emit (sheet,
4635                      sheet_signals[BUTTON_EVENT_ROW], 0,
4636                      row, event);
4637
4638       if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
4639         g_signal_emit (sheet,
4640                        sheet_signals[DOUBLE_CLICK_ROW], 0, row);
4641     }
4642
4643
4644   gdk_window_get_pointer (widget->window, NULL, NULL, &mods);
4645
4646   if (! (mods & GDK_BUTTON1_MASK)) return TRUE;
4647
4648
4649   /* press on resize windows */
4650   if (event->window == sheet->column_title_window &&
4651       gtk_sheet_columns_resizable (sheet))
4652     {
4653       gtk_widget_get_pointer (widget, &sheet->x_drag, NULL);
4654       if (POSSIBLE_XDRAG (sheet, sheet->x_drag, &sheet->drag_cell.col))
4655         {
4656           guint req;
4657           if (event->type == GDK_2BUTTON_PRESS)
4658             {
4659               gtk_sheet_autoresize_column (sheet, sheet->drag_cell.col);
4660               GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_XDRAG);
4661               return TRUE;
4662             }
4663           gtk_sheet_column_size_request (sheet, sheet->drag_cell.col, &req);
4664           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_XDRAG);
4665           gdk_pointer_grab (sheet->column_title_window, FALSE,
4666                             GDK_POINTER_MOTION_HINT_MASK |
4667                             GDK_BUTTON1_MOTION_MASK |
4668                             GDK_BUTTON_RELEASE_MASK,
4669                             NULL, NULL, event->time);
4670
4671           draw_xor_vline (sheet);
4672           return TRUE;
4673         }
4674     }
4675
4676   if (event->window == sheet->row_title_window && gtk_sheet_rows_resizable (sheet))
4677     {
4678       gtk_widget_get_pointer (widget, NULL, &sheet->y_drag);
4679
4680       if (POSSIBLE_YDRAG (sheet, sheet->y_drag, &sheet->drag_cell.row))
4681         {
4682           guint req;
4683           gtk_sheet_row_size_request (sheet, sheet->drag_cell.row, &req);
4684           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_YDRAG);
4685           gdk_pointer_grab (sheet->row_title_window, FALSE,
4686                             GDK_POINTER_MOTION_HINT_MASK |
4687                             GDK_BUTTON1_MOTION_MASK |
4688                             GDK_BUTTON_RELEASE_MASK,
4689                             NULL, NULL, event->time);
4690
4691           draw_xor_hline (sheet);
4692           return TRUE;
4693         }
4694     }
4695
4696   /* the sheet itself does not handle other than single click events */
4697   if (event->type != GDK_BUTTON_PRESS) return FALSE;
4698
4699   /* selections on the sheet */
4700   if (event->window == sheet->sheet_window)
4701     {
4702       gtk_widget_get_pointer (widget, &x, &y);
4703       gtk_sheet_get_pixel_info (sheet, x, y, &row, &column);
4704       gdk_pointer_grab (sheet->sheet_window, FALSE,
4705                         GDK_POINTER_MOTION_HINT_MASK |
4706                         GDK_BUTTON1_MOTION_MASK |
4707                         GDK_BUTTON_RELEASE_MASK,
4708                         NULL, NULL, event->time);
4709       gtk_grab_add (GTK_WIDGET (sheet));
4710
4711       /* This seems to be a kludge to work around a problem where the sheet
4712          scrolls to another position.  The timeout scrolls it back to its
4713          original posn.          JMD 3 July 2007
4714       */
4715       gtk_widget_grab_focus (GTK_WIDGET (sheet));
4716
4717       if (sheet->selection_mode != GTK_SELECTION_SINGLE &&
4718           sheet->selection_mode != GTK_SELECTION_NONE &&
4719           sheet->cursor_drag->type == GDK_SIZING &&
4720           !GTK_SHEET_IN_SELECTION (sheet) && !GTK_SHEET_IN_RESIZE (sheet))
4721         {
4722           if (sheet->state == GTK_STATE_NORMAL)
4723             {
4724               row = sheet->active_cell.row;
4725               column = sheet->active_cell.col;
4726               gtk_sheet_deactivate_cell (sheet);
4727               sheet->active_cell.row = row;
4728               sheet->active_cell.col = column;
4729               sheet->drag_range = sheet->range;
4730               sheet->state = GTK_SHEET_RANGE_SELECTED;
4731               gtk_sheet_select_range (sheet, &sheet->drag_range);
4732             }
4733           sheet->x_drag = x;
4734           sheet->y_drag = y;
4735           if (row > sheet->range.rowi) row--;
4736           if (column > sheet->range.coli) column--;
4737           sheet->drag_cell.row = row;
4738           sheet->drag_cell.col = column;
4739           sheet->drag_range = sheet->range;
4740           draw_xor_rectangle (sheet, sheet->drag_range);
4741           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_RESIZE);
4742         }
4743       else if (sheet->cursor_drag->type == GDK_TOP_LEFT_ARROW &&
4744                !GTK_SHEET_IN_SELECTION (sheet)
4745                && ! GTK_SHEET_IN_DRAG (sheet)
4746                && sheet->active_cell.row >= 0
4747                && sheet->active_cell.col >= 0
4748                )
4749         {
4750           if (sheet->state == GTK_STATE_NORMAL)
4751             {
4752               row = sheet->active_cell.row;
4753               column = sheet->active_cell.col;
4754               gtk_sheet_deactivate_cell (sheet);
4755               sheet->active_cell.row = row;
4756               sheet->active_cell.col = column;
4757               sheet->drag_range = sheet->range;
4758               sheet->state = GTK_SHEET_RANGE_SELECTED;
4759               gtk_sheet_select_range (sheet, &sheet->drag_range);
4760             }
4761           sheet->x_drag = x;
4762           sheet->y_drag = y;
4763           if (row < sheet->range.row0) row++;
4764           if (row > sheet->range.rowi) row--;
4765           if (column < sheet->range.col0) column++;
4766           if (column > sheet->range.coli) column--;
4767           sheet->drag_cell.row = row;
4768           sheet->drag_cell.col = column;
4769           sheet->drag_range = sheet->range;
4770           draw_xor_rectangle (sheet, sheet->drag_range);
4771           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_DRAG);
4772         }
4773       else
4774         {
4775           gtk_sheet_click_cell (sheet, row, column, &veto);
4776           if (veto) GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
4777         }
4778     }
4779
4780   if (event->window == sheet->column_title_window)
4781     {
4782       gtk_widget_get_pointer (widget, &x, &y);
4783       column = COLUMN_FROM_XPIXEL (sheet, x);
4784
4785       if (xxx_column_is_sensitive (sheet, column))
4786         {
4787           gtk_sheet_click_cell (sheet, - 1, column, &veto);
4788           gtk_grab_add (GTK_WIDGET (sheet));
4789           gtk_widget_grab_focus (GTK_WIDGET (sheet));
4790           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
4791         }
4792     }
4793
4794   if (event->window == sheet->row_title_window)
4795     {
4796       gtk_widget_get_pointer (widget, &x, &y);
4797       row = ROW_FROM_YPIXEL (sheet, y);
4798       if (yyy_row_is_sensitive (sheet, row))
4799         {
4800           gtk_sheet_click_cell (sheet, row, - 1, &veto);
4801           gtk_grab_add (GTK_WIDGET (sheet));
4802           gtk_widget_grab_focus (GTK_WIDGET (sheet));
4803           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
4804         }
4805     }
4806
4807   return TRUE;
4808 }
4809
4810 static void
4811 gtk_sheet_click_cell (GtkSheet *sheet, gint row, gint column, gboolean *veto)
4812 {
4813   *veto = TRUE;
4814
4815   if (row >= yyy_row_count (sheet) || column >= xxx_column_count (sheet))
4816     {
4817       *veto = FALSE;
4818       return;
4819     }
4820
4821   if (column >= 0 && row >= 0)
4822     if (! xxx_column_is_visible (sheet, column) || !yyy_row_is_visible (sheet, row))
4823       {
4824         *veto = FALSE;
4825         return;
4826       }
4827
4828   _gtkextra_signal_emit (GTK_OBJECT (sheet), sheet_signals[TRAVERSE],
4829                          sheet->active_cell.row, sheet->active_cell.col,
4830                          &row, &column, veto);
4831
4832   if (!*veto)
4833     {
4834       if (sheet->state == GTK_STATE_NORMAL) return;
4835
4836       row = sheet->active_cell.row;
4837       column = sheet->active_cell.col;
4838
4839       gtk_sheet_activate_cell (sheet, row, column);
4840       return;
4841     }
4842
4843   if (row == -1 && column >= 0)
4844     {
4845       if (gtk_sheet_autoscroll (sheet))
4846         gtk_sheet_move_query (sheet, row, column);
4847       gtk_sheet_select_column (sheet, column);
4848       return;
4849     }
4850   if (column == -1 && row >= 0)
4851     {
4852       if (gtk_sheet_autoscroll (sheet))
4853         gtk_sheet_move_query (sheet, row, column);
4854       gtk_sheet_select_row (sheet, row);
4855       return;
4856     }
4857
4858   if (row == - 1 && column == - 1)
4859     {
4860       sheet->range.row0 = 0;
4861       sheet->range.col0 = 0;
4862       sheet->range.rowi = yyy_row_count (sheet) - 1;
4863       sheet->range.coli = xxx_column_count (sheet) - 1;
4864       sheet->active_cell.row = 0;
4865       sheet->active_cell.col = 0;
4866       gtk_sheet_select_range (sheet, NULL);
4867       return;
4868     }
4869
4870   if (row != -1 && column != -1)
4871     {
4872       if (sheet->state != GTK_SHEET_NORMAL)
4873         {
4874           sheet->state = GTK_SHEET_NORMAL;
4875           gtk_sheet_real_unselect_range (sheet, NULL);
4876         }
4877       else
4878         {
4879           gtk_sheet_deactivate_cell (sheet);
4880           gtk_sheet_activate_cell (sheet, row, column);
4881         }
4882
4883       if (gtk_sheet_autoscroll (sheet))
4884         gtk_sheet_move_query (sheet, row, column);
4885       sheet->active_cell.row = row;
4886       sheet->active_cell.col = column;
4887       sheet->selection_cell.row = row;
4888       sheet->selection_cell.col = column;
4889       sheet->range.row0 = row;
4890       sheet->range.col0 = column;
4891       sheet->range.rowi = row;
4892       sheet->range.coli = column;
4893       sheet->state = GTK_SHEET_NORMAL;
4894       GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
4895       gtk_sheet_draw_active_cell (sheet);
4896       return;
4897     }
4898
4899   g_assert_not_reached ();
4900   gtk_sheet_activate_cell (sheet, sheet->active_cell.row,
4901                            sheet->active_cell.col);
4902 }
4903
4904 static gint
4905 gtk_sheet_button_release (GtkWidget * widget,
4906                           GdkEventButton * event)
4907 {
4908   GtkSheet *sheet;
4909   gint x, y;
4910
4911   sheet = GTK_SHEET (widget);
4912
4913   /* release on resize windows */
4914   if (GTK_SHEET_IN_XDRAG (sheet))
4915     {
4916       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_XDRAG);
4917       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
4918       gtk_widget_get_pointer (widget, &x, NULL);
4919       gdk_pointer_ungrab (event->time);
4920       draw_xor_vline (sheet);
4921
4922       gtk_sheet_set_column_width (sheet, sheet->drag_cell.col,
4923                                   new_column_width (sheet, sheet->drag_cell.col, &x));
4924       sheet->old_hadjustment = -1.;
4925       g_signal_emit_by_name (sheet->hadjustment, "value_changed");
4926       return TRUE;
4927     }
4928
4929   if (GTK_SHEET_IN_YDRAG (sheet))
4930     {
4931       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_YDRAG);
4932       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
4933       gtk_widget_get_pointer (widget, NULL, &y);
4934       gdk_pointer_ungrab (event->time);
4935       draw_xor_hline (sheet);
4936
4937       gtk_sheet_set_row_height (sheet, sheet->drag_cell.row, new_row_height (sheet, sheet->drag_cell.row, &y));
4938       sheet->old_vadjustment = -1.;
4939       g_signal_emit_by_name (sheet->vadjustment, "value_changed");
4940       return TRUE;
4941     }
4942
4943
4944   if (GTK_SHEET_IN_DRAG (sheet))
4945     {
4946       GtkSheetRange old_range;
4947       draw_xor_rectangle (sheet, sheet->drag_range);
4948       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_DRAG);
4949       gdk_pointer_ungrab (event->time);
4950
4951       gtk_sheet_real_unselect_range (sheet, NULL);
4952
4953       sheet->active_cell.row = sheet->active_cell.row +
4954         (sheet->drag_range.row0 - sheet->range.row0);
4955       sheet->active_cell.col = sheet->active_cell.col +
4956         (sheet->drag_range.col0 - sheet->range.col0);
4957       sheet->selection_cell.row = sheet->selection_cell.row +
4958         (sheet->drag_range.row0 - sheet->range.row0);
4959       sheet->selection_cell.col = sheet->selection_cell.col +
4960         (sheet->drag_range.col0 - sheet->range.col0);
4961       old_range = sheet->range;
4962       sheet->range = sheet->drag_range;
4963       sheet->drag_range = old_range;
4964       g_signal_emit (sheet, sheet_signals[MOVE_RANGE], 0,
4965                      &sheet->drag_range, &sheet->range);
4966       gtk_sheet_select_range (sheet, &sheet->range);
4967     }
4968
4969   if (GTK_SHEET_IN_RESIZE (sheet))
4970     {
4971       GtkSheetRange old_range;
4972       draw_xor_rectangle (sheet, sheet->drag_range);
4973       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_RESIZE);
4974       gdk_pointer_ungrab (event->time);
4975
4976       gtk_sheet_real_unselect_range (sheet, NULL);
4977
4978       sheet->active_cell.row = sheet->active_cell.row +
4979         (sheet->drag_range.row0 - sheet->range.row0);
4980       sheet->active_cell.col = sheet->active_cell.col +
4981         (sheet->drag_range.col0 - sheet->range.col0);
4982       if (sheet->drag_range.row0 < sheet->range.row0)
4983         sheet->selection_cell.row = sheet->drag_range.row0;
4984       if (sheet->drag_range.rowi >= sheet->range.rowi)
4985         sheet->selection_cell.row = sheet->drag_range.rowi;
4986       if (sheet->drag_range.col0 < sheet->range.col0)
4987         sheet->selection_cell.col = sheet->drag_range.col0;
4988       if (sheet->drag_range.coli >= sheet->range.coli)
4989         sheet->selection_cell.col = sheet->drag_range.coli;
4990       old_range = sheet->range;
4991       sheet->range = sheet->drag_range;
4992       sheet->drag_range = old_range;
4993
4994       if (sheet->state == GTK_STATE_NORMAL) sheet->state = GTK_SHEET_RANGE_SELECTED;
4995       g_signal_emit (sheet, sheet_signals[RESIZE_RANGE], 0,
4996                      &sheet->drag_range, &sheet->range);
4997       gtk_sheet_select_range (sheet, &sheet->range);
4998     }
4999
5000   if (sheet->state == GTK_SHEET_NORMAL && GTK_SHEET_IN_SELECTION (sheet))
5001     {
5002       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
5003       gdk_pointer_ungrab (event->time);
5004       gtk_sheet_activate_cell (sheet, sheet->active_cell.row,
5005                                sheet->active_cell.col);
5006     }
5007
5008   if (GTK_SHEET_IN_SELECTION)
5009     gdk_pointer_ungrab (event->time);
5010   gtk_grab_remove (GTK_WIDGET (sheet));
5011
5012   GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
5013
5014   return TRUE;
5015 }
5016
5017 /* Shamelessly lifted from gtktooltips */
5018 static gboolean
5019 gtk_sheet_subtitle_paint_window (GtkWidget *tip_window)
5020 {
5021   GtkRequisition req;
5022
5023   gtk_widget_size_request (tip_window, &req);
5024   gtk_paint_flat_box (tip_window->style, tip_window->window,
5025                       GTK_STATE_NORMAL, GTK_SHADOW_OUT,
5026                       NULL, GTK_WIDGET(tip_window), "tooltip",
5027                       0, 0, req.width, req.height);
5028
5029   return FALSE;
5030 }
5031
5032 static GtkSheetHoverTitle *
5033 create_hover_window (void)
5034 {
5035   GtkSheetHoverTitle *hw = malloc (sizeof (*hw));
5036
5037   hw->window = gtk_window_new (GTK_WINDOW_POPUP);
5038
5039 #if GTK_CHECK_VERSION (2, 9, 0)
5040   gtk_window_set_type_hint (GTK_WINDOW (hw->window),
5041                             GDK_WINDOW_TYPE_HINT_TOOLTIP);
5042 #endif
5043
5044   gtk_widget_set_app_paintable (hw->window, TRUE);
5045   gtk_window_set_resizable (GTK_WINDOW (hw->window), FALSE);
5046   gtk_widget_set_name (hw->window, "gtk-tooltips");
5047   gtk_container_set_border_width (GTK_CONTAINER (hw->window), 4);
5048
5049   g_signal_connect (hw->window,
5050                     "expose_event",
5051                     G_CALLBACK (gtk_sheet_subtitle_paint_window),
5052                     NULL);
5053
5054   hw->label = gtk_label_new (NULL);
5055
5056
5057   gtk_label_set_line_wrap (GTK_LABEL (hw->label), TRUE);
5058   gtk_misc_set_alignment (GTK_MISC (hw->label), 0.5, 0.5);
5059
5060   gtk_container_add (GTK_CONTAINER (hw->window), hw->label);
5061
5062   gtk_widget_show (hw->label);
5063
5064   g_signal_connect (hw->window,
5065                     "destroy",
5066                     G_CALLBACK (gtk_widget_destroyed),
5067                     &hw->window);
5068
5069   return hw;
5070 }
5071
5072 #define HOVER_WINDOW_Y_OFFSET 2
5073
5074 static void
5075 show_subtitle (GtkSheet *sheet, gint row, gint column, const gchar *subtitle)
5076 {
5077   gint x, y;
5078   gint px, py;
5079   gint width;
5080
5081   if ( ! subtitle )
5082     return;
5083
5084   if ( ! sheet->hover_window)
5085     {
5086       sheet->hover_window = create_hover_window ();
5087       gtk_widget_add_events (GTK_WIDGET (sheet), GDK_LEAVE_NOTIFY_MASK);
5088
5089       g_signal_connect_swapped (sheet, "leave-notify-event",
5090                                 G_CALLBACK (gtk_widget_hide),
5091                                 sheet->hover_window->window);
5092     }
5093
5094   gtk_label_set_text (GTK_LABEL (sheet->hover_window->label),
5095                       subtitle);
5096
5097
5098   sheet->hover_window->row = row;
5099   sheet->hover_window->column = column;
5100
5101   gdk_window_get_origin (GTK_WIDGET (sheet)->window, &x, &y);
5102
5103   gtk_widget_get_pointer (GTK_WIDGET (sheet), &px, &py);
5104
5105   gtk_widget_show (sheet->hover_window->window);
5106
5107   width = GTK_WIDGET (sheet->hover_window->label)->allocation.width;
5108
5109   if (row == -1 )
5110     {
5111       x += px;
5112       x -= width / 2;
5113       y += sheet->column_title_area.y;
5114       y += sheet->column_title_area.height;
5115       y += HOVER_WINDOW_Y_OFFSET;
5116     }
5117
5118   if ( column == -1 )
5119     {
5120       y += py;
5121       x += sheet->row_title_area.x;
5122       x += sheet->row_title_area.width * 2 / 3.0;
5123     }
5124
5125   gtk_window_move (GTK_WINDOW (sheet->hover_window->window),
5126                    x, y);
5127 }
5128
5129 static gboolean
5130 motion_timeout_callback (gpointer data)
5131 {
5132   GtkSheet *sheet = GTK_SHEET (data);
5133   gint x, y;
5134   gint row, column;
5135   gtk_widget_get_pointer (GTK_WIDGET (sheet), &x, &y);
5136
5137   if ( gtk_sheet_get_pixel_info (sheet, x, y, &row, &column) )
5138     {
5139       if ( column == -1 && row == -1 )
5140         return FALSE;
5141
5142       if ( column == -1)
5143         {
5144           GSheetRow *row_geo = sheet->row_geometry;
5145           gchar *text;
5146
5147           text = g_sheet_row_get_subtitle (row_geo, row);
5148
5149           show_subtitle (sheet, row, column, text);
5150           g_free (text);
5151         }
5152
5153       if ( row == -1)
5154         {
5155           GSheetColumn *col_geo = sheet->column_geometry;
5156           gchar *text;
5157
5158           text = g_sheet_column_get_subtitle (col_geo, column);
5159
5160           show_subtitle (sheet, row, column, text );
5161
5162           g_free (text);
5163         }
5164     }
5165
5166   return FALSE;
5167 }
5168
5169 static gint
5170 gtk_sheet_motion (GtkWidget * widget,
5171                   GdkEventMotion * event)
5172 {
5173   GtkSheet *sheet;
5174   GdkModifierType mods;
5175   GdkCursorType new_cursor;
5176   gint x, y;
5177   gint row, column;
5178
5179   g_return_val_if_fail (widget != NULL, FALSE);
5180   g_return_val_if_fail (GTK_IS_SHEET (widget), FALSE);
5181   g_return_val_if_fail (event != NULL, FALSE);
5182
5183   sheet = GTK_SHEET (widget);
5184
5185   /* selections on the sheet */
5186   x = event->x;
5187   y = event->y;
5188
5189   if (!sheet->hover_window || ! GTK_WIDGET_VISIBLE (sheet->hover_window->window))
5190     {
5191       if ( sheet->motion_timer > 0 )
5192         g_source_remove (sheet->motion_timer);
5193       sheet->motion_timer = g_timeout_add (TIMEOUT_HOVER, motion_timeout_callback, sheet);
5194     }
5195   else
5196     {
5197       gint row, column;
5198       gint wx, wy;
5199       gtk_widget_get_pointer (widget, &wx, &wy);
5200
5201       if ( gtk_sheet_get_pixel_info (sheet, wx, wy, &row, &column) )
5202         {
5203           if ( row != sheet->hover_window->row || column != sheet->hover_window->column)
5204             {
5205               gtk_widget_hide (sheet->hover_window->window);
5206             }
5207         }
5208     }
5209
5210   if (event->window == sheet->column_title_window &&
5211       gtk_sheet_columns_resizable (sheet))
5212     {
5213       gtk_widget_get_pointer (widget, &x, &y);
5214       if (!GTK_SHEET_IN_SELECTION (sheet) &&
5215           POSSIBLE_XDRAG (sheet, x, &column))
5216         {
5217           new_cursor = GDK_SB_H_DOUBLE_ARROW;
5218           if (new_cursor != sheet->cursor_drag->type)
5219             {
5220               gdk_cursor_destroy (sheet->cursor_drag);
5221               sheet->cursor_drag = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW);
5222               gdk_window_set_cursor (sheet->column_title_window,
5223                                      sheet->cursor_drag);
5224             }
5225         }
5226       else
5227         {
5228           new_cursor = GDK_TOP_LEFT_ARROW;
5229           if (!GTK_SHEET_IN_XDRAG (sheet) &&
5230               new_cursor != sheet->cursor_drag->type)
5231             {
5232               gdk_cursor_destroy (sheet->cursor_drag);
5233               sheet->cursor_drag = gdk_cursor_new (GDK_TOP_LEFT_ARROW);
5234               gdk_window_set_cursor (sheet->column_title_window,
5235                                      sheet->cursor_drag);
5236             }
5237         }
5238     }
5239
5240   if (event->window == sheet->row_title_window &&
5241       gtk_sheet_rows_resizable (sheet))
5242     {
5243       gtk_widget_get_pointer (widget, &x, &y);
5244       if (!GTK_SHEET_IN_SELECTION (sheet) && POSSIBLE_YDRAG (sheet, y, &column))
5245         {
5246           new_cursor = GDK_SB_V_DOUBLE_ARROW;
5247           if (new_cursor != sheet->cursor_drag->type)
5248             {
5249               gdk_cursor_destroy (sheet->cursor_drag);
5250               sheet->cursor_drag = gdk_cursor_new (GDK_SB_V_DOUBLE_ARROW);
5251               gdk_window_set_cursor (sheet->row_title_window, sheet->cursor_drag);
5252             }
5253         }
5254       else
5255         {
5256           new_cursor = GDK_TOP_LEFT_ARROW;
5257           if (!GTK_SHEET_IN_YDRAG (sheet) &&
5258               new_cursor != sheet->cursor_drag->type)
5259             {
5260               gdk_cursor_destroy (sheet->cursor_drag);
5261               sheet->cursor_drag = gdk_cursor_new (GDK_TOP_LEFT_ARROW);
5262               gdk_window_set_cursor (sheet->row_title_window, sheet->cursor_drag);
5263             }
5264         }
5265     }
5266
5267   new_cursor = GDK_PLUS;
5268   if ( event->window == sheet->sheet_window &&
5269        !POSSIBLE_DRAG (sheet, x, y, &row, &column) &&
5270        !GTK_SHEET_IN_DRAG (sheet) &&
5271        !POSSIBLE_RESIZE (sheet, x, y, &row, &column) &&
5272        !GTK_SHEET_IN_RESIZE (sheet) &&
5273        new_cursor != sheet->cursor_drag->type)
5274     {
5275       gdk_cursor_destroy (sheet->cursor_drag);
5276       sheet->cursor_drag = gdk_cursor_new (GDK_PLUS);
5277       gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
5278     }
5279
5280   new_cursor = GDK_TOP_LEFT_ARROW;
5281   if ( event->window == sheet->sheet_window &&
5282        ! (POSSIBLE_RESIZE (sheet, x, y, &row, &column) || GTK_SHEET_IN_RESIZE (sheet)) && (POSSIBLE_DRAG (sheet, x, y, &row, &column) || GTK_SHEET_IN_DRAG (sheet)) &&
5283
5284        new_cursor != sheet->cursor_drag->type)
5285     {
5286       gdk_cursor_destroy (sheet->cursor_drag);
5287       sheet->cursor_drag = gdk_cursor_new (GDK_TOP_LEFT_ARROW);
5288       gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
5289     }
5290
5291   new_cursor = GDK_SIZING;
5292   if ( event->window == sheet->sheet_window &&
5293        sheet->selection_mode != GTK_SELECTION_NONE &&
5294        !GTK_SHEET_IN_DRAG (sheet) &&
5295        (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
5296         GTK_SHEET_IN_RESIZE (sheet)) &&
5297        new_cursor != sheet->cursor_drag->type)
5298     {
5299       gdk_cursor_destroy (sheet->cursor_drag);
5300       sheet->cursor_drag = gdk_cursor_new (GDK_SIZING);
5301       gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
5302     }
5303
5304
5305   gdk_window_get_pointer (widget->window, &x, &y, &mods);
5306   if (! (mods & GDK_BUTTON1_MASK)) return FALSE;
5307
5308   if (GTK_SHEET_IN_XDRAG (sheet))
5309     {
5310       if (event->is_hint || event->window != widget->window)
5311         gtk_widget_get_pointer (widget, &x, NULL);
5312       else
5313         x = event->x;
5314
5315       new_column_width (sheet, sheet->drag_cell.col, &x);
5316       if (x != sheet->x_drag)
5317         {
5318           draw_xor_vline (sheet);
5319           sheet->x_drag = x;
5320           draw_xor_vline (sheet);
5321         }
5322       return TRUE;
5323     }
5324
5325   if (GTK_SHEET_IN_YDRAG (sheet))
5326     {
5327       if (event->is_hint || event->window != widget->window)
5328         gtk_widget_get_pointer (widget, NULL, &y);
5329       else
5330         y = event->y;
5331
5332       new_row_height (sheet, sheet->drag_cell.row, &y);
5333       if (y != sheet->y_drag)
5334         {
5335           draw_xor_hline (sheet);
5336           sheet->y_drag = y;
5337           draw_xor_hline (sheet);
5338         }
5339       return TRUE;
5340     }
5341
5342   if (GTK_SHEET_IN_DRAG (sheet))
5343     {
5344       GtkSheetRange aux;
5345       column = COLUMN_FROM_XPIXEL (sheet, x)- sheet->drag_cell.col;
5346       row = ROW_FROM_YPIXEL (sheet, y)- sheet->drag_cell.row;
5347       if (sheet->state == GTK_SHEET_COLUMN_SELECTED) row = 0;
5348       if (sheet->state == GTK_SHEET_ROW_SELECTED) column = 0;
5349       sheet->x_drag = x;
5350       sheet->y_drag = y;
5351       aux = sheet->range;
5352       if (aux.row0 + row >= 0 && aux.rowi + row < yyy_row_count (sheet) &&
5353           aux.col0 + column >= 0 && aux.coli + column < xxx_column_count (sheet))
5354         {
5355           aux = sheet->drag_range;
5356           sheet->drag_range.row0 = sheet->range.row0 + row;
5357           sheet->drag_range.col0 = sheet->range.col0 + column;
5358           sheet->drag_range.rowi = sheet->range.rowi + row;
5359           sheet->drag_range.coli = sheet->range.coli + column;
5360           if (aux.row0 != sheet->drag_range.row0 ||
5361               aux.col0 != sheet->drag_range.col0)
5362             {
5363               draw_xor_rectangle (sheet, aux);
5364               draw_xor_rectangle (sheet, sheet->drag_range);
5365             }
5366         }
5367       return TRUE;
5368     }
5369
5370   if (GTK_SHEET_IN_RESIZE (sheet))
5371     {
5372       GtkSheetRange aux;
5373       gint v_h, current_col, current_row, col_threshold, row_threshold;
5374       v_h = 1;
5375
5376       if (abs (x - COLUMN_LEFT_XPIXEL (sheet, sheet->drag_cell.col)) >
5377           abs (y - ROW_TOP_YPIXEL (sheet, sheet->drag_cell.row))) v_h = 2;
5378
5379       current_col = COLUMN_FROM_XPIXEL (sheet, x);
5380       current_row = ROW_FROM_YPIXEL (sheet, y);
5381       column = current_col - sheet->drag_cell.col;
5382       row = current_row - sheet->drag_cell.row;
5383
5384       /*use half of column width resp. row height as threshold to
5385         expand selection*/
5386       col_threshold = COLUMN_LEFT_XPIXEL (sheet, current_col) +
5387         xxx_column_width (sheet, current_col) / 2;
5388       if (column > 0)
5389         {
5390           if (x < col_threshold)
5391             column -= 1;
5392         }
5393       else if (column < 0)
5394         {
5395           if (x > col_threshold)
5396             column +=1;
5397         }
5398       row_threshold = ROW_TOP_YPIXEL (sheet, current_row) +
5399         yyy_row_height (sheet, current_row)/2;
5400       if (row > 0)
5401         {
5402           if (y < row_threshold)
5403             row -= 1;
5404         }
5405       else if (row < 0)
5406         {
5407           if (y > row_threshold)
5408             row +=1;
5409         }
5410
5411       if (sheet->state == GTK_SHEET_COLUMN_SELECTED) row = 0;
5412       if (sheet->state == GTK_SHEET_ROW_SELECTED) column = 0;
5413       sheet->x_drag = x;
5414       sheet->y_drag = y;
5415       aux = sheet->range;
5416
5417       if (v_h == 1)
5418         column = 0;
5419       else
5420         row = 0;
5421
5422       if (aux.row0 + row >= 0 && aux.rowi + row < yyy_row_count (sheet) &&
5423           aux.col0 + column >= 0 && aux.coli + column < xxx_column_count (sheet))
5424         {
5425           aux = sheet->drag_range;
5426           sheet->drag_range = sheet->range;
5427
5428           if (row < 0) sheet->drag_range.row0 = sheet->range.row0 + row;
5429           if (row > 0) sheet->drag_range.rowi = sheet->range.rowi + row;
5430           if (column < 0) sheet->drag_range.col0 = sheet->range.col0 + column;
5431           if (column > 0) sheet->drag_range.coli = sheet->range.coli + column;
5432
5433           if (aux.row0 != sheet->drag_range.row0 ||
5434               aux.rowi != sheet->drag_range.rowi ||
5435               aux.col0 != sheet->drag_range.col0 ||
5436               aux.coli != sheet->drag_range.coli)
5437             {
5438               draw_xor_rectangle (sheet, aux);
5439               draw_xor_rectangle (sheet, sheet->drag_range);
5440             }
5441         }
5442       return TRUE;
5443     }
5444
5445   gtk_sheet_get_pixel_info (sheet, x, y, &row, &column);
5446
5447   if (sheet->state == GTK_SHEET_NORMAL && row == sheet->active_cell.row &&
5448       column == sheet->active_cell.col) return TRUE;
5449
5450   if (GTK_SHEET_IN_SELECTION (sheet) && mods&GDK_BUTTON1_MASK)
5451     gtk_sheet_extend_selection (sheet, row, column);
5452
5453   return TRUE;
5454 }
5455
5456 static gboolean
5457 gtk_sheet_move_query (GtkSheet *sheet, gint row, gint column)
5458 {
5459   gint row_move, column_move;
5460   gfloat row_align, col_align;
5461   guint height, width;
5462   gint new_row = row;
5463   gint new_col = column;
5464
5465   row_move = FALSE;
5466   column_move = FALSE;
5467   row_align = -1.;
5468   col_align = -1.;
5469
5470   height = sheet->sheet_window_height;
5471   width = sheet->sheet_window_width;
5472
5473   if (row >= MAX_VISIBLE_ROW (sheet) && sheet->state != GTK_SHEET_COLUMN_SELECTED)
5474     {
5475       row_align = 1.;
5476       new_row = MIN (yyy_row_count (sheet) - 1, row + 1);
5477       row_move = TRUE;
5478       if (MAX_VISIBLE_ROW (sheet) == yyy_row_count (sheet) - 1 &&
5479           ROW_TOP_YPIXEL (sheet, yyy_row_count (sheet)- 1) +
5480           yyy_row_height (sheet, yyy_row_count (sheet)- 1) < height)
5481         {
5482           row_move = FALSE;
5483           row_align = -1.;
5484         }
5485     }
5486   if (row < MIN_VISIBLE_ROW (sheet) && sheet->state != GTK_SHEET_COLUMN_SELECTED)
5487     {
5488       row_align= 0.;
5489       row_move = TRUE;
5490     }
5491   if (column >= MAX_VISIBLE_COLUMN (sheet) && sheet->state != GTK_SHEET_ROW_SELECTED)
5492     {
5493       col_align = 1.;
5494       new_col = MIN (xxx_column_count (sheet) - 1, column + 1);
5495       column_move = TRUE;
5496       if (MAX_VISIBLE_COLUMN (sheet) == (xxx_column_count (sheet) - 1) &&
5497           COLUMN_LEFT_XPIXEL (sheet, xxx_column_count (sheet) - 1) +
5498           xxx_column_width (sheet, xxx_column_count (sheet) - 1) < width)
5499         {
5500           column_move = FALSE;
5501           col_align = -1.;
5502         }
5503     }
5504   if (column < MIN_VISIBLE_COLUMN (sheet) && sheet->state != GTK_SHEET_ROW_SELECTED)
5505     {
5506       col_align = 0.;
5507       column_move = TRUE;
5508     }
5509
5510   if (row_move || column_move)
5511     {
5512       gtk_sheet_moveto (sheet, new_row, new_col, row_align, col_align);
5513     }
5514
5515   return (row_move || column_move);
5516 }
5517
5518 static void
5519 gtk_sheet_extend_selection (GtkSheet *sheet, gint row, gint column)
5520 {
5521   GtkSheetRange range;
5522   gint state;
5523   gint r, c;
5524
5525   if (row == sheet->selection_cell.row && column == sheet->selection_cell.col)
5526     return;
5527
5528   if (sheet->selection_mode == GTK_SELECTION_SINGLE) return;
5529
5530   gtk_sheet_move_query (sheet, row, column);
5531   gtk_widget_grab_focus (GTK_WIDGET (sheet));
5532
5533   if (GTK_SHEET_IN_DRAG (sheet)) return;
5534
5535   state = sheet->state;
5536
5537   switch (sheet->state)
5538     {
5539     case GTK_SHEET_ROW_SELECTED:
5540       column = xxx_column_count (sheet) - 1;
5541       break;
5542     case GTK_SHEET_COLUMN_SELECTED:
5543       row = yyy_row_count (sheet) - 1;
5544       break;
5545     case GTK_SHEET_NORMAL:
5546       sheet->state = GTK_SHEET_RANGE_SELECTED;
5547       r = sheet->active_cell.row;
5548       c = sheet->active_cell.col;
5549       sheet->range.col0 = c;
5550       sheet->range.row0 = r;
5551       sheet->range.coli = c;
5552       sheet->range.rowi = r;
5553       gdk_draw_pixmap (sheet->sheet_window,
5554                        GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
5555                        sheet->pixmap,
5556                        COLUMN_LEFT_XPIXEL (sheet, c)- 1,
5557                        ROW_TOP_YPIXEL (sheet, r)- 1,
5558                        COLUMN_LEFT_XPIXEL (sheet, c)- 1,
5559                        ROW_TOP_YPIXEL (sheet, r)- 1,
5560                        xxx_column_width (sheet, c)+4,
5561                        yyy_row_height (sheet, r)+4);
5562       gtk_sheet_range_draw_selection (sheet, sheet->range);
5563     case GTK_SHEET_RANGE_SELECTED:
5564       sheet->state = GTK_SHEET_RANGE_SELECTED;
5565     }
5566
5567   sheet->selection_cell.row = row;
5568   sheet->selection_cell.col = column;
5569
5570   range.col0 = MIN (column, sheet->active_cell.col);
5571   range.coli = MAX (column, sheet->active_cell.col);
5572   range.row0 = MIN (row, sheet->active_cell.row);
5573   range.rowi = MAX (row, sheet->active_cell.row);
5574
5575   if (range.row0 != sheet->range.row0 || range.rowi != sheet->range.rowi ||
5576       range.col0 != sheet->range.col0 || range.coli != sheet->range.coli ||
5577       state == GTK_SHEET_NORMAL)
5578     gtk_sheet_real_select_range (sheet, &range);
5579
5580 }
5581
5582 static gint
5583 gtk_sheet_entry_key_press (GtkWidget *widget,
5584                            GdkEventKey *key)
5585 {
5586   gboolean focus;
5587   g_signal_emit_by_name (widget, "key_press_event", key, &focus);
5588   return focus;
5589 }
5590
5591 static gint
5592 gtk_sheet_key_press (GtkWidget *widget,
5593                      GdkEventKey *key)
5594 {
5595   GtkSheet *sheet;
5596   gint row, col;
5597   gint state;
5598   gboolean extend_selection = FALSE;
5599   gboolean force_move = FALSE;
5600   gboolean in_selection = FALSE;
5601   gboolean veto = TRUE;
5602   gint scroll = 1;
5603
5604   sheet = GTK_SHEET (widget);
5605
5606   if (key->state & GDK_CONTROL_MASK || key->keyval == GDK_Control_L ||
5607       key->keyval == GDK_Control_R) return FALSE;
5608
5609   extend_selection = (key->state & GDK_SHIFT_MASK) || key->keyval == GDK_Shift_L
5610     || key->keyval == GDK_Shift_R;
5611
5612   state = sheet->state;
5613   in_selection = GTK_SHEET_IN_SELECTION (sheet);
5614   GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
5615
5616   switch (key->keyval)
5617     {
5618     case GDK_Return: case GDK_KP_Enter:
5619       if (sheet->state == GTK_SHEET_NORMAL &&
5620           !GTK_SHEET_IN_SELECTION (sheet))
5621         g_signal_stop_emission_by_name (gtk_sheet_get_entry (sheet),
5622                                         "key-press-event");
5623       row = sheet->active_cell.row;
5624       col = sheet->active_cell.col;
5625       if (sheet->state == GTK_SHEET_COLUMN_SELECTED)
5626         row = MIN_VISIBLE_ROW (sheet)- 1;
5627       if (sheet->state == GTK_SHEET_ROW_SELECTED)
5628         col = MIN_VISIBLE_COLUMN (sheet);
5629       if (row < yyy_row_count (sheet) - 1)
5630         {
5631           row = row + scroll;
5632           while (!yyy_row_is_visible (sheet, row) && row < yyy_row_count (sheet)- 1)
5633             row++;
5634         }
5635       gtk_sheet_click_cell (sheet, row, col, &veto);
5636       extend_selection = FALSE;
5637       break;
5638     case GDK_ISO_Left_Tab:
5639       row = sheet->active_cell.row;
5640       col = sheet->active_cell.col;
5641       if (sheet->state == GTK_SHEET_ROW_SELECTED)
5642         col = MIN_VISIBLE_COLUMN (sheet)- 1;
5643       if (sheet->state == GTK_SHEET_COLUMN_SELECTED)
5644         row = MIN_VISIBLE_ROW (sheet);
5645       if (col > 0)
5646         {
5647           col = col - scroll;
5648           while (! xxx_column_is_visible (sheet, col) && col > 0) col--;
5649           col = MAX (0, col);
5650         }
5651       gtk_sheet_click_cell (sheet, row, col, &veto);
5652       extend_selection = FALSE;
5653       break;
5654     case GDK_Tab:
5655       row = sheet->active_cell.row;
5656       col = sheet->active_cell.col;
5657       if (sheet->state == GTK_SHEET_ROW_SELECTED)
5658         col = MIN_VISIBLE_COLUMN (sheet)- 1;
5659       if (sheet->state == GTK_SHEET_COLUMN_SELECTED)
5660         row = MIN_VISIBLE_ROW (sheet);
5661       if (col < xxx_column_count (sheet) - 1)
5662         {
5663           col = col + scroll;
5664           while (! xxx_column_is_visible (sheet, col) &&
5665                  col < xxx_column_count (sheet) - 1)
5666             col++;
5667         }
5668       gtk_sheet_click_cell (sheet, row, col, &veto);
5669       extend_selection = FALSE;
5670       break;
5671     case GDK_Page_Up:
5672       scroll = MAX_VISIBLE_ROW (sheet)- MIN_VISIBLE_ROW (sheet)+1;
5673     case GDK_Up:
5674       if (extend_selection)
5675         {
5676           if (state == GTK_STATE_NORMAL)
5677             {
5678               row = sheet->active_cell.row;
5679               col = sheet->active_cell.col;
5680               gtk_sheet_click_cell (sheet, row, col, &veto);
5681               if (!veto) break;
5682             }
5683           if (sheet->selection_cell.row > 0)
5684             {
5685               row = sheet->selection_cell.row - scroll;
5686               while (!yyy_row_is_visible (sheet, row) && row > 0) row--;
5687               row = MAX (0, row);
5688               gtk_sheet_extend_selection (sheet, row, sheet->selection_cell.col);
5689             }
5690           return TRUE;
5691         }
5692       col = sheet->active_cell.col;
5693       row = sheet->active_cell.row;
5694       if (state == GTK_SHEET_COLUMN_SELECTED)
5695         row = MIN_VISIBLE_ROW (sheet);
5696       if (state == GTK_SHEET_ROW_SELECTED)
5697         col = MIN_VISIBLE_COLUMN (sheet);
5698       row = row - scroll;
5699       while (!yyy_row_is_visible (sheet, row) && row > 0) row--;
5700       row = MAX (0, row);
5701       gtk_sheet_click_cell (sheet, row, col, &veto);
5702       extend_selection = FALSE;
5703       break;
5704     case GDK_Page_Down:
5705       scroll = MAX_VISIBLE_ROW (sheet)- MIN_VISIBLE_ROW (sheet)+1;
5706     case GDK_Down:
5707       if (extend_selection)
5708         {
5709           if (state == GTK_STATE_NORMAL)
5710             {
5711               row = sheet->active_cell.row;
5712               col = sheet->active_cell.col;
5713               gtk_sheet_click_cell (sheet, row, col, &veto);
5714               if (!veto) break;
5715             }
5716           if (sheet->selection_cell.row < yyy_row_count (sheet)- 1)
5717             {
5718               row = sheet->selection_cell.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               gtk_sheet_extend_selection (sheet, row, sheet->selection_cell.col);
5722             }
5723           return TRUE;
5724         }
5725       col = sheet->active_cell.col;
5726       row = sheet->active_cell.row;
5727       if (sheet->active_cell.row < yyy_row_count (sheet)- 1)
5728         {
5729           if (state == GTK_SHEET_COLUMN_SELECTED)
5730             row = MIN_VISIBLE_ROW (sheet)- 1;
5731           if (state == GTK_SHEET_ROW_SELECTED)
5732             col = MIN_VISIBLE_COLUMN (sheet);
5733           row = row + scroll;
5734           while (!yyy_row_is_visible (sheet, row) && row < yyy_row_count (sheet)- 1) row++;
5735           row = MIN (yyy_row_count (sheet)- 1, row);
5736         }
5737       gtk_sheet_click_cell (sheet, row, col, &veto);
5738       extend_selection = FALSE;
5739       break;
5740     case GDK_Right:
5741       if (extend_selection)
5742         {
5743           if (state == GTK_STATE_NORMAL)
5744             {
5745               row = sheet->active_cell.row;
5746               col = sheet->active_cell.col;
5747               gtk_sheet_click_cell (sheet, row, col, &veto);
5748               if (!veto) break;
5749             }
5750           if (sheet->selection_cell.col < xxx_column_count (sheet) - 1)
5751             {
5752               col = sheet->selection_cell.col + 1;
5753               while (! xxx_column_is_visible (sheet, col) && col < xxx_column_count (sheet) - 1)
5754                 col++;
5755               gtk_sheet_extend_selection (sheet, sheet->selection_cell.row, col);
5756             }
5757           return TRUE;
5758         }
5759       col = sheet->active_cell.col;
5760       row = sheet->active_cell.row;
5761       if (sheet->active_cell.col < xxx_column_count (sheet) - 1)
5762         {
5763           col ++;
5764           if (state == GTK_SHEET_ROW_SELECTED)
5765             col = MIN_VISIBLE_COLUMN (sheet)- 1;
5766           if (state == GTK_SHEET_COLUMN_SELECTED)
5767             row = MIN_VISIBLE_ROW (sheet);
5768           while (! xxx_column_is_visible (sheet, col) && col < xxx_column_count (sheet) - 1) col++;
5769           if (strlen (gtk_entry_get_text (GTK_ENTRY (gtk_sheet_get_entry (sheet)))) == 0
5770               || force_move)
5771             {
5772               gtk_sheet_click_cell (sheet, row, col, &veto);
5773             }
5774           else
5775             return FALSE;
5776         }
5777       extend_selection = FALSE;
5778       break;
5779     case GDK_Left:
5780       if (extend_selection)
5781         {
5782           if (state == GTK_STATE_NORMAL)
5783             {
5784               row = sheet->active_cell.row;
5785               col = sheet->active_cell.col;
5786               gtk_sheet_click_cell (sheet, row, col, &veto);
5787               if (!veto) break;
5788             }
5789           if (sheet->selection_cell.col > 0)
5790             {
5791               col = sheet->selection_cell.col - 1;
5792               while (! xxx_column_is_visible (sheet, col) && col > 0) col--;
5793               gtk_sheet_extend_selection (sheet, sheet->selection_cell.row, col);
5794             }
5795           return TRUE;
5796         }
5797       col = sheet->active_cell.col - 1;
5798       row = sheet->active_cell.row;
5799       if (state == GTK_SHEET_ROW_SELECTED)
5800         col = MIN_VISIBLE_COLUMN (sheet)- 1;
5801       if (state == GTK_SHEET_COLUMN_SELECTED)
5802         row = MIN_VISIBLE_ROW (sheet);
5803       while (! xxx_column_is_visible (sheet, col) && col > 0) col--;
5804       col = MAX (0, col);
5805
5806       if (strlen (gtk_entry_get_text (GTK_ENTRY (gtk_sheet_get_entry (sheet)))) == 0
5807           || force_move)
5808         {
5809           gtk_sheet_click_cell (sheet, row, col, &veto);
5810         }
5811       else
5812         return FALSE;
5813       extend_selection = FALSE;
5814       break;
5815     case GDK_Home:
5816       row = 0;
5817       while (!yyy_row_is_visible (sheet, row) && row < yyy_row_count (sheet) - 1) row++;
5818       gtk_sheet_click_cell (sheet, row, sheet->active_cell.col, &veto);
5819       extend_selection = FALSE;
5820       break;
5821     case GDK_End:
5822       row = yyy_row_count (sheet) - 1;
5823       while (!yyy_row_is_visible (sheet, row) && row > 0) row--;
5824       gtk_sheet_click_cell (sheet, row, sheet->active_cell.col, &veto);
5825       extend_selection = FALSE;
5826       break;
5827     default:
5828       if (in_selection)
5829         {
5830           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
5831           if (extend_selection) return TRUE;
5832         }
5833       if (state == GTK_SHEET_ROW_SELECTED)
5834         sheet->active_cell.col = MIN_VISIBLE_COLUMN (sheet);
5835       if (state == GTK_SHEET_COLUMN_SELECTED)
5836         sheet->active_cell.row = MIN_VISIBLE_ROW (sheet);
5837       return FALSE;
5838     }
5839
5840   if (extend_selection) return TRUE;
5841
5842   gtk_sheet_activate_cell (sheet, sheet->active_cell.row,
5843                            sheet->active_cell.col);
5844
5845   return TRUE;
5846 }
5847
5848 static void
5849 gtk_sheet_size_request (GtkWidget * widget,
5850                         GtkRequisition * requisition)
5851 {
5852   GtkSheet *sheet;
5853   GList *children;
5854   GtkSheetChild *child;
5855   GtkRequisition child_requisition;
5856
5857   g_return_if_fail (widget != NULL);
5858   g_return_if_fail (GTK_IS_SHEET (widget));
5859   g_return_if_fail (requisition != NULL);
5860
5861   sheet = GTK_SHEET (widget);
5862
5863   requisition->width = 3*DEFAULT_COLUMN_WIDTH;
5864   requisition->height = 3*DEFAULT_ROW_HEIGHT (widget);
5865
5866   /* compute the size of the column title area */
5867   if (sheet->column_titles_visible)
5868     requisition->height += sheet->column_title_area.height;
5869
5870   /* compute the size of the row title area */
5871   if (sheet->row_titles_visible)
5872     requisition->width += sheet->row_title_area.width;
5873
5874   children = sheet->children;
5875   while (children)
5876     {
5877       child = children->data;
5878       children = children->next;
5879
5880       gtk_widget_size_request (child->widget, &child_requisition);
5881     }
5882 }
5883
5884
5885 static void
5886 gtk_sheet_size_allocate (GtkWidget * widget,
5887                          GtkAllocation * allocation)
5888 {
5889   GtkSheet *sheet;
5890   GtkAllocation sheet_allocation;
5891   gint border_width;
5892
5893   g_return_if_fail (widget != NULL);
5894   g_return_if_fail (GTK_IS_SHEET (widget));
5895   g_return_if_fail (allocation != NULL);
5896
5897   sheet = GTK_SHEET (widget);
5898   widget->allocation = *allocation;
5899   border_width = GTK_CONTAINER (widget)->border_width;
5900
5901   if (GTK_WIDGET_REALIZED (widget))
5902     gdk_window_move_resize (widget->window,
5903                             allocation->x + border_width,
5904                             allocation->y + border_width,
5905                             allocation->width - 2 * border_width,
5906                             allocation->height - 2 * border_width);
5907
5908   /* use internal allocation structure for all the math
5909    * because it's easier than always subtracting the container
5910    * border width */
5911   sheet->internal_allocation.x = 0;
5912   sheet->internal_allocation.y = 0;
5913   sheet->internal_allocation.width = allocation->width - 2 * border_width;
5914   sheet->internal_allocation.height = allocation->height - 2 * border_width;
5915
5916   sheet_allocation.x = 0;
5917   sheet_allocation.y = 0;
5918   sheet_allocation.width = allocation->width - 2 * border_width;
5919   sheet_allocation.height = allocation->height - 2 * border_width;
5920
5921   sheet->sheet_window_width = sheet_allocation.width;
5922   sheet->sheet_window_height = sheet_allocation.height;
5923
5924   if (GTK_WIDGET_REALIZED (widget))
5925     gdk_window_move_resize (sheet->sheet_window,
5926                             sheet_allocation.x,
5927                             sheet_allocation.y,
5928                             sheet_allocation.width,
5929                             sheet_allocation.height);
5930
5931   /* position the window which holds the column title buttons */
5932   sheet->column_title_area.x = 0;
5933   sheet->column_title_area.y = 0;
5934   if (sheet->row_titles_visible)
5935     sheet->column_title_area.x = sheet->row_title_area.width;
5936   sheet->column_title_area.width = sheet_allocation.width -
5937     sheet->column_title_area.x;
5938   if (GTK_WIDGET_REALIZED (widget) && sheet->column_titles_visible)
5939     gdk_window_move_resize (sheet->column_title_window,
5940                             sheet->column_title_area.x,
5941                             sheet->column_title_area.y,
5942                             sheet->column_title_area.width,
5943                             sheet->column_title_area.height);
5944
5945   sheet->sheet_window_width = sheet_allocation.width;
5946   sheet->sheet_window_height = sheet_allocation.height;
5947
5948   /* column button allocation */
5949   size_allocate_column_title_buttons (sheet);
5950
5951   /* position the window which holds the row title buttons */
5952   sheet->row_title_area.x = 0;
5953   sheet->row_title_area.y = 0;
5954   if (sheet->column_titles_visible)
5955     sheet->row_title_area.y = sheet->column_title_area.height;
5956   sheet->row_title_area.height = sheet_allocation.height -
5957     sheet->row_title_area.y;
5958
5959   if (GTK_WIDGET_REALIZED (widget) && sheet->row_titles_visible)
5960     gdk_window_move_resize (sheet->row_title_window,
5961                             sheet->row_title_area.x,
5962                             sheet->row_title_area.y,
5963                             sheet->row_title_area.width,
5964                             sheet->row_title_area.height);
5965
5966
5967   /* row button allocation */
5968   size_allocate_row_title_buttons (sheet);
5969   size_allocate_column_title_buttons (sheet);
5970
5971   /* re - scale backing pixmap */
5972   gtk_sheet_make_backing_pixmap (sheet, 0, 0);
5973   gtk_sheet_position_children (sheet);
5974
5975   /* set the scrollbars adjustments */
5976   adjust_scrollbars (sheet);
5977 }
5978
5979 static void
5980 size_allocate_column_title_buttons (GtkSheet * sheet)
5981 {
5982   gint i;
5983   gint x, width;
5984
5985   if (!sheet->column_titles_visible) return;
5986   if (!GTK_WIDGET_REALIZED (sheet))
5987     return;
5988
5989   width = sheet->sheet_window_width;
5990   x = 0;
5991
5992   if (sheet->row_titles_visible)
5993     {
5994       width -= sheet->row_title_area.width;
5995       x = sheet->row_title_area.width;
5996     }
5997
5998   if (sheet->column_title_area.width != width || sheet->column_title_area.x != x)
5999     {
6000       sheet->column_title_area.width = width;
6001       sheet->column_title_area.x = x;
6002       gdk_window_move_resize (sheet->column_title_window,
6003                               sheet->column_title_area.x,
6004                               sheet->column_title_area.y,
6005                               sheet->column_title_area.width,
6006                               sheet->column_title_area.height);
6007     }
6008
6009
6010   if (MAX_VISIBLE_COLUMN (sheet) == xxx_column_count (sheet) - 1)
6011     gdk_window_clear_area (sheet->column_title_window,
6012                            0, 0,
6013                            sheet->column_title_area.width,
6014                            sheet->column_title_area.height);
6015
6016   if (!GTK_WIDGET_DRAWABLE (sheet)) return;
6017
6018   size_allocate_global_button (sheet);
6019
6020   for (i = MIN_VISIBLE_COLUMN (sheet); i <= MAX_VISIBLE_COLUMN (sheet); i++)
6021     gtk_sheet_column_title_button_draw (sheet, i);
6022 }
6023
6024 static void
6025 size_allocate_row_title_buttons (GtkSheet * sheet)
6026 {
6027   gint i;
6028   gint y, height;
6029
6030   if (!sheet->row_titles_visible) return;
6031   if (!GTK_WIDGET_REALIZED (sheet))
6032     return;
6033
6034   height = sheet->sheet_window_height;
6035   y = 0;
6036
6037   if (sheet->column_titles_visible)
6038     {
6039       height -= sheet->column_title_area.height;
6040       y = sheet->column_title_area.height;
6041     }
6042
6043   if (sheet->row_title_area.height != height || sheet->row_title_area.y != y)
6044     {
6045       sheet->row_title_area.y = y;
6046       sheet->row_title_area.height = height;
6047       gdk_window_move_resize (sheet->row_title_window,
6048                               sheet->row_title_area.x,
6049                               sheet->row_title_area.y,
6050                               sheet->row_title_area.width,
6051                               sheet->row_title_area.height);
6052     }
6053   if (MAX_VISIBLE_ROW (sheet) == yyy_row_count (sheet)- 1)
6054     gdk_window_clear_area (sheet->row_title_window,
6055                            0, 0,
6056                            sheet->row_title_area.width,
6057                            sheet->row_title_area.height);
6058
6059   if (!GTK_WIDGET_DRAWABLE (sheet)) return;
6060
6061   size_allocate_global_button (sheet);
6062
6063   for (i = MIN_VISIBLE_ROW (sheet); i <= MAX_VISIBLE_ROW (sheet); i++)
6064     {
6065       if ( i >= yyy_row_count (sheet))
6066         break;
6067       gtk_sheet_row_title_button_draw (sheet, i);
6068     }
6069 }
6070
6071
6072 static void
6073 gtk_sheet_size_allocate_entry (GtkSheet *sheet)
6074 {
6075   GtkAllocation shentry_allocation;
6076   GtkSheetCellAttr attributes = { 0 };
6077   GtkEntry *sheet_entry;
6078   GtkStyle *style = NULL, *previous_style = NULL;
6079   gint row, col;
6080   gint size, max_size, text_size, column_width;
6081   const gchar *text;
6082
6083   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
6084   if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
6085
6086   sheet_entry = GTK_ENTRY (gtk_sheet_get_entry (sheet));
6087
6088   if ( ! gtk_sheet_get_attributes (sheet, sheet->active_cell.row,
6089                                    sheet->active_cell.col,
6090                                    &attributes) )
6091     return ;
6092
6093   if ( GTK_WIDGET_REALIZED (sheet->sheet_entry) )
6094     {
6095       if (!GTK_WIDGET (sheet_entry)->style)
6096         gtk_widget_ensure_style (GTK_WIDGET (sheet_entry));
6097
6098       previous_style = GTK_WIDGET (sheet_entry)->style;
6099
6100       style = gtk_style_copy (previous_style);
6101       style->bg[GTK_STATE_NORMAL] = attributes.background;
6102       style->fg[GTK_STATE_NORMAL] = attributes.foreground;
6103       style->text[GTK_STATE_NORMAL] = attributes.foreground;
6104       style->bg[GTK_STATE_ACTIVE] = attributes.background;
6105       style->fg[GTK_STATE_ACTIVE] = attributes.foreground;
6106       style->text[GTK_STATE_ACTIVE] = attributes.foreground;
6107
6108       pango_font_description_free (style->font_desc);
6109       g_assert (attributes.font_desc);
6110       style->font_desc = pango_font_description_copy (attributes.font_desc);
6111
6112       GTK_WIDGET (sheet_entry)->style = style;
6113       gtk_widget_size_request (sheet->sheet_entry, NULL);
6114       GTK_WIDGET (sheet_entry)->style = previous_style;
6115
6116       if (style != previous_style)
6117         {
6118           if (!GTK_IS_ITEM_ENTRY (sheet->sheet_entry))
6119             {
6120               style->bg[GTK_STATE_NORMAL] = previous_style->bg[GTK_STATE_NORMAL];
6121               style->fg[GTK_STATE_NORMAL] = previous_style->fg[GTK_STATE_NORMAL];
6122               style->bg[GTK_STATE_ACTIVE] = previous_style->bg[GTK_STATE_ACTIVE];
6123               style->fg[GTK_STATE_ACTIVE] = previous_style->fg[GTK_STATE_ACTIVE];
6124             }
6125           gtk_widget_set_style (GTK_WIDGET (sheet_entry), style);
6126           g_object_unref (style);
6127         }
6128     }
6129
6130   if (GTK_IS_ITEM_ENTRY (sheet_entry))
6131     max_size = GTK_ITEM_ENTRY (sheet_entry)->text_max_size;
6132   else
6133     max_size = 0;
6134
6135   text_size = 0;
6136   text = gtk_entry_get_text (GTK_ENTRY (sheet_entry));
6137   if (text && strlen (text) > 0)
6138     text_size = STRING_WIDTH (GTK_WIDGET (sheet), attributes.font_desc, text);
6139
6140   column_width = xxx_column_width (sheet, sheet->active_cell.col);
6141
6142   size = MIN (text_size, max_size);
6143   size = MAX (size, column_width - 2 * CELLOFFSET);
6144
6145   row = sheet->active_cell.row;
6146   col = sheet->active_cell.col;
6147
6148   shentry_allocation.x = COLUMN_LEFT_XPIXEL (sheet, sheet->active_cell.col);
6149   shentry_allocation.y = ROW_TOP_YPIXEL (sheet, sheet->active_cell.row);
6150   shentry_allocation.width = column_width;
6151   shentry_allocation.height = yyy_row_height (sheet, sheet->active_cell.row);
6152
6153   if (GTK_IS_ITEM_ENTRY (sheet->sheet_entry))
6154     {
6155       shentry_allocation.height -= 2 * CELLOFFSET;
6156       shentry_allocation.y += CELLOFFSET;
6157       shentry_allocation.width = size;
6158
6159       switch (GTK_ITEM_ENTRY (sheet_entry)->justification)
6160         {
6161         case GTK_JUSTIFY_CENTER:
6162           shentry_allocation.x += column_width / 2 - size / 2;
6163           break;
6164         case GTK_JUSTIFY_RIGHT:
6165           shentry_allocation.x += column_width - size - CELLOFFSET;
6166           break;
6167         case GTK_JUSTIFY_LEFT:
6168         case GTK_JUSTIFY_FILL:
6169           shentry_allocation.x += CELLOFFSET;
6170           break;
6171         }
6172     }
6173
6174   if (!GTK_IS_ITEM_ENTRY (sheet->sheet_entry))
6175     {
6176       shentry_allocation.x += 2;
6177       shentry_allocation.y += 2;
6178       shentry_allocation.width -= MIN (shentry_allocation.width, 3);
6179       shentry_allocation.height -= MIN (shentry_allocation.height, 3);
6180     }
6181
6182   gtk_widget_size_allocate (sheet->sheet_entry, &shentry_allocation);
6183
6184   if (previous_style == style) g_object_unref (previous_style);
6185 }
6186
6187 static void
6188 gtk_sheet_entry_set_max_size (GtkSheet *sheet)
6189 {
6190   gint i;
6191   gint size = 0;
6192   gint sizel = 0, sizer = 0;
6193   gint row, col;
6194   GtkJustification justification;
6195   gchar *s = NULL;
6196
6197   row = sheet->active_cell.row;
6198   col = sheet->active_cell.col;
6199
6200   if ( ! GTK_IS_ITEM_ENTRY (sheet->sheet_entry) )
6201     return;
6202
6203   justification = GTK_ITEM_ENTRY (sheet->sheet_entry)->justification;
6204
6205   switch (justification)
6206     {
6207     case GTK_JUSTIFY_FILL:
6208     case GTK_JUSTIFY_LEFT:
6209       for (i = col + 1; i <= MAX_VISIBLE_COLUMN (sheet); i++)
6210         {
6211           if ((s = gtk_sheet_cell_get_text (sheet, row, i)))
6212             {
6213               g_free (s);
6214               break;
6215             }
6216           size +=xxx_column_width (sheet, i);
6217         }
6218       size = MIN (size, sheet->sheet_window_width - COLUMN_LEFT_XPIXEL (sheet, col));
6219       break;
6220     case GTK_JUSTIFY_RIGHT:
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           size +=xxx_column_width (sheet, i);
6229         }
6230       break;
6231     case GTK_JUSTIFY_CENTER:
6232       for (i = col + 1; i <= MAX_VISIBLE_COLUMN (sheet); i++)
6233         {
6234           sizer += xxx_column_width (sheet, i);
6235         }
6236       for (i = col - 1; i >= MIN_VISIBLE_COLUMN (sheet); i--)
6237         {
6238           if ((s = gtk_sheet_cell_get_text (sheet, row, i)))
6239             {
6240               g_free (s);
6241               break;
6242             }
6243           sizel +=xxx_column_width (sheet, i);
6244         }
6245       size = 2 * MIN (sizel, sizer);
6246       break;
6247     }
6248
6249   if (size != 0)
6250     size += xxx_column_width (sheet, col);
6251   GTK_ITEM_ENTRY (sheet->sheet_entry)->text_max_size = size;
6252 }
6253
6254
6255 static void
6256 create_sheet_entry (GtkSheet *sheet)
6257 {
6258   GtkWidget *widget;
6259   GtkWidget *parent;
6260   GtkWidget *entry;
6261   gint found_entry = FALSE;
6262
6263   widget = GTK_WIDGET (sheet);
6264
6265   if (sheet->sheet_entry)
6266     {
6267       /* avoids warnings */
6268       gtk_widget_ref (sheet->sheet_entry);
6269       gtk_widget_unparent (sheet->sheet_entry);
6270       gtk_widget_destroy (sheet->sheet_entry);
6271     }
6272
6273   if (sheet->entry_type)
6274     {
6275       if (!g_type_is_a (sheet->entry_type, GTK_TYPE_ENTRY))
6276         {
6277           parent = g_object_new (sheet->entry_type, NULL);
6278
6279           sheet->sheet_entry = parent;
6280
6281           entry = gtk_sheet_get_entry (sheet);
6282           if (GTK_IS_ENTRY (entry))
6283             found_entry = TRUE;
6284         }
6285       else
6286         {
6287           parent = g_object_new (sheet->entry_type, NULL);
6288           entry = parent;
6289           found_entry = TRUE;
6290         }
6291
6292       if (!found_entry)
6293         {
6294           g_warning ("Entry type must be GtkEntry subclass, using default");
6295           entry = gtk_item_entry_new ();
6296           sheet->sheet_entry = entry;
6297         }
6298       else
6299         sheet->sheet_entry = parent;
6300     }
6301   else
6302     {
6303       entry = gtk_item_entry_new ();
6304       sheet->sheet_entry = entry;
6305     }
6306
6307   gtk_widget_size_request (sheet->sheet_entry, NULL);
6308
6309   if (GTK_WIDGET_REALIZED (sheet))
6310     {
6311       gtk_widget_set_parent_window (sheet->sheet_entry, sheet->sheet_window);
6312       gtk_widget_set_parent (sheet->sheet_entry, GTK_WIDGET (sheet));
6313       gtk_widget_realize (sheet->sheet_entry);
6314     }
6315
6316   g_signal_connect_swapped (entry, "key_press_event",
6317                             G_CALLBACK (gtk_sheet_entry_key_press),
6318                             sheet);
6319
6320   gtk_widget_show (sheet->sheet_entry);
6321 }
6322
6323
6324 /* Finds the last child widget that happens to be of type GtkEntry */
6325 static void
6326 find_entry (GtkWidget *w, gpointer user_data)
6327 {
6328   GtkWidget **entry = user_data;
6329   if ( GTK_IS_ENTRY (w))
6330     {
6331       *entry = w;
6332     }
6333 }
6334
6335 GtkWidget *
6336 gtk_sheet_get_entry (GtkSheet *sheet)
6337 {
6338   GtkWidget *parent;
6339   GtkWidget *entry = NULL;
6340   GtkTableChild *table_child;
6341   GtkBoxChild *box_child;
6342   GList *children = NULL;
6343
6344   g_return_val_if_fail (sheet != NULL, NULL);
6345   g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);
6346   g_return_val_if_fail (sheet->sheet_entry != NULL, NULL);
6347
6348   if (GTK_IS_ENTRY (sheet->sheet_entry)) return (sheet->sheet_entry);
6349
6350   parent = GTK_WIDGET (sheet->sheet_entry);
6351
6352   if (GTK_IS_TABLE (parent)) children = GTK_TABLE (parent)->children;
6353   if (GTK_IS_BOX (parent)) children = GTK_BOX (parent)->children;
6354
6355   if (GTK_IS_CONTAINER (parent))
6356     {
6357       gtk_container_forall (GTK_CONTAINER (parent), find_entry, &entry);
6358
6359       if (GTK_IS_ENTRY (entry))
6360         return entry;
6361     }
6362
6363   if (!children) return NULL;
6364
6365   while (children)
6366     {
6367       if (GTK_IS_TABLE (parent))
6368         {
6369           table_child = children->data;
6370           entry = table_child->widget;
6371         }
6372       if (GTK_IS_BOX (parent))
6373         {
6374           box_child = children->data;
6375           entry = box_child->widget;
6376         }
6377
6378       if (GTK_IS_ENTRY (entry))
6379         break;
6380       children = children->next;
6381     }
6382
6383
6384   if (!GTK_IS_ENTRY (entry)) return NULL;
6385
6386   return (entry);
6387
6388 }
6389
6390 GtkWidget *
6391 gtk_sheet_get_entry_widget (GtkSheet *sheet)
6392 {
6393   g_return_val_if_fail (sheet != NULL, NULL);
6394   g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);
6395   g_return_val_if_fail (sheet->sheet_entry != NULL, NULL);
6396
6397   return (sheet->sheet_entry);
6398 }
6399
6400
6401 static void
6402 gtk_sheet_button_draw (GtkSheet *sheet, GdkWindow *window,
6403                        GtkSheetButton *button, gboolean is_sensitive,
6404                        GdkRectangle allocation)
6405 {
6406   GtkShadowType shadow_type;
6407   gint text_width = 0, text_height = 0;
6408   GtkSheetChild *child = NULL;
6409   PangoAlignment align = PANGO_ALIGN_LEFT;
6410
6411   gboolean rtl ;
6412
6413   gint state = 0;
6414   gint len = 0;
6415   gchar *line = 0;
6416
6417   g_return_if_fail (sheet != NULL);
6418   g_return_if_fail (button != NULL);
6419
6420   rtl = gtk_widget_get_direction (GTK_WIDGET (sheet)) == GTK_TEXT_DIR_RTL;
6421
6422   gdk_window_clear_area (window,
6423                          allocation.x, allocation.y,
6424                          allocation.width, allocation.height);
6425
6426   gtk_paint_box (sheet->button->style, window,
6427                  GTK_STATE_NORMAL, GTK_SHADOW_OUT,
6428                  &allocation, GTK_WIDGET (sheet->button),
6429                  "buttondefault",
6430                  allocation.x, allocation.y,
6431                  allocation.width, allocation.height);
6432
6433   state = button->state;
6434   if (!is_sensitive) state = GTK_STATE_INSENSITIVE;
6435
6436   if (state == GTK_STATE_ACTIVE)
6437     shadow_type = GTK_SHADOW_IN;
6438   else
6439     shadow_type = GTK_SHADOW_OUT;
6440
6441   if (state != GTK_STATE_NORMAL && state != GTK_STATE_INSENSITIVE)
6442     gtk_paint_box (sheet->button->style, window,
6443                    button->state, shadow_type,
6444                    &allocation, GTK_WIDGET (sheet->button),
6445                    "button",
6446                    allocation.x, allocation.y,
6447                    allocation.width, allocation.height);
6448
6449   if (button->label_visible)
6450     {
6451
6452       text_height = DEFAULT_ROW_HEIGHT (GTK_WIDGET (sheet))- 2 * CELLOFFSET;
6453
6454       gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
6455                                  &allocation);
6456       gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc, &allocation);
6457
6458       allocation.y += 2 * sheet->button->style->ythickness;
6459
6460
6461       if (button->label && strlen (button->label)>0)
6462         {
6463           gchar *words = 0;
6464           PangoLayout *layout = NULL;
6465           gint real_x = allocation.x, real_y = allocation.y;
6466
6467           words = button->label;
6468           line = g_new (gchar, 1);
6469           line[0]='\0';
6470
6471           while (words && *words != '\0')
6472             {
6473               if (*words != '\n')
6474                 {
6475                   len = strlen (line);
6476                   line = g_realloc (line, len + 2);
6477                   line[len]=*words;
6478                   line[len + 1]='\0';
6479                 }
6480               if (*words == '\n' || * (words + 1) == '\0')
6481                 {
6482                   text_width = STRING_WIDTH (GTK_WIDGET (sheet), GTK_WIDGET (sheet)->style->font_desc, line);
6483
6484                   layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), line);
6485                   switch (button->justification)
6486                     {
6487                     case GTK_JUSTIFY_LEFT:
6488                       real_x = allocation.x + CELLOFFSET;
6489                       align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
6490                       break;
6491                     case GTK_JUSTIFY_RIGHT:
6492                       real_x = allocation.x + allocation.width - text_width - CELLOFFSET;
6493                       align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
6494                       break;
6495                     case GTK_JUSTIFY_CENTER:
6496                     default:
6497                       real_x = allocation.x + (allocation.width - text_width)/2;
6498                       align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
6499                       pango_layout_set_justify (layout, TRUE);
6500                     }
6501                   pango_layout_set_alignment (layout, align);
6502                   gtk_paint_layout (GTK_WIDGET (sheet)->style,
6503                                     window,
6504                                     state,
6505                                     FALSE,
6506                                     &allocation,
6507                                     GTK_WIDGET (sheet),
6508                                     "label",
6509                                     real_x, real_y,
6510                                     layout);
6511                   g_object_unref (layout);
6512
6513                   real_y += text_height + 2;
6514
6515                   g_free (line);
6516                   line = g_new (gchar, 1);
6517                   line[0]='\0';
6518                 }
6519               words++;
6520             }
6521           g_free (line);
6522         }
6523
6524       gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
6525                                  NULL);
6526       gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc, NULL);
6527
6528     }
6529
6530   if ((child = button->child) && (child->widget))
6531     {
6532       child->x = allocation.x;
6533       child->y = allocation.y;
6534
6535       child->x += (allocation.width - child->widget->requisition.width) / 2;
6536       child->y += (allocation.height - child->widget->requisition.height) / 2;
6537       allocation.x = child->x;
6538       allocation.y = child->y;
6539       allocation.width = child->widget->requisition.width;
6540       allocation.height = child->widget->requisition.height;
6541
6542       allocation.x = child->x;
6543       allocation.y = child->y;
6544
6545       gtk_widget_set_state (child->widget, button->state);
6546
6547       if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) &&
6548           GTK_WIDGET_MAPPED (child->widget))
6549         {
6550           gtk_widget_size_allocate (child->widget,
6551                                     &allocation);
6552           gtk_widget_queue_draw (child->widget);
6553         }
6554     }
6555
6556   gtk_sheet_button_free (button);
6557 }
6558
6559
6560 /* COLUMN value of - 1 indicates that the area to the right of the rightmost
6561    button should be redrawn */
6562 static void
6563 gtk_sheet_column_title_button_draw (GtkSheet *sheet, gint column)
6564 {
6565   GdkWindow *window = NULL;
6566   GdkRectangle allocation;
6567
6568   gboolean is_sensitive = FALSE;
6569
6570   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
6571
6572   if (column >= 0 && ! xxx_column_is_visible (sheet, column)) return;
6573   if (column >= 0 && !sheet->column_titles_visible) return;
6574   if (column >= 0 && column < MIN_VISIBLE_COLUMN (sheet)) return;
6575   if (column >= 0 && column > MAX_VISIBLE_COLUMN (sheet)) return;
6576
6577   window = sheet->column_title_window;
6578   allocation.y = 0;
6579   allocation.height = sheet->column_title_area.height;
6580
6581   if ( column == -1 )
6582     {
6583       const gint cols = xxx_column_count (sheet) ;
6584       allocation.x = COLUMN_LEFT_XPIXEL (sheet, cols - 1)
6585         ;
6586       allocation.width = sheet->column_title_area.width
6587         + sheet->column_title_area.x
6588         - allocation.x;
6589
6590       gdk_window_clear_area (window,
6591                              allocation.x, allocation.y,
6592                              allocation.width, allocation.height);
6593     }
6594   else
6595     {
6596       GtkSheetButton *button = xxx_column_button (sheet, column);
6597       allocation.x = COLUMN_LEFT_XPIXEL (sheet, column) + CELL_SPACING;
6598       if (sheet->row_titles_visible)
6599         allocation.x -= sheet->row_title_area.width;
6600
6601       allocation.width = xxx_column_width (sheet, column);
6602
6603       is_sensitive = xxx_column_is_sensitive (sheet, column);
6604       gtk_sheet_button_draw (sheet, window, button,
6605                              is_sensitive, allocation);
6606
6607       /* FIXME: Not freeing this button is correct (sort of),
6608          because in PSPP the model always returns a static copy */
6609
6610       /* gtk_sheet_button_free (button); */
6611
6612     }
6613 }
6614
6615 static void
6616 gtk_sheet_row_title_button_draw (GtkSheet *sheet, gint row)
6617 {
6618   GdkWindow *window = NULL;
6619   GdkRectangle allocation;
6620   GtkSheetButton *button = NULL;
6621   gboolean is_sensitive = FALSE;
6622
6623
6624   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
6625
6626   if (row >= 0 && !yyy_row_is_visible (sheet, row)) return;
6627   if (row >= 0 && !sheet->row_titles_visible) return;
6628   if (row >= 0 && row < MIN_VISIBLE_ROW (sheet)) return;
6629   if (row >= 0 && row > MAX_VISIBLE_ROW (sheet)) return;
6630
6631
6632   window = sheet->row_title_window;
6633   button = yyy_row_button (sheet, row);
6634   allocation.x = 0;
6635   allocation.y = ROW_TOP_YPIXEL (sheet, row) + CELL_SPACING;
6636   if (sheet->column_titles_visible)
6637     allocation.y -= sheet->column_title_area.height;
6638   allocation.width = sheet->row_title_area.width;
6639   allocation.height = yyy_row_height (sheet, row);
6640   is_sensitive = yyy_row_is_sensitive (sheet, row);
6641
6642   gtk_sheet_button_draw (sheet, window, button, is_sensitive, allocation);
6643 }
6644
6645 /* SCROLLBARS
6646  *
6647  * functions:
6648  * adjust_scrollbars
6649  * vadjustment_value_changed
6650  * hadjustment_value_changed */
6651
6652 static void
6653 adjust_scrollbars (GtkSheet * sheet)
6654 {
6655   if (sheet->vadjustment)
6656     {
6657       sheet->vadjustment->page_size = sheet->sheet_window_height;
6658       sheet->vadjustment->page_increment = sheet->sheet_window_height / 2;
6659       sheet->vadjustment->step_increment = DEFAULT_ROW_HEIGHT (GTK_WIDGET (sheet));
6660       sheet->vadjustment->lower = 0;
6661       sheet->vadjustment->upper = SHEET_HEIGHT (sheet) + 80;
6662       g_signal_emit_by_name (sheet->vadjustment, "changed");
6663
6664     }
6665
6666   if (sheet->hadjustment)
6667     {
6668       sheet->hadjustment->page_size = sheet->sheet_window_width;
6669       sheet->hadjustment->page_increment = sheet->sheet_window_width / 2;
6670       sheet->hadjustment->step_increment = DEFAULT_COLUMN_WIDTH;
6671       sheet->hadjustment->lower = 0;
6672       sheet->hadjustment->upper = SHEET_WIDTH (sheet)+ 80;
6673       g_signal_emit_by_name (sheet->hadjustment, "changed");
6674
6675     }
6676 }
6677
6678 static void
6679 vadjustment_value_changed (GtkAdjustment * adjustment,
6680                            gpointer data)
6681 {
6682   GtkSheet *sheet;
6683   gint diff, value, old_value;
6684   gint row, new_row;
6685   gint y = 0;
6686
6687   g_return_if_fail (adjustment != NULL);
6688   g_return_if_fail (data != NULL);
6689   g_return_if_fail (GTK_IS_SHEET (data));
6690
6691   sheet = GTK_SHEET (data);
6692
6693   if (GTK_SHEET_IS_FROZEN (sheet)) return;
6694
6695   row = ROW_FROM_YPIXEL (sheet, sheet->column_title_area.height + CELL_SPACING);
6696   if (!sheet->column_titles_visible)
6697     row = ROW_FROM_YPIXEL (sheet, CELL_SPACING);
6698
6699   old_value = - sheet->voffset;
6700
6701   new_row = g_sheet_row_pixel_to_row (sheet->row_geometry,
6702                                       adjustment->value, sheet);
6703
6704   y = g_sheet_row_start_pixel (sheet->row_geometry, new_row, sheet);
6705
6706   if (adjustment->value > sheet->old_vadjustment && sheet->old_vadjustment > 0. &&
6707       yyy_row_height (sheet, row) > sheet->vadjustment->step_increment)
6708     {
6709       /* This avoids embarrassing twitching */
6710       if (row == new_row && row != yyy_row_count (sheet) - 1 &&
6711           adjustment->value - sheet->old_vadjustment >=
6712           sheet->vadjustment->step_increment &&
6713           new_row + 1 != MIN_VISIBLE_ROW (sheet))
6714         {
6715           new_row +=1;
6716           y = y+yyy_row_height (sheet, row);
6717         }
6718     }
6719
6720   /* Negative old_adjustment enforces the redraw, otherwise avoid
6721      spureous redraw */
6722   if (sheet->old_vadjustment >= 0. && row == new_row)
6723     {
6724       sheet->old_vadjustment = sheet->vadjustment->value;
6725       return;
6726     }
6727
6728   sheet->old_vadjustment = sheet->vadjustment->value;
6729   adjustment->value = y;
6730
6731
6732   if (new_row == 0)
6733     {
6734       sheet->vadjustment->step_increment = yyy_row_height (sheet, 0);
6735     }
6736   else
6737     {
6738       sheet->vadjustment->step_increment =
6739         MIN (yyy_row_height (sheet, new_row), yyy_row_height (sheet, new_row - 1));
6740     }
6741
6742   sheet->vadjustment->value = adjustment->value;
6743
6744   value = adjustment->value;
6745
6746   if (value >= - sheet->voffset)
6747     {
6748       /* scroll down */
6749       diff = value + sheet->voffset;
6750     }
6751   else
6752     {
6753       /* scroll up */
6754       diff = - sheet->voffset - value;
6755     }
6756
6757   sheet->voffset = - value;
6758
6759   if (GTK_WIDGET_REALIZED (sheet->sheet_entry) &&
6760       sheet->state == GTK_SHEET_NORMAL &&
6761       sheet->active_cell.row >= 0 && sheet->active_cell.col >= 0 &&
6762       !gtk_sheet_cell_isvisible (sheet, sheet->active_cell.row,
6763                                  sheet->active_cell.col))
6764     {
6765       const gchar *text;
6766
6767       text = gtk_entry_get_text (GTK_ENTRY (gtk_sheet_get_entry (sheet)));
6768
6769       if (!text || strlen (text) == 0)
6770         gtk_sheet_cell_clear (sheet,
6771                               sheet->active_cell.row,
6772                               sheet->active_cell.col);
6773       gtk_widget_unmap (sheet->sheet_entry);
6774     }
6775
6776   gtk_sheet_position_children (sheet);
6777
6778   gtk_sheet_range_draw (sheet, NULL);
6779   size_allocate_row_title_buttons (sheet);
6780   size_allocate_global_button (sheet);
6781 }
6782
6783 static void
6784 hadjustment_value_changed (GtkAdjustment * adjustment,
6785                            gpointer data)
6786 {
6787   GtkSheet *sheet;
6788   gint i, diff, value, old_value;
6789   gint column, new_column;
6790   gint x = 0;
6791
6792   g_return_if_fail (adjustment != NULL);
6793   g_return_if_fail (data != NULL);
6794   g_return_if_fail (GTK_IS_SHEET (data));
6795
6796   sheet = GTK_SHEET (data);
6797
6798   if (GTK_SHEET_IS_FROZEN (sheet)) return;
6799
6800   column = COLUMN_FROM_XPIXEL (sheet, sheet->row_title_area.width + CELL_SPACING);
6801   if (!sheet->row_titles_visible)
6802     column = COLUMN_FROM_XPIXEL (sheet, CELL_SPACING);
6803
6804   old_value = - sheet->hoffset;
6805
6806   for (i = 0; i < xxx_column_count (sheet); i++)
6807     {
6808       if (xxx_column_is_visible (sheet, i)) x += xxx_column_width (sheet, i);
6809       if (x > adjustment->value) break;
6810     }
6811   x -= xxx_column_width (sheet, i);
6812   new_column = i;
6813
6814   if (adjustment->value > sheet->old_hadjustment && sheet->old_hadjustment > 0 &&
6815       xxx_column_width (sheet, i) > sheet->hadjustment->step_increment)
6816     {
6817       /* This avoids embarrassing twitching */
6818       if (column == new_column && column != xxx_column_count (sheet) - 1 &&
6819           adjustment->value - sheet->old_hadjustment >=
6820           sheet->hadjustment->step_increment &&
6821           new_column + 1 != MIN_VISIBLE_COLUMN (sheet))
6822         {
6823           new_column += 1;
6824           x += xxx_column_width (sheet, column);
6825         }
6826     }
6827
6828   /* Negative old_adjustment enforces the redraw, otherwise avoid spureous redraw */
6829   if (sheet->old_hadjustment >= 0. && new_column == column)
6830     {
6831       sheet->old_hadjustment = sheet->hadjustment->value;
6832       return;
6833     }
6834
6835   sheet->old_hadjustment = sheet->hadjustment->value;
6836   adjustment->value = x;
6837
6838   if (new_column == 0)
6839     {
6840       sheet->hadjustment->step_increment = xxx_column_width (sheet, 0);
6841     }
6842   else
6843     {
6844       sheet->hadjustment->step_increment =
6845         MIN (xxx_column_width (sheet, new_column), xxx_column_width (sheet, new_column - 1));
6846     }
6847
6848
6849   sheet->hadjustment->value = adjustment->value;
6850
6851   value = adjustment->value;
6852
6853   if (value >= - sheet->hoffset)
6854     {
6855       /* scroll right */
6856       diff = value + sheet->hoffset;
6857     }
6858   else
6859     {
6860       /* scroll left */
6861       diff = - sheet->hoffset - value;
6862     }
6863
6864   sheet->hoffset = - value;
6865   if (GTK_WIDGET_REALIZED (sheet->sheet_entry) &&
6866       sheet->state == GTK_SHEET_NORMAL &&
6867       sheet->active_cell.row >= 0 && sheet->active_cell.col >= 0 &&
6868       !gtk_sheet_cell_isvisible (sheet, sheet->active_cell.row,
6869                                  sheet->active_cell.col))
6870     {
6871       const gchar *text;
6872
6873       text = gtk_entry_get_text (GTK_ENTRY (gtk_sheet_get_entry (sheet)));
6874       if (!text || strlen (text) == 0)
6875         gtk_sheet_cell_clear (sheet,
6876                               sheet->active_cell.row,
6877                               sheet->active_cell.col);
6878
6879       gtk_widget_unmap (sheet->sheet_entry);
6880     }
6881
6882   gtk_sheet_position_children (sheet);
6883
6884   gtk_sheet_range_draw (sheet, NULL);
6885   size_allocate_column_title_buttons (sheet);
6886 }
6887
6888
6889 /* COLUMN RESIZING */
6890 static void
6891 draw_xor_vline (GtkSheet * sheet)
6892 {
6893   GtkWidget *widget;
6894
6895   g_return_if_fail (sheet != NULL);
6896
6897   widget = GTK_WIDGET (sheet);
6898
6899   gdk_draw_line (widget->window, sheet->xor_gc,
6900                  sheet->x_drag,
6901                  sheet->column_title_area.height,
6902                  sheet->x_drag,
6903                  sheet->sheet_window_height + 1);
6904 }
6905
6906 /* ROW RESIZING */
6907 static void
6908 draw_xor_hline (GtkSheet * sheet)
6909 {
6910   GtkWidget *widget;
6911
6912   g_return_if_fail (sheet != NULL);
6913
6914   widget = GTK_WIDGET (sheet);
6915
6916   gdk_draw_line (widget->window, sheet->xor_gc,
6917                  sheet->row_title_area.width,
6918                  sheet->y_drag,
6919
6920                  sheet->sheet_window_width + 1,
6921                  sheet->y_drag);
6922 }
6923
6924 /* SELECTED RANGE */
6925 static void
6926 draw_xor_rectangle (GtkSheet *sheet, GtkSheetRange range)
6927 {
6928   gint i;
6929   GdkRectangle clip_area, area;
6930   GdkGCValues values;
6931
6932   area.x = COLUMN_LEFT_XPIXEL (sheet, range.col0);
6933   area.y = ROW_TOP_YPIXEL (sheet, range.row0);
6934   area.width = COLUMN_LEFT_XPIXEL (sheet, range.coli)- area.x+
6935     xxx_column_width (sheet, range.coli);
6936   area.height = ROW_TOP_YPIXEL (sheet, range.rowi)- area.y+
6937     yyy_row_height (sheet, range.rowi);
6938
6939   clip_area.x = sheet->row_title_area.width;
6940   clip_area.y = sheet->column_title_area.height;
6941   clip_area.width = sheet->sheet_window_width;
6942   clip_area.height = sheet->sheet_window_height;
6943
6944   if (!sheet->row_titles_visible) clip_area.x = 0;
6945   if (!sheet->column_titles_visible) clip_area.y = 0;
6946
6947   if (area.x < 0)
6948     {
6949       area.width = area.width + area.x;
6950       area.x = 0;
6951     }
6952   if (area.width > clip_area.width) area.width = clip_area.width + 10;
6953   if (area.y < 0)
6954     {
6955       area.height = area.height + area.y;
6956       area.y = 0;
6957     }
6958   if (area.height > clip_area.height) area.height = clip_area.height + 10;
6959
6960   clip_area.x--;
6961   clip_area.y--;
6962   clip_area.width += 3;
6963   clip_area.height += 3;
6964
6965   gdk_gc_get_values (sheet->xor_gc, &values);
6966
6967   gdk_gc_set_clip_rectangle (sheet->xor_gc, &clip_area);
6968
6969   for (i = -1; i <= 1; ++i)
6970     gdk_draw_rectangle (sheet->sheet_window,
6971                         sheet->xor_gc,
6972                         FALSE,
6973                         area.x + i, area.y + i,
6974                         area.width - 2 * i, area.height - 2 * i);
6975
6976
6977   gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
6978
6979   gdk_gc_set_foreground (sheet->xor_gc, &values.foreground);
6980
6981 }
6982
6983
6984 /* this function returns the new width of the column being resized given
6985  * the column and x position of the cursor; the x cursor position is passed
6986  * in as a pointer and automaticaly corrected if it's beyond min / max limits */
6987 static guint
6988 new_column_width (GtkSheet * sheet,
6989                   gint column,
6990                   gint * x)
6991 {
6992   gint cx, width;
6993   guint min_width;
6994
6995   cx = *x;
6996
6997   min_width = sheet->column_requisition;
6998
6999   /* you can't shrink a column to less than its minimum width */
7000   if (cx < COLUMN_LEFT_XPIXEL (sheet, column) + min_width)
7001     {
7002       *x = cx = COLUMN_LEFT_XPIXEL (sheet, column) + min_width;
7003     }
7004
7005   /* calculate new column width making sure it doesn't end up
7006    * less than the minimum width */
7007   width = cx - COLUMN_LEFT_XPIXEL (sheet, column);
7008   if (width < min_width)
7009     width = min_width;
7010
7011   xxx_set_column_width (sheet, column, width);
7012   size_allocate_column_title_buttons (sheet);
7013
7014   return width;
7015 }
7016
7017 /* this function returns the new height of the row being resized given
7018  * the row and y position of the cursor; the y cursor position is passed
7019  * in as a pointer and automaticaly corrected if it's beyond min / max limits */
7020 static guint
7021 new_row_height (GtkSheet * sheet,
7022                 gint row,
7023                 gint * y)
7024 {
7025   gint cy, height;
7026   guint min_height;
7027
7028   cy = *y;
7029   min_height = sheet->row_requisition;
7030
7031   /* you can't shrink a row to less than its minimum height */
7032   if (cy < ROW_TOP_YPIXEL (sheet, row) + min_height)
7033
7034     {
7035       *y = cy = ROW_TOP_YPIXEL (sheet, row) + min_height;
7036     }
7037
7038   /* calculate new row height making sure it doesn't end up
7039    * less than the minimum height */
7040   height = (cy - ROW_TOP_YPIXEL (sheet, row));
7041   if (height < min_height)
7042     height = min_height;
7043
7044   yyy_set_row_height (sheet, row, height);
7045   size_allocate_row_title_buttons (sheet);
7046
7047   return height;
7048 }
7049
7050 static void
7051 gtk_sheet_set_column_width (GtkSheet * sheet,
7052                             gint column,
7053                             guint width)
7054 {
7055   guint min_width;
7056
7057   g_return_if_fail (sheet != NULL);
7058   g_return_if_fail (GTK_IS_SHEET (sheet));
7059
7060   if (column < 0 || column >= xxx_column_count (sheet))
7061     return;
7062
7063   gtk_sheet_column_size_request (sheet, column, &min_width);
7064   if (width < min_width) return;
7065
7066   xxx_set_column_width (sheet, column, width);
7067
7068   if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) && !GTK_SHEET_IS_FROZEN (sheet))
7069     {
7070       size_allocate_column_title_buttons (sheet);
7071       adjust_scrollbars (sheet);
7072       gtk_sheet_size_allocate_entry (sheet);
7073       gtk_sheet_range_draw (sheet, NULL);
7074     }
7075
7076   g_signal_emit (sheet, sheet_signals[CHANGED], 0, -1, column);
7077 }
7078
7079
7080
7081 void
7082 gtk_sheet_set_row_height (GtkSheet * sheet,
7083                           gint row,
7084                           guint height)
7085 {
7086   guint min_height;
7087
7088   g_return_if_fail (sheet != NULL);
7089   g_return_if_fail (GTK_IS_SHEET (sheet));
7090
7091   if (row < 0 || row >= yyy_row_count (sheet))
7092     return;
7093
7094   gtk_sheet_row_size_request (sheet, row, &min_height);
7095   if (height < min_height) return;
7096
7097   yyy_set_row_height (sheet, row, height);
7098
7099   if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) && !GTK_SHEET_IS_FROZEN (sheet))
7100     {
7101       size_allocate_row_title_buttons (sheet);
7102       adjust_scrollbars (sheet);
7103       gtk_sheet_size_allocate_entry (sheet);
7104       gtk_sheet_range_draw (sheet, NULL);
7105     }
7106
7107   g_signal_emit (sheet, sheet_signals[CHANGED], 0, row, - 1);
7108 }
7109 gboolean
7110 gtk_sheet_get_attributes (const GtkSheet *sheet, gint row, gint col,
7111                           GtkSheetCellAttr *attributes)
7112 {
7113   const GdkColor *fg, *bg;
7114   const GtkJustification *j ;
7115   const PangoFontDescription *font_desc ;
7116   const GtkSheetCellBorder *border ;
7117
7118   g_return_val_if_fail (sheet != NULL, FALSE);
7119   g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE);
7120
7121   if (row < 0 || col < 0) return FALSE;
7122
7123   init_attributes (sheet, col, attributes);
7124
7125   if ( !sheet->model)
7126     return FALSE;
7127
7128   attributes->is_editable = g_sheet_model_is_editable (sheet->model, row, col);
7129   attributes->is_visible = g_sheet_model_is_visible (sheet->model, row, col);
7130
7131   fg = g_sheet_model_get_foreground (sheet->model, row, col);
7132   if ( fg )
7133     attributes->foreground = *fg;
7134
7135   bg = g_sheet_model_get_background (sheet->model, row, col);
7136   if ( bg )
7137     attributes->background = *bg;
7138
7139   j = g_sheet_model_get_justification (sheet->model, row, col);
7140   if (j) attributes->justification = *j;
7141
7142   font_desc = g_sheet_model_get_font_desc (sheet->model, row, col);
7143   if ( font_desc ) attributes->font_desc = font_desc;
7144
7145   border = g_sheet_model_get_cell_border (sheet->model, row, col);
7146
7147   if ( border ) attributes->border = *border;
7148
7149   return TRUE;
7150 }
7151
7152 static void
7153 init_attributes (const GtkSheet *sheet, gint col, GtkSheetCellAttr *attributes)
7154 {
7155   /* DEFAULT VALUES */
7156   attributes->foreground = GTK_WIDGET (sheet)->style->black;
7157   attributes->background = sheet->bg_color;
7158   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
7159     {
7160       GdkColormap *colormap;
7161       colormap = gdk_colormap_get_system ();
7162       gdk_color_black (colormap, &attributes->foreground);
7163       attributes->background = sheet->bg_color;
7164     }
7165   attributes->justification = xxx_column_justification (sheet, col);
7166   attributes->border.width = 0;
7167   attributes->border.line_style = GDK_LINE_SOLID;
7168   attributes->border.cap_style = GDK_CAP_NOT_LAST;
7169   attributes->border.join_style = GDK_JOIN_MITER;
7170   attributes->border.mask = 0;
7171   attributes->border.color = GTK_WIDGET (sheet)->style->black;
7172   attributes->is_editable = TRUE;
7173   attributes->is_visible = TRUE;
7174   attributes->font_desc = GTK_WIDGET (sheet)->style->font_desc;
7175 }
7176
7177
7178 /********************************************************************
7179  * Container Functions:
7180  * gtk_sheet_add
7181  * gtk_sheet_put
7182  * gtk_sheet_attach
7183  * gtk_sheet_remove
7184  * gtk_sheet_move_child
7185  * gtk_sheet_position_child
7186  * gtk_sheet_position_children
7187  * gtk_sheet_realize_child
7188  * gtk_sheet_get_child_at
7189  ********************************************************************/
7190
7191 GtkSheetChild *
7192 gtk_sheet_put (GtkSheet *sheet, GtkWidget *child, gint x, gint y)
7193 {
7194   GtkRequisition child_requisition;
7195   GtkSheetChild *child_info;
7196
7197   g_return_val_if_fail (sheet != NULL, NULL);
7198   g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);
7199   g_return_val_if_fail (child != NULL, NULL);
7200   g_return_val_if_fail (child->parent == NULL, NULL);
7201
7202   child_info = g_new (GtkSheetChild, 1);
7203   child_info->widget = child;
7204   child_info->x = x;
7205   child_info->y = y;
7206   child_info->attached_to_cell = FALSE;
7207   child_info->floating = TRUE;
7208   child_info->xpadding = child_info->ypadding = 0;
7209   child_info->xexpand = child_info->yexpand = FALSE;
7210   child_info->xshrink = child_info->yshrink = FALSE;
7211   child_info->xfill = child_info->yfill = FALSE;
7212
7213   sheet->children = g_list_append (sheet->children, child_info);
7214
7215   gtk_widget_set_parent (child, GTK_WIDGET (sheet));
7216
7217   gtk_widget_size_request (child, &child_requisition);
7218
7219   if (GTK_WIDGET_VISIBLE (GTK_WIDGET (sheet)))
7220     {
7221       if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) &&
7222           (!GTK_WIDGET_REALIZED (child) || GTK_WIDGET_NO_WINDOW (child)))
7223         gtk_sheet_realize_child (sheet, child_info);
7224
7225       if (GTK_WIDGET_MAPPED (GTK_WIDGET (sheet)) &&
7226           !GTK_WIDGET_MAPPED (child))
7227         gtk_widget_map (child);
7228     }
7229
7230   gtk_sheet_position_child (sheet, child_info);
7231
7232   /* This will avoid drawing on the titles */
7233
7234   if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
7235     {
7236       if (sheet->row_titles_visible)
7237         gdk_window_show (sheet->row_title_window);
7238       if (sheet->column_titles_visible)
7239         gdk_window_show (sheet->column_title_window);
7240     }
7241
7242   return (child_info);
7243 }
7244
7245 void
7246 gtk_sheet_attach_floating (GtkSheet *sheet,
7247                            GtkWidget *widget,
7248                            gint row, gint col)
7249 {
7250   GdkRectangle area;
7251   GtkSheetChild *child;
7252
7253   if (row < 0 || col < 0)
7254     {
7255       gtk_sheet_button_attach (sheet, widget, row, col);
7256       return;
7257     }
7258
7259   gtk_sheet_get_cell_area (sheet, row, col, &area);
7260   child = gtk_sheet_put (sheet, widget, area.x, area.y);
7261   child->attached_to_cell = TRUE;
7262   child->row = row;
7263   child->col = col;
7264 }
7265
7266 void
7267 gtk_sheet_attach_default (GtkSheet *sheet,
7268                           GtkWidget *widget,
7269                           gint row, gint col)
7270 {
7271   if (row < 0 || col < 0)
7272     {
7273       gtk_sheet_button_attach (sheet, widget, row, col);
7274       return;
7275     }
7276
7277   gtk_sheet_attach (sheet, widget, row, col,
7278                     GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
7279 }
7280
7281 void
7282 gtk_sheet_attach (GtkSheet *sheet,
7283                   GtkWidget *widget,
7284                   gint row, gint col,
7285                   gint xoptions,
7286                   gint yoptions,
7287                   gint xpadding,
7288                   gint ypadding)
7289 {
7290   GdkRectangle area;
7291   GtkSheetChild *child = NULL;
7292
7293   if (row < 0 || col < 0)
7294     {
7295       gtk_sheet_button_attach (sheet, widget, row, col);
7296       return;
7297     }
7298
7299   child = g_new0 (GtkSheetChild, 1);
7300   child->attached_to_cell = TRUE;
7301   child->floating = FALSE;
7302   child->widget = widget;
7303   child->row = row;
7304   child->col = col;
7305   child->xpadding = xpadding;
7306   child->ypadding = ypadding;
7307   child->xexpand = (xoptions & GTK_EXPAND) != 0;
7308   child->yexpand = (yoptions & GTK_EXPAND) != 0;
7309   child->xshrink = (xoptions & GTK_SHRINK) != 0;
7310   child->yshrink = (yoptions & GTK_SHRINK) != 0;
7311   child->xfill = (xoptions & GTK_FILL) != 0;
7312   child->yfill = (yoptions & GTK_FILL) != 0;
7313
7314   sheet->children = g_list_append (sheet->children, child);
7315
7316   gtk_sheet_get_cell_area (sheet, row, col, &area);
7317
7318   child->x = area.x + child->xpadding;
7319   child->y = area.y + child->ypadding;
7320
7321   if (GTK_WIDGET_VISIBLE (GTK_WIDGET (sheet)))
7322     {
7323       if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) &&
7324           (!GTK_WIDGET_REALIZED (widget) || GTK_WIDGET_NO_WINDOW (widget)))
7325         gtk_sheet_realize_child (sheet, child);
7326
7327       if (GTK_WIDGET_MAPPED (GTK_WIDGET (sheet)) &&
7328           !GTK_WIDGET_MAPPED (widget))
7329         gtk_widget_map (widget);
7330     }
7331
7332   gtk_sheet_position_child (sheet, child);
7333
7334   /* This will avoid drawing on the titles */
7335
7336   if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
7337     {
7338       if (GTK_SHEET_ROW_TITLES_VISIBLE (sheet))
7339         gdk_window_show (sheet->row_title_window);
7340       if (GTK_SHEET_COL_TITLES_VISIBLE (sheet))
7341         gdk_window_show (sheet->column_title_window);
7342     }
7343
7344 }
7345
7346 void
7347 gtk_sheet_button_attach          (GtkSheet *sheet,
7348                                   GtkWidget *widget,
7349                                   gint row, gint col)
7350 {
7351   GtkSheetButton *button = 0;
7352   GtkSheetChild *child;
7353   GtkRequisition button_requisition;
7354
7355   if (row >= 0 && col >= 0) return;
7356   if (row < 0 && col < 0) return;
7357
7358   child = g_new (GtkSheetChild, 1);
7359   child->widget = widget;
7360   child->x = 0;
7361   child->y = 0;
7362   child->attached_to_cell = TRUE;
7363   child->floating = FALSE;
7364   child->row = row;
7365   child->col = col;
7366   child->xpadding = child->ypadding = 0;
7367   child->xshrink = child->yshrink = FALSE;
7368   child->xfill = child->yfill = FALSE;
7369
7370
7371   sheet->children = g_list_append (sheet->children, child);
7372
7373   gtk_sheet_button_size_request (sheet, button, &button_requisition);
7374
7375
7376   if (GTK_WIDGET_VISIBLE (GTK_WIDGET (sheet)))
7377     {
7378       if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) &&
7379           (!GTK_WIDGET_REALIZED (widget) || GTK_WIDGET_NO_WINDOW (widget)))
7380         gtk_sheet_realize_child (sheet, child);
7381
7382       if (GTK_WIDGET_MAPPED (GTK_WIDGET (sheet)) &&
7383           !GTK_WIDGET_MAPPED (widget))
7384         gtk_widget_map (widget);
7385     }
7386
7387   if (row == -1) size_allocate_column_title_buttons (sheet);
7388   if (col == -1) size_allocate_row_title_buttons (sheet);
7389
7390 }
7391
7392 static void
7393 label_size_request (GtkSheet *sheet, gchar *label, GtkRequisition *req)
7394 {
7395   gchar *words;
7396   gchar word[1000];
7397   gint n = 0;
7398   gint row_height = DEFAULT_ROW_HEIGHT (GTK_WIDGET (sheet)) - 2 * CELLOFFSET + 2;
7399
7400   req->height = 0;
7401   req->width = 0;
7402   words = label;
7403
7404   while (words && *words != '\0')
7405     {
7406       if (*words == '\n' || * (words + 1) == '\0')
7407         {
7408           req->height += row_height;
7409
7410           word[n] = '\0';
7411           req->width = MAX (req->width, STRING_WIDTH (GTK_WIDGET (sheet), GTK_WIDGET (sheet)->style->font_desc, word));
7412           n = 0;
7413         }
7414       else
7415         {
7416           word[n++] = *words;
7417         }
7418       words++;
7419     }
7420
7421   if (n > 0) req->height -= 2;
7422 }
7423
7424 static void
7425 gtk_sheet_button_size_request    (GtkSheet *sheet,
7426                                   const GtkSheetButton *button,
7427                                   GtkRequisition *button_requisition)
7428 {
7429   GtkRequisition requisition;
7430   GtkRequisition label_requisition;
7431
7432   if (gtk_sheet_autoresize (sheet) && button->label && strlen (button->label) > 0)
7433     {
7434       label_size_request (sheet, button->label, &label_requisition);
7435       label_requisition.width += 2 * CELLOFFSET;
7436       label_requisition.height += 2 * CELLOFFSET;
7437     }
7438   else
7439     {
7440       label_requisition.height = DEFAULT_ROW_HEIGHT (GTK_WIDGET (sheet));
7441       label_requisition.width = COLUMN_MIN_WIDTH;
7442     }
7443
7444   if (button->child)
7445     {
7446       gtk_widget_size_request (button->child->widget, &requisition);
7447       requisition.width += 2 * button->child->xpadding;
7448       requisition.height += 2 * button->child->ypadding;
7449       requisition.width += 2 * sheet->button->style->xthickness;
7450       requisition.height += 2 * sheet->button->style->ythickness;
7451     }
7452   else
7453     {
7454       requisition.height = DEFAULT_ROW_HEIGHT (GTK_WIDGET (sheet));
7455       requisition.width = COLUMN_MIN_WIDTH;
7456     }
7457
7458   *button_requisition = requisition;
7459   button_requisition->width = MAX (requisition.width, label_requisition.width);
7460   button_requisition->height = MAX (requisition.height, label_requisition.height);
7461
7462 }
7463
7464 static void
7465 gtk_sheet_row_size_request (GtkSheet *sheet,
7466                             gint row,
7467                             guint *requisition)
7468 {
7469   GtkRequisition button_requisition;
7470   GList *children;
7471
7472   gtk_sheet_button_size_request (sheet,
7473                                  yyy_row_button (sheet, row),
7474                                  &button_requisition);
7475
7476   *requisition = button_requisition.height;
7477
7478   children = sheet->children;
7479   while (children)
7480     {
7481       GtkSheetChild *child = (GtkSheetChild *)children->data;
7482       GtkRequisition child_requisition;
7483
7484       if (child->attached_to_cell && child->row == row && child->col != -1 && !child->floating && !child->yshrink)
7485         {
7486           gtk_widget_get_child_requisition (child->widget, &child_requisition);
7487
7488           if (child_requisition.height + 2 * child->ypadding > *requisition)
7489             *requisition = child_requisition.height + 2 * child->ypadding;
7490         }
7491       children = children->next;
7492     }
7493
7494   sheet->row_requisition = * requisition;
7495 }
7496
7497 static void
7498 gtk_sheet_column_size_request (GtkSheet *sheet,
7499                                gint col,
7500                                guint *requisition)
7501 {
7502   GtkRequisition button_requisition;
7503   GList *children;
7504   GtkSheetButton *button = xxx_column_button (sheet, col);
7505
7506   gtk_sheet_button_size_request (sheet,
7507                                  button,
7508                                  &button_requisition);
7509
7510   gtk_sheet_button_free (button);
7511
7512   *requisition = button_requisition.width;
7513
7514   children = sheet->children;
7515   while (children)
7516     {
7517       GtkSheetChild *child = (GtkSheetChild *)children->data;
7518       GtkRequisition child_requisition;
7519
7520       if (child->attached_to_cell && child->col == col && child->row != -1 && !child->floating && !child->xshrink)
7521         {
7522           gtk_widget_get_child_requisition (child->widget, &child_requisition);
7523
7524           if (child_requisition.width + 2 * child->xpadding > *requisition)
7525             *requisition = child_requisition.width + 2 * child->xpadding;
7526         }
7527       children = children->next;
7528     }
7529
7530   sheet->column_requisition = *requisition;
7531 }
7532
7533 void
7534 gtk_sheet_move_child (GtkSheet *sheet, GtkWidget *widget, gint x, gint y)
7535 {
7536   GtkSheetChild *child;
7537   GList *children;
7538
7539   g_return_if_fail (sheet != NULL);
7540   g_return_if_fail (GTK_IS_SHEET (sheet));
7541
7542   children = sheet->children;
7543   while (children)
7544     {
7545       child = children->data;
7546
7547       if (child->widget == widget)
7548         {
7549           child->x = x;
7550           child->y = y;
7551           child->row = ROW_FROM_YPIXEL (sheet, y);
7552           child->col = COLUMN_FROM_XPIXEL (sheet, x);
7553           gtk_sheet_position_child (sheet, child);
7554           return;
7555         }
7556
7557       children = children->next;
7558     }
7559
7560   g_warning ("Widget must be a GtkSheet child");
7561
7562 }
7563
7564 static void
7565 gtk_sheet_position_child (GtkSheet *sheet, GtkSheetChild *child)
7566 {
7567   GtkRequisition child_requisition;
7568   GtkAllocation child_allocation;
7569   gint xoffset = 0;
7570   gint yoffset = 0;
7571   gint x = 0, y = 0;
7572   GdkRectangle area;
7573
7574   gtk_widget_get_child_requisition (child->widget, &child_requisition);
7575
7576   if (sheet->column_titles_visible)
7577     yoffset = sheet->column_title_area.height;
7578
7579   if (sheet->row_titles_visible)
7580     xoffset = sheet->row_title_area.width;
7581
7582   if (child->attached_to_cell)
7583     {
7584       gtk_sheet_get_cell_area (sheet, child->row, child->col, &area);
7585       child->x = area.x + child->xpadding;
7586       child->y = area.y + child->ypadding;
7587
7588       if (!child->floating)
7589         {
7590           if (child_requisition.width + 2 * child->xpadding <= xxx_column_width (sheet, child->col))
7591             {
7592               if (child->xfill)
7593                 {
7594                   child_requisition.width = child_allocation.width = xxx_column_width (sheet, child->col) - 2 * child->xpadding;
7595                 }
7596               else
7597                 {
7598                   if (child->xexpand)
7599                     {
7600                       child->x = area.x + xxx_column_width (sheet, child->col) / 2 -
7601                         child_requisition.width / 2;
7602                     }
7603                   child_allocation.width = child_requisition.width;
7604                 }
7605             }
7606           else
7607             {
7608               if (!child->xshrink)
7609                 {
7610                   gtk_sheet_set_column_width (sheet, child->col, child_requisition.width + 2 * child->xpadding);
7611                 }
7612               child_allocation.width = xxx_column_width (sheet, child->col) - 2 * child->xpadding;
7613             }
7614
7615           if (child_requisition.height +
7616               2 * child->ypadding <= yyy_row_height (sheet, child->row))
7617             {
7618               if (child->yfill)
7619                 {
7620                   child_requisition.height = child_allocation.height =
7621                     yyy_row_height (sheet, child->row) - 2 * child->ypadding;
7622                 }
7623               else
7624                 {
7625                   if (child->yexpand)
7626                     {
7627                       child->y = area.y + yyy_row_height (sheet, child->row) / 2
7628                         - child_requisition.height / 2;
7629                     }
7630                   child_allocation.height = child_requisition.height;
7631                 }
7632             }
7633           else
7634             {
7635               if (!child->yshrink)
7636                 {
7637                   gtk_sheet_set_row_height (sheet, child->row, child_requisition.height + 2 * child->ypadding);
7638                 }
7639               child_allocation.height = yyy_row_height (sheet, child->row) -
7640                 2 * child->ypadding;
7641             }
7642         }
7643       else
7644         {
7645           child_allocation.width = child_requisition.width;
7646           child_allocation.height = child_requisition.height;
7647         }
7648
7649       x = child_allocation.x = child->x + xoffset;
7650       y = child_allocation.y = child->y + yoffset;
7651     }
7652   else
7653     {
7654       x = child_allocation.x = child->x + sheet->hoffset + xoffset;
7655       x = child_allocation.x = child->x + xoffset;
7656       y = child_allocation.y = child->y + sheet->voffset + yoffset;
7657       y = child_allocation.y = child->y + yoffset;
7658       child_allocation.width = child_requisition.width;
7659       child_allocation.height = child_requisition.height;
7660     }
7661
7662   gtk_widget_size_allocate (child->widget, &child_allocation);
7663   gtk_widget_queue_draw (child->widget);
7664 }
7665
7666 static void
7667 gtk_sheet_forall (GtkContainer *container,
7668                   gboolean include_internals,
7669                   GtkCallback callback,
7670                   gpointer callback_data)
7671 {
7672   GtkSheet *sheet;
7673   GtkSheetChild *child;
7674   GList *children;
7675
7676   g_return_if_fail (GTK_IS_SHEET (container));
7677   g_return_if_fail (callback != NULL);
7678
7679   sheet = GTK_SHEET (container);
7680   children = sheet->children;
7681   while (children)
7682     {
7683       child = children->data;
7684       children = children->next;
7685
7686       (* callback) (child->widget, callback_data);
7687     }
7688   if (sheet->button)
7689     (* callback) (sheet->button, callback_data);
7690   if (sheet->sheet_entry)
7691     (* callback) (sheet->sheet_entry, callback_data);
7692 }
7693
7694
7695 static void
7696 gtk_sheet_position_children (GtkSheet *sheet)
7697 {
7698   GList *children;
7699   GtkSheetChild *child;
7700
7701   children = sheet->children;
7702
7703   while (children)
7704     {
7705       child = (GtkSheetChild *)children->data;
7706
7707       if (child->col != -1 && child->row != -1)
7708         gtk_sheet_position_child (sheet, child);
7709
7710       if (child->row == -1)
7711         {
7712           if (child->col < MIN_VISIBLE_COLUMN (sheet) ||
7713               child->col > MAX_VISIBLE_COLUMN (sheet))
7714             gtk_sheet_child_hide (child);
7715           else
7716             gtk_sheet_child_show (child);
7717         }
7718       if (child->col == -1)
7719         {
7720           if (child->row < MIN_VISIBLE_ROW (sheet) ||
7721               child->row > MAX_VISIBLE_ROW (sheet))
7722             gtk_sheet_child_hide (child);
7723           else
7724             gtk_sheet_child_show (child);
7725         }
7726
7727       children = children->next;
7728     }
7729 }
7730
7731 static void
7732 gtk_sheet_remove (GtkContainer *container, GtkWidget *widget)
7733 {
7734   GtkSheet *sheet;
7735   GList *children;
7736   GtkSheetChild *child = 0;
7737
7738   g_return_if_fail (container != NULL);
7739   g_return_if_fail (GTK_IS_SHEET (container));
7740
7741   sheet = GTK_SHEET (container);
7742
7743   children = sheet->children;
7744
7745   while (children)
7746     {
7747       child = (GtkSheetChild *)children->data;
7748
7749       if (child->widget == widget) break;
7750
7751       children = children->next;
7752     }
7753
7754   if (children)
7755     {
7756       gtk_widget_unparent (widget);
7757       child->widget = NULL;
7758
7759       sheet->children = g_list_remove_link (sheet->children, children);
7760       g_list_free_1 (children);
7761       g_free (child);
7762     }
7763
7764 }
7765
7766 static void
7767 gtk_sheet_realize_child (GtkSheet *sheet, GtkSheetChild *child)
7768 {
7769   GtkWidget *widget;
7770
7771   widget = GTK_WIDGET (sheet);
7772
7773   if (GTK_WIDGET_REALIZED (widget))
7774     {
7775       if (child->row == -1)
7776         gtk_widget_set_parent_window (child->widget, sheet->column_title_window);
7777       else if (child->col == -1)
7778         gtk_widget_set_parent_window (child->widget, sheet->row_title_window);
7779       else
7780         gtk_widget_set_parent_window (child->widget, sheet->sheet_window);
7781     }
7782
7783   gtk_widget_set_parent (child->widget, widget);
7784 }
7785
7786
7787
7788 GtkSheetChild *
7789 gtk_sheet_get_child_at (GtkSheet *sheet, gint row, gint col)
7790 {
7791   GList *children;
7792   GtkSheetChild *child = 0;
7793
7794   g_return_val_if_fail (sheet != NULL, NULL);
7795   g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);
7796
7797   children = sheet->children;
7798
7799   while (children)
7800     {
7801       child = (GtkSheetChild *)children->data;
7802
7803       if (child->attached_to_cell)
7804         if (child->row == row && child->col == col) break;
7805
7806       children = children->next;
7807     }
7808
7809   if (children) return child;
7810
7811   return NULL;
7812 }
7813
7814 static void
7815 gtk_sheet_child_hide (GtkSheetChild *child)
7816 {
7817   g_return_if_fail (child != NULL);
7818   gtk_widget_hide (child->widget);
7819 }
7820
7821 static void
7822 gtk_sheet_child_show (GtkSheetChild *child)
7823 {
7824   g_return_if_fail (child != NULL);
7825
7826   gtk_widget_show (child->widget);
7827 }
7828
7829 GSheetModel *
7830 gtk_sheet_get_model (const GtkSheet *sheet)
7831 {
7832   g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);
7833
7834   return sheet->model;
7835 }
7836
7837
7838 GtkSheetButton *
7839 gtk_sheet_button_new (void)
7840 {
7841   GtkSheetButton *button = g_malloc (sizeof (GtkSheetButton));
7842
7843   button->state = GTK_STATE_NORMAL;
7844   button->label = NULL;
7845   button->label_visible = TRUE;
7846   button->child = NULL;
7847   button->justification = GTK_JUSTIFY_FILL;
7848
7849   return button;
7850 }
7851
7852
7853 inline void
7854 gtk_sheet_button_free (GtkSheetButton *button)
7855 {
7856   if (!button) return ;
7857
7858   g_free (button->label);
7859   g_free (button);
7860 }
7861
7862
7863 static GString *
7864 range_to_text (const GtkSheet *sheet)
7865 {
7866   gchar *celltext = NULL;
7867   gint r, c;
7868   GString *string;
7869
7870   if ( !gtk_sheet_range_isvisible (sheet, sheet->range))
7871     return NULL;
7872
7873   string = g_string_sized_new (80);
7874
7875   for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
7876     {
7877       for (c = sheet->range.col0; c < sheet->range.coli; ++c)
7878         {
7879           celltext = gtk_sheet_cell_get_text (sheet, r, c);
7880           g_string_append (string, celltext);
7881           g_string_append (string, "\t");
7882           g_free (celltext);
7883         }
7884       celltext = gtk_sheet_cell_get_text (sheet, r, c);
7885       g_string_append (string, celltext);
7886       if ( r < sheet->range.rowi)
7887         g_string_append (string, "\n");
7888       g_free (celltext);
7889     }
7890
7891   return string;
7892 }
7893
7894 static GString *
7895 range_to_html (const GtkSheet *sheet)
7896 {
7897   gchar *celltext = NULL;
7898   gint r, c;
7899   GString *string;
7900
7901   if ( !gtk_sheet_range_isvisible (sheet, sheet->range))
7902     return NULL;
7903
7904   string = g_string_sized_new (480);
7905
7906   g_string_append (string, "<html>\n");
7907   g_string_append (string, "<body>\n");
7908   g_string_append (string, "<table>\n");
7909   for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
7910     {
7911       g_string_append (string, "<tr>\n");
7912       for (c = sheet->range.col0; c <= sheet->range.coli; ++c)
7913         {
7914           g_string_append (string, "<td>");
7915           celltext = gtk_sheet_cell_get_text (sheet, r, c);
7916           g_string_append (string, celltext);
7917           g_string_append (string, "</td>\n");
7918           g_free (celltext);
7919         }
7920       g_string_append (string, "</tr>\n");
7921     }
7922   g_string_append (string, "</table>\n");
7923   g_string_append (string, "</body>\n");
7924   g_string_append (string, "</html>\n");
7925
7926   return string;
7927 }
7928
7929 enum {
7930   SELECT_FMT_NULL,
7931   SELECT_FMT_TEXT,
7932   SELECT_FMT_HTML
7933 };
7934
7935 static void
7936 primary_get_cb (GtkClipboard     *clipboard,
7937                 GtkSelectionData *selection_data,
7938                 guint             info,
7939                 gpointer          data)
7940 {
7941   GtkSheet *sheet = GTK_SHEET (data);
7942   GString *string = NULL;
7943
7944   switch (info)
7945     {
7946     case SELECT_FMT_TEXT:
7947       string = range_to_text (sheet);
7948       break;
7949     case SELECT_FMT_HTML:
7950       string = range_to_html (sheet);
7951       break;
7952     default:
7953       g_assert_not_reached ();
7954     }
7955
7956   gtk_selection_data_set (selection_data, selection_data->target,
7957                           8,
7958                           (const guchar *) string->str, string->len);
7959   g_string_free (string, TRUE);
7960 }
7961
7962 static void
7963 primary_clear_cb (GtkClipboard *clipboard,
7964                   gpointer      data)
7965 {
7966   GtkSheet *sheet = GTK_SHEET (data);
7967   gtk_sheet_real_unselect_range (sheet, NULL);
7968 }
7969
7970 static void
7971 gtk_sheet_update_primary_selection (GtkSheet *sheet)
7972 {
7973   static const GtkTargetEntry targets[] = {
7974     { "UTF8_STRING",   0, SELECT_FMT_TEXT },
7975     { "STRING",        0, SELECT_FMT_TEXT },
7976     { "TEXT",          0, SELECT_FMT_TEXT },
7977     { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
7978     { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
7979     { "text/plain",    0, SELECT_FMT_TEXT },
7980     { "text/html",     0, SELECT_FMT_HTML }
7981   };
7982
7983   GtkClipboard *clipboard;
7984
7985   if (!GTK_WIDGET_REALIZED (sheet))
7986     return;
7987
7988   clipboard = gtk_widget_get_clipboard (GTK_WIDGET (sheet),
7989                                         GDK_SELECTION_PRIMARY);
7990
7991   if (gtk_sheet_range_isvisible (sheet, sheet->range))
7992     {
7993       if (!gtk_clipboard_set_with_owner (clipboard, targets,
7994                                          G_N_ELEMENTS (targets),
7995                                          primary_get_cb, primary_clear_cb,
7996                                          G_OBJECT (sheet)))
7997         primary_clear_cb (clipboard, sheet);
7998     }
7999   else
8000     {
8001       if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (sheet))
8002         gtk_clipboard_clear (clipboard);
8003     }
8004 }
8005