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