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