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