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