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