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