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