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