Remove unsafe colormap operations.
[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   colormap = gtk_widget_get_colormap (widget);
2008
2009   attributes.window_type = GDK_WINDOW_CHILD;
2010   attributes.x = widget->allocation.x;
2011   attributes.y = widget->allocation.y;
2012   attributes.width = widget->allocation.width;
2013   attributes.height = widget->allocation.height;
2014   attributes.wclass = GDK_INPUT_OUTPUT;
2015
2016   attributes.visual = gtk_widget_get_visual (widget);
2017   attributes.colormap = colormap;
2018
2019   attributes.event_mask = gtk_widget_get_events (widget);
2020   attributes.event_mask |= (GDK_EXPOSURE_MASK |
2021                             GDK_BUTTON_PRESS_MASK |
2022                             GDK_BUTTON_RELEASE_MASK |
2023                             GDK_KEY_PRESS_MASK |
2024                             GDK_ENTER_NOTIFY_MASK |
2025                             GDK_LEAVE_NOTIFY_MASK |
2026                             GDK_POINTER_MOTION_MASK |
2027                             GDK_POINTER_MOTION_HINT_MASK);
2028   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP |
2029     GDK_WA_CURSOR;
2030
2031   attributes.cursor = gdk_cursor_new (GDK_TOP_LEFT_ARROW);
2032
2033   /* main window */
2034   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
2035
2036   gdk_window_set_user_data (widget->window, sheet);
2037
2038   widget->style = gtk_style_attach (widget->style, widget->window);
2039
2040   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
2041
2042   gdk_color_parse ("white", &sheet->color[BG_COLOR]);
2043   gdk_colormap_alloc_color (colormap, &sheet->color[BG_COLOR], FALSE,
2044                             TRUE);
2045   gdk_color_parse ("gray", &sheet->color[GRID_COLOR]);
2046   gdk_colormap_alloc_color (colormap, &sheet->color[GRID_COLOR], FALSE,
2047                             TRUE);
2048
2049   attributes.x = 0;
2050   attributes.y = 0;
2051   attributes.width = sheet->column_title_area.width;
2052   attributes.height = sheet->column_title_area.height;
2053
2054
2055   /* column - title window */
2056   sheet->column_title_window =
2057     gdk_window_new (widget->window, &attributes, attributes_mask);
2058   gdk_window_set_user_data (sheet->column_title_window, sheet);
2059   gtk_style_set_background (widget->style, sheet->column_title_window,
2060                             GTK_STATE_NORMAL);
2061
2062
2063   attributes.x = 0;
2064   attributes.y = 0;
2065   attributes.width = sheet->row_title_area.width;
2066   attributes.height = sheet->row_title_area.height;
2067
2068   /* row - title window */
2069   sheet->row_title_window = gdk_window_new (widget->window,
2070                                             &attributes, attributes_mask);
2071   gdk_window_set_user_data (sheet->row_title_window, sheet);
2072   gtk_style_set_background (widget->style, sheet->row_title_window,
2073                             GTK_STATE_NORMAL);
2074
2075   /* sheet - window */
2076   attributes.cursor = gdk_cursor_new (GDK_PLUS);
2077
2078   attributes.x = 0;
2079   attributes.y = 0;
2080   attributes.width = sheet->sheet_window_width;
2081     attributes.height = sheet->sheet_window_height;
2082
2083   sheet->sheet_window = gdk_window_new (widget->window,
2084                                         &attributes, attributes_mask);
2085   gdk_window_set_user_data (sheet->sheet_window, sheet);
2086
2087   gdk_cursor_unref (attributes.cursor);
2088
2089   gdk_window_set_background (sheet->sheet_window, &widget->style->white);
2090   gdk_window_show (sheet->sheet_window);
2091
2092   /* backing_pixmap */
2093   gtk_sheet_make_backing_pixmap (sheet);
2094
2095   /* GCs */
2096   sheet->fg_gc = gdk_gc_new (widget->window);
2097   sheet->bg_gc = gdk_gc_new (widget->window);
2098
2099
2100
2101   gdk_gc_get_values (sheet->fg_gc, &auxvalues);
2102
2103   values.foreground = widget->style->white;
2104   values.function = GDK_INVERT;
2105   values.subwindow_mode = GDK_INCLUDE_INFERIORS;
2106   values.line_width = 3;
2107
2108   sheet->xor_gc = gdk_gc_new_with_values (widget->window,
2109                                           &values,
2110                                           GDK_GC_FOREGROUND |
2111                                           GDK_GC_FUNCTION |
2112                                           GDK_GC_SUBWINDOW |
2113                                           GDK_GC_LINE_WIDTH
2114                                           );
2115
2116
2117   gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
2118   gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
2119
2120   gtk_widget_set_parent_window (sheet->button, sheet->sheet_window);
2121   gtk_widget_set_parent (sheet->button, GTK_WIDGET (sheet));
2122
2123
2124   sheet->cursor_drag = gdk_cursor_new (GDK_PLUS);
2125
2126   if (sheet->column_titles_visible)
2127     gdk_window_show (sheet->column_title_window);
2128   if (sheet->row_titles_visible)
2129     gdk_window_show (sheet->row_title_window);
2130
2131   sheet->hover_window = create_hover_window ();
2132
2133   size_allocate_row_title_buttons (sheet);
2134   size_allocate_column_title_buttons (sheet);
2135
2136   gtk_sheet_update_primary_selection (sheet);
2137 }
2138
2139 static void
2140 create_global_button (GtkSheet *sheet)
2141 {
2142   sheet->button = gtk_button_new_with_label (" ");
2143
2144   g_object_ref_sink (sheet->button);
2145
2146   g_signal_connect (sheet->button,
2147                     "pressed",
2148                     G_CALLBACK (global_button_clicked),
2149                     sheet);
2150 }
2151
2152 static void
2153 size_allocate_global_button (GtkSheet *sheet)
2154 {
2155   GtkAllocation allocation;
2156
2157   if (!sheet->column_titles_visible) return;
2158   if (!sheet->row_titles_visible) return;
2159
2160   gtk_widget_size_request (sheet->button, NULL);
2161
2162   allocation.x = 0;
2163   allocation.y = 0;
2164   allocation.width = sheet->row_title_area.width;
2165   allocation.height = sheet->column_title_area.height;
2166
2167   gtk_widget_size_allocate (sheet->button, &allocation);
2168   gtk_widget_show (sheet->button);
2169 }
2170
2171 static void
2172 global_button_clicked (GtkWidget *widget, gpointer data)
2173 {
2174   gboolean veto;
2175
2176   gtk_sheet_click_cell (GTK_SHEET (data), - 1, - 1, &veto);
2177   gtk_widget_grab_focus (GTK_WIDGET (data));
2178 }
2179
2180
2181 static void
2182 gtk_sheet_unrealize (GtkWidget *widget)
2183 {
2184   GtkSheet *sheet;
2185
2186   g_return_if_fail (widget != NULL);
2187   g_return_if_fail (GTK_IS_SHEET (widget));
2188
2189   sheet = GTK_SHEET (widget);
2190
2191   gdk_cursor_unref (sheet->cursor_drag);
2192   sheet->cursor_drag = NULL;
2193
2194   gdk_colormap_free_colors (gtk_widget_get_colormap (widget),
2195                             sheet->color, n_COLORS);
2196
2197   g_object_unref (sheet->xor_gc);
2198   g_object_unref (sheet->fg_gc);
2199   g_object_unref (sheet->bg_gc);
2200
2201   destroy_hover_window (sheet->hover_window);
2202
2203   gdk_window_destroy (sheet->sheet_window);
2204   gdk_window_destroy (sheet->column_title_window);
2205   gdk_window_destroy (sheet->row_title_window);
2206
2207   if (sheet->pixmap)
2208     {
2209       g_object_unref (sheet->pixmap);
2210       sheet->pixmap = NULL;
2211     }
2212
2213   gtk_widget_unparent (sheet->entry_widget);
2214   if (sheet->button != NULL)
2215     gtk_widget_unparent (sheet->button);
2216
2217   if (GTK_WIDGET_CLASS (parent_class)->unrealize)
2218     (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
2219 }
2220
2221 static void
2222 gtk_sheet_map (GtkWidget * widget)
2223 {
2224   GtkSheet *sheet = GTK_SHEET (widget);
2225
2226   g_return_if_fail (widget != NULL);
2227   g_return_if_fail (GTK_IS_SHEET (widget));
2228
2229   if (!GTK_WIDGET_MAPPED (widget))
2230     {
2231       GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
2232
2233       gdk_window_show (widget->window);
2234       gdk_window_show (sheet->sheet_window);
2235
2236       if (sheet->column_titles_visible)
2237         {
2238           size_allocate_column_title_buttons (sheet);
2239           gdk_window_show (sheet->column_title_window);
2240         }
2241       if (sheet->row_titles_visible)
2242         {
2243           size_allocate_row_title_buttons (sheet);
2244           gdk_window_show (sheet->row_title_window);
2245         }
2246
2247       if (!GTK_WIDGET_MAPPED (sheet->entry_widget)
2248           && sheet->active_cell.row >= 0
2249           && sheet->active_cell.col >= 0 )
2250         {
2251           gtk_widget_show (sheet->entry_widget);
2252           gtk_widget_map (sheet->entry_widget);
2253         }
2254
2255       if (GTK_WIDGET_VISIBLE (sheet->button) &&
2256           !GTK_WIDGET_MAPPED (sheet->button))
2257         {
2258           gtk_widget_show (sheet->button);
2259           gtk_widget_map (sheet->button);
2260         }
2261
2262       if (GTK_BIN (sheet->button)->child)
2263         if (GTK_WIDGET_VISIBLE (GTK_BIN (sheet->button)->child) &&
2264             !GTK_WIDGET_MAPPED (GTK_BIN (sheet->button)->child))
2265           gtk_widget_map (GTK_BIN (sheet->button)->child);
2266
2267       gtk_sheet_range_draw (sheet, NULL);
2268       gtk_sheet_activate_cell (sheet,
2269                                sheet->active_cell.row,
2270                                sheet->active_cell.col);
2271     }
2272 }
2273
2274 static void
2275 gtk_sheet_unmap (GtkWidget * widget)
2276 {
2277   GtkSheet *sheet = GTK_SHEET (widget);
2278
2279   if (!GTK_WIDGET_MAPPED (widget))
2280     return;
2281
2282       GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
2283
2284       gdk_window_hide (sheet->sheet_window);
2285       if (sheet->column_titles_visible)
2286         gdk_window_hide (sheet->column_title_window);
2287       if (sheet->row_titles_visible)
2288         gdk_window_hide (sheet->row_title_window);
2289       gdk_window_hide (widget->window);
2290
2291       if (GTK_WIDGET_MAPPED (sheet->entry_widget))
2292         gtk_widget_unmap (sheet->entry_widget);
2293
2294       if (GTK_WIDGET_MAPPED (sheet->button))
2295         gtk_widget_unmap (sheet->button);
2296 }
2297
2298
2299 static void
2300 gtk_sheet_cell_draw_default (GtkSheet *sheet, gint row, gint col)
2301 {
2302   GdkGC *fg_gc, *bg_gc;
2303   GtkSheetCellAttr attributes;
2304   GdkRectangle area;
2305
2306   g_return_if_fail (sheet != NULL);
2307
2308   /* bail now if we arn't drawable yet */
2309   if (!GTK_WIDGET_DRAWABLE (sheet)) return;
2310
2311   if (row < 0 || row >= g_sheet_row_get_row_count (sheet->row_geometry)) return;
2312   if (col < 0 || col >= g_sheet_column_get_column_count (sheet->column_geometry)) return;
2313   if (! g_sheet_column_get_visibility (sheet->column_geometry, col)) return;
2314   if (! g_sheet_row_get_visibility (sheet->row_geometry, row)) return;
2315
2316   gtk_sheet_get_attributes (sheet, row, col, &attributes);
2317
2318   /* select GC for background rectangle */
2319   gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground);
2320   gdk_gc_set_foreground (sheet->bg_gc, &attributes.background);
2321
2322   fg_gc = sheet->fg_gc;
2323   bg_gc = sheet->bg_gc;
2324
2325   area.x = g_sheet_column_start_pixel (sheet->column_geometry, col);
2326   area.x -= sheet->hadjustment->value;
2327
2328   area.y = g_sheet_row_start_pixel (sheet->row_geometry, row);
2329   area.y -= sheet->vadjustment->value;
2330
2331   area.width= g_sheet_column_get_width (sheet->column_geometry, col);
2332   area.height = g_sheet_row_get_height (sheet->row_geometry, row);
2333
2334   gdk_gc_set_line_attributes (sheet->fg_gc, 1, 0, 0, 0);
2335
2336   if (sheet->show_grid)
2337     {
2338       gdk_gc_set_foreground (sheet->bg_gc, &sheet->color[GRID_COLOR]);
2339
2340       gdk_draw_rectangle (sheet->pixmap,
2341                           sheet->bg_gc,
2342                           FALSE,
2343                           area.x, area.y,
2344                           area.width, area.height);
2345     }
2346 }
2347
2348
2349 static void
2350 gtk_sheet_cell_draw_label (GtkSheet *sheet, gint row, gint col)
2351 {
2352   GtkWidget *widget;
2353   GdkRectangle area;
2354   gint i;
2355   gint text_width, text_height, y;
2356   gint xoffset = 0;
2357   gint size, sizel, sizer;
2358   GdkGC *fg_gc, *bg_gc;
2359   GtkSheetCellAttr attributes;
2360   PangoLayout *layout;
2361   PangoRectangle rect;
2362   PangoRectangle logical_rect;
2363   PangoLayoutLine *line;
2364   PangoFontMetrics *metrics;
2365   PangoContext *context = gtk_widget_get_pango_context (GTK_WIDGET (sheet));
2366   gint ascent, descent, y_pos;
2367
2368   gchar *label;
2369
2370   g_return_if_fail (sheet != NULL);
2371
2372   /* bail now if we aren't drawable yet */
2373   if (!GTK_WIDGET_DRAWABLE (sheet))
2374     return;
2375
2376   label = gtk_sheet_cell_get_text (sheet, row, col);
2377   if (!label)
2378     return;
2379
2380   if (row < 0 || row >= g_sheet_row_get_row_count (sheet->row_geometry)) return;
2381   if (col < 0 || col >= g_sheet_column_get_column_count (sheet->column_geometry)) return;
2382   if (! g_sheet_column_get_visibility (sheet->column_geometry, col)) return;
2383   if (!g_sheet_row_get_visibility (sheet->row_geometry, row)) return;
2384
2385   widget = GTK_WIDGET (sheet);
2386
2387   gtk_sheet_get_attributes (sheet, row, col, &attributes);
2388
2389   /* select GC for background rectangle */
2390   gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground);
2391   gdk_gc_set_foreground (sheet->bg_gc, &attributes.background);
2392
2393   fg_gc = sheet->fg_gc;
2394   bg_gc = sheet->bg_gc;
2395
2396   area.x = g_sheet_column_start_pixel (sheet->column_geometry, col);
2397   area.x -= sheet->hadjustment->value;
2398
2399   area.y = g_sheet_row_start_pixel (sheet->row_geometry, row);
2400   area.y -= sheet->vadjustment->value;
2401
2402   area.width = g_sheet_column_get_width (sheet->column_geometry, col);
2403   area.height = g_sheet_row_get_height (sheet->row_geometry, row);
2404
2405
2406   layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), label);
2407   dispose_string (sheet, label);
2408   pango_layout_set_font_description (layout, attributes.font_desc);
2409
2410   pango_layout_get_pixel_extents (layout, NULL, &rect);
2411
2412   line = pango_layout_get_lines (layout)->data;
2413   pango_layout_line_get_extents (line, NULL, &logical_rect);
2414
2415   metrics = pango_context_get_metrics (context,
2416                                        attributes.font_desc,
2417                                        pango_context_get_language (context));
2418
2419   ascent = pango_font_metrics_get_ascent (metrics) / PANGO_SCALE;
2420   descent = pango_font_metrics_get_descent (metrics) / PANGO_SCALE;
2421
2422   pango_font_metrics_unref (metrics);
2423
2424   /* Align primarily for locale's ascent / descent */
2425
2426   logical_rect.height /= PANGO_SCALE;
2427   logical_rect.y /= PANGO_SCALE;
2428   y_pos = area.height - logical_rect.height;
2429
2430   if (logical_rect.height > area.height)
2431     y_pos = (logical_rect.height - area.height - 2 * COLUMN_TITLES_HEIGHT) / 2;
2432   else if (y_pos < 0)
2433     y_pos = 0;
2434   else if (y_pos + logical_rect.height > area.height)
2435     y_pos = area.height - logical_rect.height;
2436
2437   text_width = rect.width;
2438   text_height = rect.height;
2439   y = area.y + y_pos - COLUMN_TITLES_HEIGHT;
2440
2441   switch (attributes.justification)
2442     {
2443     case GTK_JUSTIFY_RIGHT:
2444       size = area.width;
2445       area.x +=area.width;
2446       {
2447         for (i = col - 1; i >= MIN_VISIBLE_COLUMN (sheet); i--)
2448           {
2449             if ( !gtk_sheet_cell_empty (sheet, row, i)) break;
2450             if (size >= text_width + COLUMN_TITLES_HEIGHT) break;
2451             size += g_sheet_column_get_width (sheet->column_geometry, i);
2452             g_sheet_column_set_right_text_column (sheet->column_geometry, i,
2453                                          MAX (col,
2454                                                        g_sheet_column_get_right_text_column (sheet->column_geometry, i)));
2455           }
2456         area.width = size;
2457       }
2458       area.x -= size;
2459       xoffset += area.width - text_width - 2 * COLUMN_TITLES_HEIGHT -
2460         attributes.border.width / 2;
2461       break;
2462     case GTK_JUSTIFY_CENTER:
2463       sizel = area.width / 2;
2464       sizer = area.width / 2;
2465       area.x += area.width / 2;
2466       {
2467         for (i = col + 1; i <= MAX_VISIBLE_COLUMN (sheet); i++)
2468           {
2469             if ( ! gtk_sheet_cell_empty (sheet, row, i)) break;
2470             if (sizer >= text_width / 2) break;
2471             sizer += g_sheet_column_get_width (sheet->column_geometry, i);
2472             g_sheet_column_set_left_text_column (sheet->column_geometry, i,
2473                                         MIN (
2474                                              col,
2475                                                       g_sheet_column_get_left_text_column (sheet->column_geometry, i)));
2476           }
2477         for (i = col - 1; i >= MIN_VISIBLE_COLUMN (sheet); i--)
2478           {
2479             if ( ! gtk_sheet_cell_empty (sheet, row, i)) break;
2480             if (sizel >= text_width / 2) break;
2481             sizel += g_sheet_column_get_width (sheet->column_geometry, i);
2482             g_sheet_column_set_right_text_column (sheet->column_geometry, i,
2483                                          MAX (col,
2484                                                        g_sheet_column_get_right_text_column (sheet->column_geometry, i)));
2485           }
2486         size = MIN (sizel, sizer);
2487       }
2488       area.x -= sizel;
2489       xoffset += sizel - text_width / 2 - COLUMN_TITLES_HEIGHT;
2490       area.width = sizel + sizer;
2491       break;
2492     case GTK_JUSTIFY_LEFT:
2493     default:
2494       size = area.width;
2495       {
2496         for (i = col + 1; i <= MAX_VISIBLE_COLUMN (sheet); i++)
2497           {
2498             if (! gtk_sheet_cell_empty (sheet, row, i)) break;
2499             if (size >= text_width + COLUMN_TITLES_HEIGHT) break;
2500             size += g_sheet_column_get_width (sheet->column_geometry, i);
2501             g_sheet_column_set_left_text_column (sheet->column_geometry, i,
2502                                         MIN (
2503                                              col,
2504                                                       g_sheet_column_get_left_text_column (sheet->column_geometry, i)));
2505
2506           }
2507         area.width = size;
2508       }
2509       xoffset += attributes.border.width / 2;
2510       break;
2511     }
2512
2513   gdk_gc_set_clip_rectangle (fg_gc, &area);
2514
2515
2516   gdk_draw_layout (sheet->pixmap, fg_gc,
2517                    area.x + xoffset + COLUMN_TITLES_HEIGHT,
2518                    area.y,
2519                    layout);
2520
2521   gdk_gc_set_clip_rectangle (fg_gc, NULL);
2522   g_object_unref (layout);
2523
2524 }
2525
2526 static void
2527 gtk_sheet_range_draw (GtkSheet *sheet, const GtkSheetRange *range)
2528 {
2529   gint i, j;
2530   GtkSheetRange drawing_range;
2531
2532   g_return_if_fail (sheet != NULL);
2533   g_return_if_fail (GTK_SHEET (sheet));
2534
2535   if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (sheet))) return;
2536   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2537   if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
2538
2539   if (sheet->sheet_window_width <= 0) return;
2540   if (sheet->sheet_window_height <=0) return;
2541
2542   if (sheet->pixmap == NULL) return ;
2543
2544   if (range == NULL)
2545     {
2546       drawing_range.row0 = MIN_VISIBLE_ROW (sheet);
2547       drawing_range.col0 = MIN_VISIBLE_COLUMN (sheet);
2548       drawing_range.rowi = MIN (MAX_VISIBLE_ROW (sheet),
2549                                 g_sheet_row_get_row_count (sheet->row_geometry) - 1);
2550       drawing_range.coli = MAX_VISIBLE_COLUMN (sheet);
2551
2552       gdk_draw_rectangle (sheet->pixmap,
2553                           GTK_WIDGET (sheet)->style->white_gc,
2554                           TRUE,
2555                           0, 0,
2556                           sheet->sheet_window_width,
2557                           sheet->sheet_window_height);
2558     }
2559   else
2560     {
2561       drawing_range.row0 = MAX (range->row0, MIN_VISIBLE_ROW (sheet));
2562       drawing_range.col0 = MAX (range->col0, MIN_VISIBLE_COLUMN (sheet));
2563       drawing_range.rowi = MIN (range->rowi, MAX_VISIBLE_ROW (sheet));
2564       drawing_range.coli = MIN (range->coli, MAX_VISIBLE_COLUMN (sheet));
2565     }
2566
2567   for (i = drawing_range.row0; i <= drawing_range.rowi; i++)
2568     for (j = drawing_range.col0; j <= drawing_range.coli; j++)
2569       {
2570         gtk_sheet_cell_draw_default (sheet, i, j);
2571         gtk_sheet_cell_draw_label (sheet, i, j);
2572       }
2573
2574   gtk_sheet_draw_backing_pixmap (sheet, drawing_range);
2575
2576   if (sheet->state != GTK_SHEET_NORMAL &&
2577       gtk_sheet_range_isvisible (sheet, sheet->range))
2578     gtk_sheet_range_draw_selection (sheet, drawing_range);
2579
2580   if (sheet->state == GTK_STATE_NORMAL &&
2581       sheet->active_cell.row >= drawing_range.row0 &&
2582       sheet->active_cell.row <= drawing_range.rowi &&
2583       sheet->active_cell.col >= drawing_range.col0 &&
2584       sheet->active_cell.col <= drawing_range.coli)
2585     gtk_sheet_show_active_cell (sheet);
2586 }
2587
2588 static void
2589 gtk_sheet_range_draw_selection (GtkSheet *sheet, GtkSheetRange range)
2590 {
2591   GdkRectangle area;
2592   gint i, j;
2593   GtkSheetRange aux;
2594
2595   if (range.col0 > sheet->range.coli || range.coli < sheet->range.col0 ||
2596       range.row0 > sheet->range.rowi || range.rowi < sheet->range.row0)
2597     return;
2598
2599   if (!gtk_sheet_range_isvisible (sheet, range)) return;
2600   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2601
2602   aux = range;
2603
2604   range.col0 = MAX (sheet->range.col0, range.col0);
2605   range.coli = MIN (sheet->range.coli, range.coli);
2606   range.row0 = MAX (sheet->range.row0, range.row0);
2607   range.rowi = MIN (sheet->range.rowi, range.rowi);
2608
2609   range.col0 = MAX (range.col0, MIN_VISIBLE_COLUMN (sheet));
2610   range.coli = MIN (range.coli, MAX_VISIBLE_COLUMN (sheet));
2611   range.row0 = MAX (range.row0, MIN_VISIBLE_ROW (sheet));
2612   range.rowi = MIN (range.rowi, MAX_VISIBLE_ROW (sheet));
2613
2614   for (i = range.row0; i <= range.rowi; i++)
2615     {
2616       for (j = range.col0; j <= range.coli; j++)
2617         {
2618
2619           if (gtk_sheet_cell_get_state (sheet, i, j) == GTK_STATE_SELECTED &&
2620               g_sheet_column_get_visibility (sheet->column_geometry, j) && g_sheet_row_get_visibility (sheet->row_geometry, i))
2621             {
2622               area.x = COLUMN_LEFT_XPIXEL (sheet, j);
2623               if ( sheet->row_titles_visible)
2624                 area.x += sheet->row_title_area.width;
2625
2626               area.x -= sheet->hadjustment->value;
2627
2628               area.y = g_sheet_row_start_pixel (sheet->row_geometry, i);
2629               if ( sheet->column_titles_visible)
2630                 area.y += sheet->column_title_area.height;
2631
2632               area.y -= sheet->vadjustment->value;
2633
2634
2635               area.width= g_sheet_column_get_width (sheet->column_geometry, j);
2636               area.height = g_sheet_row_get_height (sheet->row_geometry, i);
2637
2638               if (i == sheet->range.row0)
2639                 {
2640                   area.y = area.y + 2;
2641                   area.height = area.height - 2;
2642                 }
2643               if (i == sheet->range.rowi) area.height = area.height - 3;
2644               if (j == sheet->range.col0)
2645                 {
2646                   area.x = area.x + 2;
2647                   area.width = area.width - 2;
2648                 }
2649               if (j == sheet->range.coli) area.width = area.width - 3;
2650
2651               if (i != sheet->active_cell.row || j != sheet->active_cell.col)
2652                 {
2653                   gdk_draw_rectangle (sheet->sheet_window,
2654                                       sheet->xor_gc,
2655                                       TRUE,
2656                                       area.x + 1, area.y + 1,
2657                                       area.width, area.height);
2658                 }
2659             }
2660
2661         }
2662     }
2663
2664   gtk_sheet_draw_border (sheet, sheet->range);
2665 }
2666
2667 static void
2668 gtk_sheet_draw_backing_pixmap (GtkSheet *sheet, GtkSheetRange range)
2669 {
2670   gint width, height;
2671
2672   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2673
2674   if ( sheet->pixmap == NULL) return;
2675
2676   gdk_drawable_get_size (sheet->pixmap, &width, &height);
2677
2678   gdk_draw_drawable (sheet->sheet_window,
2679                    GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
2680                    sheet->pixmap,
2681                      0, 0, /* src */
2682                      sheet->row_titles_visible ? sheet->row_title_area.width : 0,
2683                      sheet->column_titles_visible ? sheet->column_title_area.height : 0,
2684                      width, height);
2685 }
2686
2687 static void gtk_sheet_set_cell (GtkSheet *sheet, gint row, gint col,
2688                                 GtkJustification justification,
2689                                 const gchar *text);
2690
2691
2692 static inline gint
2693 safe_strcmp (const gchar *s1, const gchar *s2)
2694 {
2695   if ( !s1 && !s2) return 0;
2696   if ( !s1) return - 1;
2697   if ( !s2) return +1;
2698   return strcmp (s1, s2);
2699 }
2700
2701 static void
2702 gtk_sheet_set_cell (GtkSheet *sheet, gint row, gint col,
2703                     GtkJustification justification,
2704                     const gchar *text)
2705 {
2706   GSheetModel *model ;
2707   gboolean changed ;
2708   gchar *old_text ;
2709
2710   GtkSheetRange range;
2711   gint text_width;
2712   GtkSheetCellAttr attributes;
2713
2714   g_return_if_fail (sheet != NULL);
2715   g_return_if_fail (GTK_IS_SHEET (sheet));
2716   if (col >= g_sheet_column_get_column_count (sheet->column_geometry) || row >= g_sheet_row_get_row_count (sheet->row_geometry)) return;
2717   if (col < 0 || row < 0) return;
2718
2719   gtk_sheet_get_attributes (sheet, row, col, &attributes);
2720
2721   attributes.justification = justification;
2722
2723   model = gtk_sheet_get_model (sheet);
2724
2725   old_text = g_sheet_model_get_string (model, row, col);
2726
2727   changed = FALSE;
2728
2729   if (0 != safe_strcmp (old_text, text))
2730     changed = g_sheet_model_set_string (model, text, row, col);
2731
2732   if ( g_sheet_model_free_strings (model))
2733     g_free (old_text);
2734
2735
2736   if (changed && attributes.is_visible)
2737     {
2738       gchar *s = gtk_sheet_cell_get_text (sheet, row, col);
2739       text_width = 0;
2740       if (s && strlen (s) > 0)
2741         {
2742           text_width = STRING_WIDTH (GTK_WIDGET (sheet),
2743                                      attributes.font_desc, text);
2744         }
2745       dispose_string (sheet, s);
2746
2747       range.row0 = row;
2748       range.rowi = row;
2749       range.col0 = MIN_VISIBLE_COLUMN (sheet);
2750       range.coli = MAX_VISIBLE_COLUMN (sheet);
2751
2752       if (gtk_sheet_autoresize (sheet) &&
2753           text_width > g_sheet_column_get_width (sheet->column_geometry, col) -
2754           2 * COLUMN_TITLES_HEIGHT- attributes.border.width)
2755         {
2756           gtk_sheet_set_column_width (sheet, col, text_width + 2 * COLUMN_TITLES_HEIGHT
2757                                       + attributes.border.width);
2758           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_REDRAW_PENDING);
2759         }
2760       else
2761           gtk_sheet_range_draw (sheet, &range);
2762     }
2763
2764   if ( changed )
2765     g_signal_emit (sheet, sheet_signals[CHANGED], 0, row, col);
2766
2767 }
2768
2769
2770 void
2771 gtk_sheet_cell_clear (GtkSheet *sheet, gint row, gint column)
2772 {
2773   GtkSheetRange range;
2774
2775   g_return_if_fail (sheet != NULL);
2776   g_return_if_fail (GTK_IS_SHEET (sheet));
2777   if (column >= g_sheet_column_get_column_count (sheet->column_geometry) ||
2778       row >= g_sheet_row_get_row_count (sheet->row_geometry)) return;
2779
2780   if (column < 0 || row < 0) return;
2781
2782   range.row0 = row;
2783   range.rowi = row;
2784   range.col0 = MIN_VISIBLE_COLUMN (sheet);
2785   range.coli = MAX_VISIBLE_COLUMN (sheet);
2786
2787   gtk_sheet_real_cell_clear (sheet, row, column);
2788
2789       gtk_sheet_range_draw (sheet, &range);
2790     }
2791
2792 static void
2793 gtk_sheet_real_cell_clear (GtkSheet *sheet, gint row, gint column)
2794 {
2795   GSheetModel *model = gtk_sheet_get_model (sheet);
2796
2797   gchar *old_text = gtk_sheet_cell_get_text (sheet, row, column);
2798
2799   if (old_text && strlen (old_text) > 0 )
2800     {
2801       g_sheet_model_datum_clear (model, row, column);
2802     }
2803
2804   dispose_string (sheet, old_text);
2805 }
2806
2807 void
2808 gtk_sheet_range_clear (GtkSheet *sheet, const GtkSheetRange *range)
2809 {
2810   g_return_if_fail (sheet != NULL);
2811   g_return_if_fail (GTK_IS_SHEET (sheet));
2812
2813   gtk_sheet_real_range_clear (sheet, range);
2814 }
2815
2816 static void
2817 gtk_sheet_real_range_clear (GtkSheet *sheet, const GtkSheetRange *range)
2818 {
2819   gint i, j;
2820   GtkSheetRange clear;
2821
2822   if (!range)
2823     {
2824       clear.row0 = 0;
2825       clear.rowi = g_sheet_row_get_row_count (sheet->row_geometry) - 1;
2826       clear.col0 = 0;
2827       clear.coli = g_sheet_column_get_column_count (sheet->column_geometry) - 1;
2828     }
2829   else
2830     clear=*range;
2831
2832   clear.row0 = MAX (clear.row0, 0);
2833   clear.col0 = MAX (clear.col0, 0);
2834   clear.rowi = MIN (clear.rowi, g_sheet_row_get_row_count (sheet->row_geometry) - 1 );
2835   clear.coli = MIN (clear.coli, g_sheet_column_get_column_count (sheet->column_geometry) - 1 );
2836
2837   for (i = clear.row0; i <= clear.rowi; i++)
2838     for (j = clear.col0; j <= clear.coli; j++)
2839       {
2840         gtk_sheet_real_cell_clear (sheet, i, j);
2841       }
2842
2843   gtk_sheet_range_draw (sheet, NULL);
2844 }
2845
2846
2847 static gboolean
2848 gtk_sheet_cell_empty (const GtkSheet *sheet, gint row, gint col)
2849 {
2850   gboolean empty;
2851   char *text = gtk_sheet_cell_get_text (sheet, row, col);
2852   empty = (text == NULL );
2853
2854   dispose_string (sheet, text);
2855
2856   return empty;
2857 }
2858
2859
2860 gchar *
2861 gtk_sheet_cell_get_text (const GtkSheet *sheet, gint row, gint col)
2862 {
2863   GSheetModel *model;
2864   g_return_val_if_fail (sheet != NULL, NULL);
2865   g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);
2866
2867   if (col >= g_sheet_column_get_column_count (sheet->column_geometry) || row >= g_sheet_row_get_row_count (sheet->row_geometry))
2868     return NULL;
2869   if (col < 0 || row < 0) return NULL;
2870
2871   model = gtk_sheet_get_model (sheet);
2872
2873   if ( !model )
2874     return NULL;
2875
2876   return g_sheet_model_get_string (model, row, col);
2877 }
2878
2879
2880 GtkStateType
2881 gtk_sheet_cell_get_state (GtkSheet *sheet, gint row, gint col)
2882 {
2883   gint state;
2884   GtkSheetRange *range;
2885
2886   g_return_val_if_fail (sheet != NULL, 0);
2887   g_return_val_if_fail (GTK_IS_SHEET (sheet), 0);
2888   if (col >= g_sheet_column_get_column_count (sheet->column_geometry) || row >= g_sheet_row_get_row_count (sheet->row_geometry)) return 0;
2889   if (col < 0 || row < 0) return 0;
2890
2891   state = sheet->state;
2892   range = &sheet->range;
2893
2894   switch (state)
2895     {
2896     case GTK_SHEET_NORMAL:
2897       return GTK_STATE_NORMAL;
2898       break;
2899     case GTK_SHEET_ROW_SELECTED:
2900       if (row >= range->row0 && row <= range->rowi)
2901         return GTK_STATE_SELECTED;
2902       break;
2903     case GTK_SHEET_COLUMN_SELECTED:
2904       if (col >= range->col0 && col <= range->coli)
2905         return GTK_STATE_SELECTED;
2906       break;
2907     case GTK_SHEET_RANGE_SELECTED:
2908       if (row >= range->row0 && row <= range->rowi && \
2909           col >= range->col0 && col <= range->coli)
2910         return GTK_STATE_SELECTED;
2911       break;
2912     }
2913   return GTK_STATE_NORMAL;
2914 }
2915
2916 /* Convert X, Y (in pixels) to *ROW, *COLUMN
2917    If the function returns FALSE, then the results will be unreliable.
2918 */
2919 static gboolean
2920 gtk_sheet_get_pixel_info (GtkSheet *sheet,
2921                           gint x,
2922                           gint y,
2923                           gint *row,
2924                           gint *column)
2925 {
2926   gint trow, tcol;
2927   *row = -G_MAXINT;
2928   *column = -G_MAXINT;
2929
2930   g_return_val_if_fail (sheet != NULL, 0);
2931   g_return_val_if_fail (GTK_IS_SHEET (sheet), 0);
2932
2933   /* bounds checking, return false if the user clicked
2934      on a blank area */
2935   if (y < 0)
2936     return FALSE;
2937
2938   if (x < 0)
2939     return FALSE;
2940
2941   if ( sheet->column_titles_visible)
2942     y -= sheet->column_title_area.height;
2943
2944   y += sheet->vadjustment->value;
2945
2946   trow = yyy_row_ypixel_to_row (sheet, y);
2947   if (trow > g_sheet_row_get_row_count (sheet->row_geometry))
2948         return FALSE;
2949
2950       *row = trow;
2951
2952   if ( sheet->row_titles_visible)
2953     x -= sheet->row_title_area.width;
2954
2955   x += sheet->hadjustment->value;
2956
2957       tcol = COLUMN_FROM_XPIXEL (sheet, x);
2958   if (tcol > g_sheet_column_get_column_count (sheet->column_geometry))
2959         return FALSE;
2960
2961       *column = tcol;
2962
2963   return TRUE;
2964 }
2965
2966 gboolean
2967 gtk_sheet_get_cell_area (GtkSheet * sheet,
2968                          gint row,
2969                          gint column,
2970                          GdkRectangle *area)
2971 {
2972   g_return_val_if_fail (sheet != NULL, 0);
2973   g_return_val_if_fail (GTK_IS_SHEET (sheet), 0);
2974
2975   if (row >= g_sheet_row_get_row_count (sheet->row_geometry) || column >= g_sheet_column_get_column_count (sheet->column_geometry))
2976     return FALSE;
2977
2978   area->x = (column == -1) ? 0 : COLUMN_LEFT_XPIXEL (sheet, column);
2979   area->y = (row == -1)    ? 0 : g_sheet_row_start_pixel (sheet->row_geometry, row);
2980
2981   area->width= (column == -1) ? sheet->row_title_area.width
2982     : g_sheet_column_get_width (sheet->column_geometry, column);
2983
2984   area->height= (row == -1) ? sheet->column_title_area.height
2985     : g_sheet_row_get_height (sheet->row_geometry, row);
2986
2987   return TRUE;
2988 }
2989
2990 gboolean
2991 gtk_sheet_set_active_cell (GtkSheet *sheet, gint row, gint column)
2992 {
2993   g_return_val_if_fail (sheet != NULL, 0);
2994   g_return_val_if_fail (GTK_IS_SHEET (sheet), 0);
2995
2996   if (row < - 1 || column < - 1) return FALSE;
2997   if (row >= g_sheet_row_get_row_count (sheet->row_geometry) || column >= g_sheet_column_get_column_count (sheet->column_geometry))
2998     return FALSE;
2999
3000   if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
3001     gtk_sheet_deactivate_cell (sheet);
3002
3003   sheet->active_cell.row = row;
3004   sheet->active_cell.col = column;
3005
3006   if ( row == -1 || column == -1)
3007     {
3008       gtk_sheet_hide_active_cell (sheet);
3009       return TRUE;
3010     }
3011
3012   if (!gtk_sheet_activate_cell (sheet, row, column)) return FALSE;
3013
3014   if (gtk_sheet_autoscroll (sheet))
3015     gtk_sheet_move_query (sheet, row, column);
3016
3017   return TRUE;
3018 }
3019
3020 void
3021 gtk_sheet_get_active_cell (GtkSheet *sheet, gint *row, gint *column)
3022 {
3023   g_return_if_fail (sheet != NULL);
3024   g_return_if_fail (GTK_IS_SHEET (sheet));
3025
3026   if ( row ) *row = sheet->active_cell.row;
3027   if (column) *column = sheet->active_cell.col;
3028 }
3029
3030 static void
3031 gtk_sheet_entry_changed (GtkWidget *widget, gpointer data)
3032 {
3033   GtkSheet *sheet;
3034   gint row, col;
3035   const char *text;
3036   GtkJustification justification;
3037   GtkSheetCellAttr attributes;
3038
3039   g_return_if_fail (data != NULL);
3040   g_return_if_fail (GTK_IS_SHEET (data));
3041
3042   sheet = GTK_SHEET (data);
3043
3044   if (!GTK_WIDGET_VISIBLE (widget)) return;
3045   if (sheet->state != GTK_STATE_NORMAL) return;
3046
3047   row = sheet->active_cell.row;
3048   col = sheet->active_cell.col;
3049
3050   if (row < 0 || col < 0) return;
3051
3052   sheet->active_cell.row = -1;
3053   sheet->active_cell.col = -1;
3054
3055   text = gtk_entry_get_text (GTK_ENTRY (gtk_sheet_get_entry (sheet)));
3056
3057   if (text && strlen (text) > 0)
3058     {
3059       gtk_sheet_get_attributes (sheet, row, col, &attributes);
3060       justification = attributes.justification;
3061       gtk_sheet_set_cell (sheet, row, col, justification, text);
3062     }
3063
3064   sheet->active_cell.row = row;;
3065   sheet->active_cell.col = col;
3066 }
3067
3068
3069 static void
3070 gtk_sheet_deactivate_cell (GtkSheet *sheet)
3071 {
3072   g_return_if_fail (sheet != NULL);
3073   g_return_if_fail (GTK_IS_SHEET (sheet));
3074
3075   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return ;
3076   if (sheet->state != GTK_SHEET_NORMAL) return ;
3077
3078   if ( sheet->active_cell.row == -1 || sheet->active_cell.col == -1 )
3079     return ;
3080
3081   g_signal_emit (sheet, sheet_signals[DEACTIVATE], 0,
3082                  sheet->active_cell.row,
3083                  sheet->active_cell.col);
3084
3085
3086   g_signal_handlers_disconnect_by_func (gtk_sheet_get_entry (sheet),
3087                                         G_CALLBACK (gtk_sheet_entry_changed),
3088                                         sheet);
3089
3090   gtk_sheet_hide_active_cell (sheet);
3091   sheet->active_cell.row = -1;
3092   sheet->active_cell.col = -1;
3093
3094   if (GTK_SHEET_REDRAW_PENDING (sheet))
3095     {
3096       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_REDRAW_PENDING);
3097       gtk_sheet_range_draw (sheet, NULL);
3098     }
3099 }
3100
3101 static void
3102 gtk_sheet_hide_active_cell (GtkSheet *sheet)
3103 {
3104   const char *text;
3105   gint row, col;
3106   GtkJustification justification;
3107   GtkSheetCellAttr attributes;
3108
3109   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
3110
3111   row = sheet->active_cell.row;
3112   col = sheet->active_cell.col;
3113
3114   if (row < 0 || col < 0) return;
3115
3116   text = gtk_entry_get_text (GTK_ENTRY (gtk_sheet_get_entry (sheet)));
3117
3118   gtk_sheet_get_attributes (sheet, row, col, &attributes);
3119   justification = attributes.justification;
3120
3121   row = sheet->active_cell.row;
3122   col = sheet->active_cell.col;
3123
3124   gtk_widget_hide (sheet->entry_widget);
3125   gtk_widget_unmap (sheet->entry_widget);
3126
3127   gtk_widget_grab_focus (GTK_WIDGET (sheet));
3128
3129   GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
3130 }
3131
3132 static gboolean
3133 gtk_sheet_activate_cell (GtkSheet *sheet, gint row, gint col)
3134 {
3135   gboolean veto = TRUE;
3136
3137   g_return_val_if_fail (sheet != NULL, FALSE);
3138   g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE);
3139
3140   if (row < 0 || col < 0) return FALSE;
3141
3142   if ( row > g_sheet_row_get_row_count (sheet->row_geometry) || col > g_sheet_column_get_column_count (sheet->column_geometry))
3143     return FALSE;
3144
3145   if (!veto) return FALSE;
3146   if (sheet->state != GTK_SHEET_NORMAL)
3147     {
3148       sheet->state = GTK_SHEET_NORMAL;
3149       gtk_sheet_real_unselect_range (sheet, NULL);
3150     }
3151
3152   sheet->range.row0 = row;
3153   sheet->range.col0 = col;
3154   sheet->range.rowi = row;
3155   sheet->range.coli = col;
3156   sheet->active_cell.row = row;
3157   sheet->active_cell.col = col;
3158   sheet->selection_cell.row = row;
3159   sheet->selection_cell.col = col;
3160
3161   GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
3162
3163
3164
3165   gtk_sheet_show_active_cell (sheet);
3166
3167   g_signal_connect (gtk_sheet_get_entry (sheet),
3168                     "changed",
3169                     G_CALLBACK (gtk_sheet_entry_changed),
3170                     sheet);
3171
3172   _gtkextra_signal_emit (GTK_OBJECT (sheet), sheet_signals [ACTIVATE], row, col, &veto);
3173
3174   return TRUE;
3175 }
3176
3177 static void
3178 gtk_sheet_show_active_cell (GtkSheet *sheet)
3179 {
3180   GtkEntry *sheet_entry;
3181   GtkSheetCellAttr attributes;
3182   gchar *text = NULL;
3183   const gchar *old_text;
3184   GtkJustification justification;
3185   gint row, col;
3186
3187   g_return_if_fail (sheet != NULL);
3188   g_return_if_fail (GTK_IS_SHEET (sheet));
3189
3190   row = sheet->active_cell.row;
3191   col = sheet->active_cell.col;
3192
3193   /* Don't show the active cell, if there is no active cell: */
3194   if (! (row >= 0 && col >= 0)) /* e.g row or coll == -1. */
3195     return;
3196
3197   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
3198   if (sheet->state != GTK_SHEET_NORMAL) return;
3199   if (GTK_SHEET_IN_SELECTION (sheet)) return;
3200
3201   GTK_WIDGET_SET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
3202
3203   sheet_entry = GTK_ENTRY (gtk_sheet_get_entry (sheet));
3204
3205   gtk_sheet_get_attributes (sheet, row, col, &attributes);
3206
3207   justification = GTK_JUSTIFY_LEFT;
3208
3209   if (gtk_sheet_justify_entry (sheet))
3210     justification = attributes.justification;
3211
3212   text = gtk_sheet_cell_get_text (sheet, row, col);
3213   if ( ! text )
3214     text = g_strdup ("");
3215
3216   gtk_entry_set_visibility (GTK_ENTRY (sheet_entry), attributes.is_visible);
3217
3218
3219   /*** Added by John Gotts. Mar 25, 2005 *********/
3220   old_text = gtk_entry_get_text (GTK_ENTRY (sheet_entry));
3221   if (strcmp (old_text, text) != 0)
3222     {
3223       if (!GTK_IS_ITEM_ENTRY (sheet_entry))
3224         gtk_entry_set_text (GTK_ENTRY (sheet_entry), text);
3225       else
3226         gtk_item_entry_set_text (GTK_ITEM_ENTRY (sheet_entry), text, justification);
3227     }
3228
3229   gtk_sheet_entry_set_max_size (sheet);
3230   gtk_sheet_size_allocate_entry (sheet);
3231
3232   gtk_widget_map (sheet->entry_widget);
3233
3234   gtk_widget_grab_focus (GTK_WIDGET (sheet_entry));
3235
3236   dispose_string (sheet, text);
3237 }
3238
3239 static void
3240 gtk_sheet_draw_active_cell (GtkSheet *sheet)
3241 {
3242   gint row, col;
3243   GtkSheetRange range;
3244
3245   if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (sheet))) return;
3246   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
3247
3248   row = sheet->active_cell.row;
3249   col = sheet->active_cell.col;
3250
3251   if (row < 0 || col < 0) return;
3252
3253   if (!gtk_sheet_cell_isvisible (sheet, row, col)) return;
3254
3255   range.col0 = range.coli = col;
3256   range.row0 = range.rowi = row;
3257
3258   gtk_sheet_draw_border (sheet, range);
3259 }
3260
3261
3262
3263 static void
3264 gtk_sheet_make_backing_pixmap (GtkSheet *sheet)
3265 {
3266   gint pixmap_width, pixmap_height;
3267   gint width, height;
3268
3269   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
3270
3271   width = sheet->sheet_window_width ;
3272   height = sheet->sheet_window_height ;
3273
3274
3275   if ( width <= 0) return;
3276   if ( height <= 0) return;
3277
3278   if (!sheet->pixmap)
3279     {
3280       /* allocate */
3281       sheet->pixmap = gdk_pixmap_new (sheet->sheet_window,
3282                                       width, height,
3283                                       - 1);
3284
3285       gtk_sheet_range_draw (sheet, NULL);
3286     }
3287   else
3288     {
3289       /* reallocate if sizes don't match */
3290       gdk_drawable_get_size (sheet->pixmap,
3291                              &pixmap_width, &pixmap_height);
3292       if ( (pixmap_width != width) || (pixmap_height != height))
3293         {
3294           g_object_unref (sheet->pixmap);
3295           sheet->pixmap = gdk_pixmap_new (sheet->sheet_window,
3296                                           width, height,
3297                                           - 1);
3298           gtk_sheet_range_draw (sheet, NULL);
3299         }
3300     }
3301 }
3302
3303 static void
3304 gtk_sheet_new_selection (GtkSheet *sheet, GtkSheetRange *range)
3305 {
3306   gint i, j, mask1, mask2;
3307   gint state, selected;
3308   gint x, y, width, height;
3309   GtkSheetRange new_range, aux_range;
3310
3311   g_return_if_fail (sheet != NULL);
3312
3313   if (range == NULL) range=&sheet->range;
3314
3315   new_range=*range;
3316
3317   range->row0 = MIN (range->row0, sheet->range.row0);
3318   range->rowi = MAX (range->rowi, sheet->range.rowi);
3319   range->col0 = MIN (range->col0, sheet->range.col0);
3320   range->coli = MAX (range->coli, sheet->range.coli);
3321
3322   range->row0 = MAX (range->row0, MIN_VISIBLE_ROW (sheet));
3323   range->rowi = MIN (range->rowi, MAX_VISIBLE_ROW (sheet));
3324   range->col0 = MAX (range->col0, MIN_VISIBLE_COLUMN (sheet));
3325   range->coli = MIN (range->coli, MAX_VISIBLE_COLUMN (sheet));
3326
3327   aux_range.row0 = MAX (new_range.row0, MIN_VISIBLE_ROW (sheet));
3328   aux_range.rowi = MIN (new_range.rowi, MAX_VISIBLE_ROW (sheet));
3329   aux_range.col0 = MAX (new_range.col0, MIN_VISIBLE_COLUMN (sheet));
3330   aux_range.coli = MIN (new_range.coli, MAX_VISIBLE_COLUMN (sheet));
3331
3332   for (i = range->row0; i <= range->rowi; i++)
3333     {
3334       for (j = range->col0; j <= range->coli; j++)
3335         {
3336
3337           state = gtk_sheet_cell_get_state (sheet, i, j);
3338           selected= (i <= new_range.rowi && i >= new_range.row0 &&
3339                      j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
3340
3341           if (state == GTK_STATE_SELECTED && selected &&
3342               g_sheet_column_get_visibility (sheet->column_geometry, j) && g_sheet_row_get_visibility (sheet->row_geometry, i) &&
3343               (i == sheet->range.row0 || i == sheet->range.rowi ||
3344                j == sheet->range.col0 || j == sheet->range.coli ||
3345                i == new_range.row0 || i == new_range.rowi ||
3346                j == new_range.col0 || j == new_range.coli))
3347             {
3348
3349               mask1 = i == sheet->range.row0 ? 1 : 0;
3350               mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1;
3351               mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1;
3352               mask1 = j == sheet->range.coli ? mask1 + 8 : mask1;
3353
3354               mask2 = i == new_range.row0 ? 1 : 0;
3355               mask2 = i == new_range.rowi ? mask2 + 2 : mask2;
3356               mask2 = j == new_range.col0 ? mask2 + 4 : mask2;
3357               mask2 = j == new_range.coli ? mask2 + 8 : mask2;
3358
3359               if (mask1 != mask2)
3360                 {
3361                   x = COLUMN_LEFT_XPIXEL (sheet, j);
3362                   y = g_sheet_row_start_pixel (sheet->row_geometry, i);
3363                   width = COLUMN_LEFT_XPIXEL (sheet, j)- x+
3364                     g_sheet_column_get_width (sheet->column_geometry, j);
3365                   height = g_sheet_row_start_pixel (sheet->row_geometry, i) - y + g_sheet_row_get_height (sheet->row_geometry, i);
3366
3367                   if (i == sheet->range.row0)
3368                     {
3369                       y = y - 3;
3370                       height = height + 3;
3371                     }
3372                   if (i == sheet->range.rowi) height = height + 3;
3373                   if (j == sheet->range.col0)
3374                     {
3375                       x = x - 3;
3376                       width = width + 3;
3377                     }
3378                   if (j == sheet->range.coli) width = width + 3;
3379
3380                   if (i != sheet->active_cell.row || j != sheet->active_cell.col)
3381                     {
3382                       x = COLUMN_LEFT_XPIXEL (sheet, j);
3383                       y = g_sheet_row_start_pixel (sheet->row_geometry, i);
3384                       width = COLUMN_LEFT_XPIXEL (sheet, j)- x+
3385                         g_sheet_column_get_width (sheet->column_geometry, j);
3386
3387                       height = g_sheet_row_start_pixel (sheet->row_geometry, i) - y + g_sheet_row_get_height (sheet->row_geometry, i);
3388
3389                       if (i == new_range.row0)
3390                         {
3391                           y = y+2;
3392                           height = height - 2;
3393                         }
3394                       if (i == new_range.rowi) height = height - 3;
3395                       if (j == new_range.col0)
3396                         {
3397                           x = x+2;
3398                           width = width - 2;
3399                         }
3400                       if (j == new_range.coli) width = width - 3;
3401
3402                       gdk_draw_rectangle (sheet->sheet_window,
3403                                           sheet->xor_gc,
3404                                           TRUE,
3405                                           x + 1, y + 1,
3406                                           width, height);
3407                     }
3408                 }
3409             }
3410         }
3411     }
3412
3413   for (i = range->row0; i <= range->rowi; i++)
3414     {
3415       for (j = range->col0; j <= range->coli; j++)
3416         {
3417
3418           state = gtk_sheet_cell_get_state (sheet, i, j);
3419           selected= (i <= new_range.rowi && i >= new_range.row0 &&
3420                      j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
3421
3422           if (state == GTK_STATE_SELECTED && !selected &&
3423               g_sheet_column_get_visibility (sheet->column_geometry, j) && g_sheet_row_get_visibility (sheet->row_geometry, i))
3424             {
3425
3426               x = COLUMN_LEFT_XPIXEL (sheet, j);
3427               y = g_sheet_row_start_pixel (sheet->row_geometry, i);
3428               width = COLUMN_LEFT_XPIXEL (sheet, j) - x + g_sheet_column_get_width (sheet->column_geometry, j);
3429               height = g_sheet_row_start_pixel (sheet->row_geometry, i) - y + g_sheet_row_get_height (sheet->row_geometry, i);
3430
3431               if (i == sheet->range.row0)
3432                 {
3433                   y = y - 3;
3434                   height = height + 3;
3435                 }
3436               if (i == sheet->range.rowi) height = height + 3;
3437               if (j == sheet->range.col0)
3438                 {
3439                   x = x - 3;
3440                   width = width + 3;
3441                 }
3442               if (j == sheet->range.coli) width = width + 3;
3443
3444             }
3445         }
3446     }
3447
3448   for (i = range->row0; i <= range->rowi; i++)
3449     {
3450       for (j = range->col0; j <= range->coli; j++)
3451         {
3452
3453           state = gtk_sheet_cell_get_state (sheet, i, j);
3454           selected= (i <= new_range.rowi && i >= new_range.row0 &&
3455                      j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
3456
3457           if (state != GTK_STATE_SELECTED && selected &&
3458               g_sheet_column_get_visibility (sheet->column_geometry, j) && g_sheet_row_get_visibility (sheet->row_geometry, i) &&
3459               (i != sheet->active_cell.row || j != sheet->active_cell.col))
3460             {
3461
3462               x = COLUMN_LEFT_XPIXEL (sheet, j);
3463               y = g_sheet_row_start_pixel (sheet->row_geometry, i);
3464               width = COLUMN_LEFT_XPIXEL (sheet, j) - x + g_sheet_column_get_width (sheet->column_geometry, j);
3465               height = g_sheet_row_start_pixel (sheet->row_geometry, i) - y + g_sheet_row_get_height (sheet->row_geometry, i);
3466
3467               if (i == new_range.row0)
3468                 {
3469                   y = y+2;
3470                   height = height - 2;
3471                 }
3472               if (i == new_range.rowi) height = height - 3;
3473               if (j == new_range.col0)
3474                 {
3475                   x = x+2;
3476                   width = width - 2;
3477                 }
3478               if (j == new_range.coli) width = width - 3;
3479
3480               gdk_draw_rectangle (sheet->sheet_window,
3481                                   sheet->xor_gc,
3482                                   TRUE,
3483                                   x + 1, y + 1,
3484                                   width, height);
3485
3486             }
3487
3488         }
3489     }
3490
3491   for (i = aux_range.row0; i <= aux_range.rowi; i++)
3492     {
3493       for (j = aux_range.col0; j <= aux_range.coli; j++)
3494         {
3495
3496           if (g_sheet_column_get_visibility (sheet->column_geometry, j) && g_sheet_row_get_visibility (sheet->row_geometry, i))
3497             {
3498
3499               state = gtk_sheet_cell_get_state (sheet, i, j);
3500
3501               mask1 = i == sheet->range.row0 ? 1 : 0;
3502               mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1;
3503               mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1;
3504               mask1 = j == sheet->range.coli ? mask1 + 8 : mask1;
3505
3506               mask2 = i == new_range.row0 ? 1 : 0;
3507               mask2 = i == new_range.rowi ? mask2 + 2 : mask2;
3508               mask2 = j == new_range.col0 ? mask2 + 4 : mask2;
3509               mask2 = j == new_range.coli ? mask2 + 8 : mask2;
3510               if (mask2 != mask1 || (mask2 == mask1 && state != GTK_STATE_SELECTED))
3511                 {
3512                   x = COLUMN_LEFT_XPIXEL (sheet, j);
3513                   y = g_sheet_row_start_pixel (sheet->row_geometry, i);
3514                   width = g_sheet_column_get_width (sheet->column_geometry, j);
3515                   height = g_sheet_row_get_height (sheet->row_geometry, i);
3516                   if (mask2 & 1)
3517                     gdk_draw_rectangle (sheet->sheet_window,
3518                                         sheet->xor_gc,
3519                                         TRUE,
3520                                         x + 1, y - 1,
3521                                         width, 3);
3522
3523
3524                   if (mask2 & 2)
3525                     gdk_draw_rectangle (sheet->sheet_window,
3526                                         sheet->xor_gc,
3527                                         TRUE,
3528                                         x + 1, y + height - 1,
3529                                         width, 3);
3530
3531                   if (mask2 & 4)
3532                     gdk_draw_rectangle (sheet->sheet_window,
3533                                         sheet->xor_gc,
3534                                         TRUE,
3535                                         x - 1, y + 1,
3536                                         3, height);
3537
3538
3539                   if (mask2 & 8)
3540                     gdk_draw_rectangle (sheet->sheet_window,
3541                                         sheet->xor_gc,
3542                                         TRUE,
3543                                         x + width - 1, y + 1,
3544                                         3, height);
3545
3546
3547
3548                 }
3549
3550             }
3551
3552         }
3553     }
3554
3555
3556   *range = new_range;
3557 }
3558
3559 static void
3560 gtk_sheet_draw_border (GtkSheet *sheet, GtkSheetRange new_range)
3561 {
3562   GdkRectangle area;
3563   gint width, height;
3564
3565   gint x = COLUMN_LEFT_XPIXEL (sheet, new_range.col0);
3566   gint y = g_sheet_row_start_pixel (sheet->row_geometry, new_range.row0);
3567
3568   if ( sheet->row_titles_visible)
3569     x += sheet->row_title_area.width;
3570
3571   x -= sheet->hadjustment->value;
3572
3573   if ( sheet->column_titles_visible)
3574     y += sheet->column_title_area.height;
3575
3576   y -= sheet->vadjustment->value;
3577
3578   width = COLUMN_LEFT_XPIXEL (sheet, new_range.coli) -
3579     COLUMN_LEFT_XPIXEL (sheet, new_range.col0)
3580     +
3581     g_sheet_column_get_width (sheet->column_geometry, new_range.coli);
3582
3583   height = g_sheet_row_start_pixel (sheet->row_geometry, new_range.rowi) -
3584     g_sheet_row_start_pixel (sheet->row_geometry, new_range.row0)
3585     +
3586     g_sheet_row_get_height (sheet->row_geometry, new_range.rowi);
3587
3588   area.x = COLUMN_LEFT_XPIXEL (sheet, MIN_VISIBLE_COLUMN (sheet));
3589   if ( sheet->row_titles_visible)
3590     area.x += sheet->row_title_area.width;
3591
3592   area.x -= sheet->hadjustment->value;
3593
3594   area.y = g_sheet_row_start_pixel (sheet->row_geometry, MIN_VISIBLE_ROW (sheet));
3595   if ( sheet->column_titles_visible)
3596     area.y += sheet->column_title_area.height;
3597
3598   area.y -= sheet->vadjustment->value;
3599
3600
3601   area.width = sheet->sheet_window_width;
3602   area.height = sheet->sheet_window_height;
3603
3604   gdk_gc_set_clip_rectangle (sheet->xor_gc, &area);
3605
3606       gdk_draw_rectangle (sheet->sheet_window,
3607                           sheet->xor_gc,
3608                       FALSE,
3609                       x, y,
3610                       width - 2,
3611                       height - 2);
3612
3613   gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
3614 }
3615
3616
3617 static void
3618 gtk_sheet_real_select_range (GtkSheet * sheet,
3619                              const GtkSheetRange * range)
3620 {
3621   gint state;
3622
3623   g_return_if_fail (sheet != NULL);
3624
3625   if (range == NULL) range = &sheet->range;
3626
3627   memcpy (&sheet->range, range, sizeof (*range));
3628
3629   if (range->row0 < 0 || range->rowi < 0) return;
3630   if (range->col0 < 0 || range->coli < 0) return;
3631
3632   state = sheet->state;
3633
3634   if (range->coli != sheet->range.coli || range->col0 != sheet->range.col0 ||
3635       range->rowi != sheet->range.rowi || range->row0 != sheet->range.row0)
3636     {
3637       gtk_sheet_new_selection (sheet, &sheet->range);
3638     }
3639   else
3640     {
3641       gtk_sheet_draw_backing_pixmap (sheet, sheet->range);
3642       gtk_sheet_range_draw_selection (sheet, sheet->range);
3643     }
3644
3645   gtk_sheet_update_primary_selection (sheet);
3646
3647   g_signal_emit (sheet, sheet_signals[SELECT_RANGE], 0, &sheet->range);
3648 }
3649
3650
3651 void
3652 gtk_sheet_get_selected_range (GtkSheet *sheet, GtkSheetRange *range)
3653 {
3654   g_return_if_fail (sheet != NULL);
3655   *range = sheet->range;
3656 }
3657
3658
3659 void
3660 gtk_sheet_select_range (GtkSheet * sheet, const GtkSheetRange *range)
3661 {
3662   g_return_if_fail (sheet != NULL);
3663
3664   if (range == NULL) range=&sheet->range;
3665
3666   if (range->row0 < 0 || range->rowi < 0) return;
3667   if (range->col0 < 0 || range->coli < 0) return;
3668
3669
3670   if (sheet->state != GTK_SHEET_NORMAL)
3671     gtk_sheet_real_unselect_range (sheet, NULL);
3672   else
3673     gtk_sheet_deactivate_cell (sheet);
3674
3675   sheet->range.row0 = range->row0;
3676   sheet->range.rowi = range->rowi;
3677   sheet->range.col0 = range->col0;
3678   sheet->range.coli = range->coli;
3679   sheet->active_cell.row = range->row0;
3680   sheet->active_cell.col = range->col0;
3681   sheet->selection_cell.row = range->rowi;
3682   sheet->selection_cell.col = range->coli;
3683
3684   sheet->state = GTK_SHEET_RANGE_SELECTED;
3685   gtk_sheet_real_select_range (sheet, NULL);
3686 }
3687
3688 void
3689 gtk_sheet_unselect_range (GtkSheet * sheet)
3690 {
3691   if (! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
3692     return;
3693
3694   gtk_sheet_real_unselect_range (sheet, NULL);
3695   sheet->state = GTK_STATE_NORMAL;
3696
3697   gtk_sheet_activate_cell (sheet,
3698                            sheet->active_cell.row, sheet->active_cell.col);
3699 }
3700
3701
3702 static void
3703 gtk_sheet_real_unselect_range (GtkSheet * sheet,
3704                                const GtkSheetRange *range)
3705 {
3706   g_return_if_fail (sheet != NULL);
3707   g_return_if_fail (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)));
3708
3709   if ( range == NULL)
3710     range = &sheet->range;
3711
3712   if (range->row0 < 0 || range->rowi < 0) return;
3713   if (range->col0 < 0 || range->coli < 0) return;
3714
3715   g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, -1);
3716   g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, -1);
3717
3718   if (gtk_sheet_range_isvisible (sheet, *range))
3719     gtk_sheet_draw_backing_pixmap (sheet, *range);
3720
3721   sheet->range.row0 = -1;
3722   sheet->range.rowi = -1;
3723   sheet->range.col0 = -1;
3724   sheet->range.coli = -1;
3725 }
3726
3727
3728 static gint
3729 gtk_sheet_expose (GtkWidget * widget,
3730                   GdkEventExpose * event)
3731 {
3732   GtkSheet *sheet;
3733   GtkSheetRange range;
3734
3735   g_return_val_if_fail (widget != NULL, FALSE);
3736   g_return_val_if_fail (GTK_IS_SHEET (widget), FALSE);
3737   g_return_val_if_fail (event != NULL, FALSE);
3738
3739   g_print ("%s %p\n", __FUNCTION__, widget);
3740
3741   sheet = GTK_SHEET (widget);
3742
3743   if (GTK_WIDGET_DRAWABLE (widget))
3744     {
3745       range.row0 = yyy_row_ypixel_to_row (sheet, event->area.y);
3746       range.col0 = COLUMN_FROM_XPIXEL (sheet, event->area.x);
3747       range.rowi = yyy_row_ypixel_to_row (sheet,
3748                                           event->area.y + event->area.height);
3749
3750       range.coli = COLUMN_FROM_XPIXEL (sheet,
3751                                        event->area.x + event->area.width);
3752
3753       g_print ("Redrawing rows %ld--%ld, columns %ld--%ld\n",
3754                range.row0, range.rowi, range.col0, range.coli);
3755
3756
3757       /* exposure events on the sheet */
3758       if (event->window == sheet->row_title_window &&
3759           sheet->row_titles_visible)
3760         {
3761           gint i;
3762           for (i = MIN_VISIBLE_ROW (sheet); i <= MAX_VISIBLE_ROW (sheet); i++)
3763             gtk_sheet_row_title_button_draw (sheet, i);
3764         }
3765
3766       if (event->window == sheet->column_title_window &&
3767           sheet->column_titles_visible)
3768         {
3769           gint i;
3770           for (i = MIN_VISIBLE_COLUMN (sheet);
3771                i <= MAX_VISIBLE_COLUMN (sheet);
3772                ++i)
3773             gtk_sheet_column_title_button_draw (sheet, i);
3774         }
3775
3776       if (event->window == sheet->sheet_window)
3777         {
3778           gtk_sheet_draw_backing_pixmap (sheet, range);
3779
3780           if (sheet->state != GTK_SHEET_NORMAL)
3781             {
3782               if (gtk_sheet_range_isvisible (sheet, sheet->range))
3783                 gtk_sheet_draw_backing_pixmap (sheet, sheet->range);
3784               if (GTK_SHEET_IN_RESIZE (sheet) || GTK_SHEET_IN_DRAG (sheet))
3785                 gtk_sheet_draw_backing_pixmap (sheet, sheet->drag_range);
3786
3787               if (gtk_sheet_range_isvisible (sheet, sheet->range))
3788                 gtk_sheet_range_draw_selection (sheet, sheet->range);
3789               if (GTK_SHEET_IN_RESIZE (sheet) || GTK_SHEET_IN_DRAG (sheet))
3790                 draw_xor_rectangle (sheet, sheet->drag_range);
3791             }
3792
3793           if ((!GTK_SHEET_IN_XDRAG (sheet)) && (!GTK_SHEET_IN_YDRAG (sheet)))
3794             {
3795               if (sheet->state == GTK_SHEET_NORMAL)
3796                 gtk_sheet_draw_active_cell (sheet);
3797             }
3798         }
3799     }
3800
3801   if (sheet->state != GTK_SHEET_NORMAL && GTK_SHEET_IN_SELECTION (sheet))
3802     gtk_widget_grab_focus (GTK_WIDGET (sheet));
3803
3804   (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
3805
3806   return FALSE;
3807 }
3808
3809
3810 static gboolean
3811 gtk_sheet_button_press (GtkWidget * widget,
3812                         GdkEventButton * event)
3813 {
3814   GtkSheet *sheet;
3815   GdkModifierType mods;
3816   gint x, y, row, column;
3817   gboolean veto;
3818
3819   g_return_val_if_fail (widget != NULL, FALSE);
3820   g_return_val_if_fail (GTK_IS_SHEET (widget), FALSE);
3821   g_return_val_if_fail (event != NULL, FALSE);
3822
3823   sheet = GTK_SHEET (widget);
3824
3825   /* Cancel any pending tooltips */
3826   if (sheet->motion_timer)
3827     {
3828       g_source_remove (sheet->motion_timer);
3829       sheet->motion_timer = 0;
3830     }
3831
3832   gtk_widget_get_pointer (widget, &x, &y);
3833   gtk_sheet_get_pixel_info (sheet, x, y, &row, &column);
3834
3835
3836   if (event->window == sheet->column_title_window)
3837     {
3838       g_signal_emit (sheet,
3839                      sheet_signals[BUTTON_EVENT_COLUMN], 0,
3840                      column, event);
3841
3842       if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
3843         g_signal_emit (sheet,
3844                        sheet_signals[DOUBLE_CLICK_COLUMN], 0, column);
3845
3846     }
3847   else if (event->window == sheet->row_title_window)
3848     {
3849       g_signal_emit (sheet,
3850                      sheet_signals[BUTTON_EVENT_ROW], 0,
3851                      row, event);
3852
3853       if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
3854         g_signal_emit (sheet,
3855                        sheet_signals[DOUBLE_CLICK_ROW], 0, row);
3856     }
3857
3858
3859   gdk_window_get_pointer (widget->window, NULL, NULL, &mods);
3860
3861   if (! (mods & GDK_BUTTON1_MASK)) return TRUE;
3862
3863
3864   /* press on resize windows */
3865   if (event->window == sheet->column_title_window &&
3866       gtk_sheet_columns_resizable (sheet))
3867     {
3868 #if 0
3869       gtk_widget_get_pointer (widget, &sheet->x_drag, NULL);
3870       if ( sheet->row_titles_visible)
3871         sheet->x_drag -= sheet->row_title_area.width;
3872 #endif
3873
3874       sheet->x_drag = event->x;
3875
3876       if (on_column_boundary (sheet, sheet->x_drag, &sheet->drag_cell.col))
3877         {
3878           guint req;
3879           if (event->type == GDK_2BUTTON_PRESS)
3880             {
3881               gtk_sheet_autoresize_column (sheet, sheet->drag_cell.col);
3882               GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_XDRAG);
3883               return TRUE;
3884             }
3885           gtk_sheet_column_size_request (sheet, sheet->drag_cell.col, &req);
3886           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_XDRAG);
3887           gdk_pointer_grab (sheet->column_title_window, FALSE,
3888                             GDK_POINTER_MOTION_HINT_MASK |
3889                             GDK_BUTTON1_MOTION_MASK |
3890                             GDK_BUTTON_RELEASE_MASK,
3891                             NULL, NULL, event->time);
3892
3893           draw_xor_vline (sheet);
3894           return TRUE;
3895         }
3896     }
3897
3898   if (event->window == sheet->row_title_window && gtk_sheet_rows_resizable (sheet))
3899     {
3900       gtk_widget_get_pointer (widget, NULL, &sheet->y_drag);
3901
3902       if (POSSIBLE_YDRAG (sheet, sheet->y_drag, &sheet->drag_cell.row))
3903         {
3904           guint req;
3905           gtk_sheet_row_size_request (sheet, sheet->drag_cell.row, &req);
3906           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_YDRAG);
3907           gdk_pointer_grab (sheet->row_title_window, FALSE,
3908                             GDK_POINTER_MOTION_HINT_MASK |
3909                             GDK_BUTTON1_MOTION_MASK |
3910                             GDK_BUTTON_RELEASE_MASK,
3911                             NULL, NULL, event->time);
3912
3913           draw_xor_hline (sheet);
3914           return TRUE;
3915         }
3916     }
3917
3918   /* the sheet itself does not handle other than single click events */
3919   if (event->type != GDK_BUTTON_PRESS) return FALSE;
3920
3921   /* selections on the sheet */
3922   if (event->window == sheet->sheet_window)
3923     {
3924       gtk_widget_get_pointer (widget, &x, &y);
3925       gtk_sheet_get_pixel_info (sheet, x, y, &row, &column);
3926       gdk_pointer_grab (sheet->sheet_window, FALSE,
3927                         GDK_POINTER_MOTION_HINT_MASK |
3928                         GDK_BUTTON1_MOTION_MASK |
3929                         GDK_BUTTON_RELEASE_MASK,
3930                         NULL, NULL, event->time);
3931       gtk_grab_add (GTK_WIDGET (sheet));
3932
3933       /* This seems to be a kludge to work around a problem where the sheet
3934          scrolls to another position.  The timeout scrolls it back to its
3935          original posn.          JMD 3 July 2007
3936       */
3937       gtk_widget_grab_focus (GTK_WIDGET (sheet));
3938
3939       if (sheet->selection_mode != GTK_SELECTION_SINGLE &&
3940           sheet->selection_mode != GTK_SELECTION_NONE &&
3941           sheet->cursor_drag->type == GDK_SIZING &&
3942           !GTK_SHEET_IN_SELECTION (sheet) && !GTK_SHEET_IN_RESIZE (sheet))
3943         {
3944           if (sheet->state == GTK_STATE_NORMAL)
3945             {
3946               row = sheet->active_cell.row;
3947               column = sheet->active_cell.col;
3948               gtk_sheet_deactivate_cell (sheet);
3949               sheet->active_cell.row = row;
3950               sheet->active_cell.col = column;
3951               sheet->drag_range = sheet->range;
3952               sheet->state = GTK_SHEET_RANGE_SELECTED;
3953               gtk_sheet_select_range (sheet, &sheet->drag_range);
3954             }
3955           sheet->x_drag = x;
3956           sheet->y_drag = y;
3957           if (row > sheet->range.rowi) row--;
3958           if (column > sheet->range.coli) column--;
3959           sheet->drag_cell.row = row;
3960           sheet->drag_cell.col = column;
3961           sheet->drag_range = sheet->range;
3962           draw_xor_rectangle (sheet, sheet->drag_range);
3963           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_RESIZE);
3964         }
3965       else if (sheet->cursor_drag->type == GDK_TOP_LEFT_ARROW &&
3966                !GTK_SHEET_IN_SELECTION (sheet)
3967                && ! GTK_SHEET_IN_DRAG (sheet)
3968                && sheet->active_cell.row >= 0
3969                && sheet->active_cell.col >= 0
3970                )
3971         {
3972           if (sheet->state == GTK_STATE_NORMAL)
3973             {
3974               row = sheet->active_cell.row;
3975               column = sheet->active_cell.col;
3976               gtk_sheet_deactivate_cell (sheet);
3977               sheet->active_cell.row = row;
3978               sheet->active_cell.col = column;
3979               sheet->drag_range = sheet->range;
3980               sheet->state = GTK_SHEET_RANGE_SELECTED;
3981               gtk_sheet_select_range (sheet, &sheet->drag_range);
3982             }
3983           sheet->x_drag = x;
3984           sheet->y_drag = y;
3985           if (row < sheet->range.row0) row++;
3986           if (row > sheet->range.rowi) row--;
3987           if (column < sheet->range.col0) column++;
3988           if (column > sheet->range.coli) column--;
3989           sheet->drag_cell.row = row;
3990           sheet->drag_cell.col = column;
3991           sheet->drag_range = sheet->range;
3992           draw_xor_rectangle (sheet, sheet->drag_range);
3993           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_DRAG);
3994         }
3995       else
3996         {
3997           gtk_sheet_click_cell (sheet, row, column, &veto);
3998           if (veto) GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
3999         }
4000     }
4001
4002   if (event->window == sheet->column_title_window)
4003     {
4004       gtk_widget_get_pointer (widget, &x, &y);
4005       if ( sheet->row_titles_visible)
4006         x -= sheet->row_title_area.width;
4007
4008       x += sheet->hadjustment->value;
4009
4010       column = COLUMN_FROM_XPIXEL (sheet, x);
4011
4012       if (g_sheet_column_get_sensitivity (sheet->column_geometry, column))
4013         {
4014           gtk_sheet_click_cell (sheet, - 1, column, &veto);
4015           gtk_grab_add (GTK_WIDGET (sheet));
4016           gtk_widget_grab_focus (GTK_WIDGET (sheet));
4017           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
4018         }
4019     }
4020
4021   if (event->window == sheet->row_title_window)
4022     {
4023       gtk_widget_get_pointer (widget, &x, &y);
4024       if ( sheet->column_titles_visible)
4025         y -= sheet->column_title_area.height;
4026
4027       y += sheet->vadjustment->value;
4028
4029       row = yyy_row_ypixel_to_row (sheet, y);
4030       if (g_sheet_row_get_sensitivity (sheet->row_geometry, row))
4031         {
4032           gtk_sheet_click_cell (sheet, row, - 1, &veto);
4033           gtk_grab_add (GTK_WIDGET (sheet));
4034           gtk_widget_grab_focus (GTK_WIDGET (sheet));
4035           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
4036         }
4037     }
4038
4039   return TRUE;
4040 }
4041
4042 static void
4043 gtk_sheet_click_cell (GtkSheet *sheet, gint row, gint column, gboolean *veto)
4044 {
4045   *veto = TRUE;
4046
4047   if (row >= g_sheet_row_get_row_count (sheet->row_geometry) || column >= g_sheet_column_get_column_count (sheet->column_geometry))
4048     {
4049       *veto = FALSE;
4050       return;
4051     }
4052
4053   if (column >= 0 && row >= 0)
4054     {
4055       if (! g_sheet_column_get_visibility (sheet->column_geometry, column)
4056           || !g_sheet_row_get_visibility (sheet->row_geometry, row))
4057       {
4058         *veto = FALSE;
4059         return;
4060       }
4061     }
4062
4063   _gtkextra_signal_emit (GTK_OBJECT (sheet), sheet_signals[TRAVERSE],
4064                          sheet->active_cell.row, sheet->active_cell.col,
4065                          &row, &column, veto);
4066
4067   if (!*veto)
4068     {
4069       if (sheet->state == GTK_STATE_NORMAL) return;
4070
4071       row = sheet->active_cell.row;
4072       column = sheet->active_cell.col;
4073
4074       gtk_sheet_activate_cell (sheet, row, column);
4075       return;
4076     }
4077
4078   if (row == -1 && column >= 0)
4079     {
4080       if (gtk_sheet_autoscroll (sheet))
4081         gtk_sheet_move_query (sheet, row, column);
4082       gtk_sheet_select_column (sheet, column);
4083       return;
4084     }
4085   if (column == -1 && row >= 0)
4086     {
4087       if (gtk_sheet_autoscroll (sheet))
4088         gtk_sheet_move_query (sheet, row, column);
4089       gtk_sheet_select_row (sheet, row);
4090       return;
4091     }
4092
4093   if (row == - 1 && column == - 1)
4094     {
4095       sheet->range.row0 = 0;
4096       sheet->range.col0 = 0;
4097       sheet->range.rowi = g_sheet_row_get_row_count (sheet->row_geometry) - 1;
4098       sheet->range.coli = g_sheet_column_get_column_count (sheet->column_geometry) - 1;
4099       sheet->active_cell.row = 0;
4100       sheet->active_cell.col = 0;
4101       gtk_sheet_select_range (sheet, NULL);
4102       return;
4103     }
4104
4105   if (row != -1 && column != -1)
4106     {
4107       if (sheet->state != GTK_SHEET_NORMAL)
4108         {
4109           sheet->state = GTK_SHEET_NORMAL;
4110           gtk_sheet_real_unselect_range (sheet, NULL);
4111         }
4112       else
4113         {
4114           gtk_sheet_deactivate_cell (sheet);
4115           gtk_sheet_activate_cell (sheet, row, column);
4116         }
4117
4118       if (gtk_sheet_autoscroll (sheet))
4119         gtk_sheet_move_query (sheet, row, column);
4120       sheet->active_cell.row = row;
4121       sheet->active_cell.col = column;
4122       sheet->selection_cell.row = row;
4123       sheet->selection_cell.col = column;
4124       sheet->range.row0 = row;
4125       sheet->range.col0 = column;
4126       sheet->range.rowi = row;
4127       sheet->range.coli = column;
4128       sheet->state = GTK_SHEET_NORMAL;
4129       GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
4130       gtk_sheet_draw_active_cell (sheet);
4131       return;
4132     }
4133
4134   g_assert_not_reached ();
4135   gtk_sheet_activate_cell (sheet, sheet->active_cell.row,
4136                            sheet->active_cell.col);
4137 }
4138
4139 static gint
4140 gtk_sheet_button_release (GtkWidget * widget,
4141                           GdkEventButton * event)
4142 {
4143   gint y;
4144
4145   GtkSheet *sheet = GTK_SHEET (widget);
4146
4147   /* release on resize windows */
4148   if (GTK_SHEET_IN_XDRAG (sheet))
4149     {
4150       gint xpos = event->x;
4151       gint width;
4152       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_XDRAG);
4153       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
4154
4155       gdk_pointer_ungrab (event->time);
4156       draw_xor_vline (sheet);
4157
4158       width = new_column_width (sheet, sheet->drag_cell.col, &xpos);
4159
4160       gtk_sheet_set_column_width (sheet, sheet->drag_cell.col, width);
4161       return TRUE;
4162     }
4163
4164   if (GTK_SHEET_IN_YDRAG (sheet))
4165     {
4166       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_YDRAG);
4167       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
4168       gtk_widget_get_pointer (widget, NULL, &y);
4169       gdk_pointer_ungrab (event->time);
4170       draw_xor_hline (sheet);
4171
4172       gtk_sheet_set_row_height (sheet, sheet->drag_cell.row,
4173                                 new_row_height (sheet, sheet->drag_cell.row, &y));
4174       g_signal_emit_by_name (sheet->vadjustment, "value_changed");
4175       return TRUE;
4176     }
4177
4178
4179   if (GTK_SHEET_IN_DRAG (sheet))
4180     {
4181       GtkSheetRange old_range;
4182       draw_xor_rectangle (sheet, sheet->drag_range);
4183       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_DRAG);
4184       gdk_pointer_ungrab (event->time);
4185
4186       gtk_sheet_real_unselect_range (sheet, NULL);
4187
4188       sheet->active_cell.row = sheet->active_cell.row +
4189         (sheet->drag_range.row0 - sheet->range.row0);
4190       sheet->active_cell.col = sheet->active_cell.col +
4191         (sheet->drag_range.col0 - sheet->range.col0);
4192       sheet->selection_cell.row = sheet->selection_cell.row +
4193         (sheet->drag_range.row0 - sheet->range.row0);
4194       sheet->selection_cell.col = sheet->selection_cell.col +
4195         (sheet->drag_range.col0 - sheet->range.col0);
4196       old_range = sheet->range;
4197       sheet->range = sheet->drag_range;
4198       sheet->drag_range = old_range;
4199       g_signal_emit (sheet, sheet_signals[MOVE_RANGE], 0,
4200                      &sheet->drag_range, &sheet->range);
4201       gtk_sheet_select_range (sheet, &sheet->range);
4202     }
4203
4204   if (GTK_SHEET_IN_RESIZE (sheet))
4205     {
4206       GtkSheetRange old_range;
4207       draw_xor_rectangle (sheet, sheet->drag_range);
4208       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_RESIZE);
4209       gdk_pointer_ungrab (event->time);
4210
4211       gtk_sheet_real_unselect_range (sheet, NULL);
4212
4213       sheet->active_cell.row = sheet->active_cell.row +
4214         (sheet->drag_range.row0 - sheet->range.row0);
4215       sheet->active_cell.col = sheet->active_cell.col +
4216         (sheet->drag_range.col0 - sheet->range.col0);
4217       if (sheet->drag_range.row0 < sheet->range.row0)
4218         sheet->selection_cell.row = sheet->drag_range.row0;
4219       if (sheet->drag_range.rowi >= sheet->range.rowi)
4220         sheet->selection_cell.row = sheet->drag_range.rowi;
4221       if (sheet->drag_range.col0 < sheet->range.col0)
4222         sheet->selection_cell.col = sheet->drag_range.col0;
4223       if (sheet->drag_range.coli >= sheet->range.coli)
4224         sheet->selection_cell.col = sheet->drag_range.coli;
4225       old_range = sheet->range;
4226       sheet->range = sheet->drag_range;
4227       sheet->drag_range = old_range;
4228
4229       if (sheet->state == GTK_STATE_NORMAL) sheet->state = GTK_SHEET_RANGE_SELECTED;
4230       g_signal_emit (sheet, sheet_signals[RESIZE_RANGE], 0,
4231                      &sheet->drag_range, &sheet->range);
4232       gtk_sheet_select_range (sheet, &sheet->range);
4233     }
4234
4235   if (sheet->state == GTK_SHEET_NORMAL && GTK_SHEET_IN_SELECTION (sheet))
4236     {
4237       GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
4238       gdk_pointer_ungrab (event->time);
4239       gtk_sheet_activate_cell (sheet, sheet->active_cell.row,
4240                                sheet->active_cell.col);
4241     }
4242
4243   if (GTK_SHEET_IN_SELECTION)
4244     gdk_pointer_ungrab (event->time);
4245   gtk_grab_remove (GTK_WIDGET (sheet));
4246
4247   GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
4248
4249   return TRUE;
4250 }
4251
4252 \f
4253
4254
4255 /* Shamelessly lifted from gtktooltips */
4256 static gboolean
4257 gtk_sheet_subtitle_paint_window (GtkWidget *tip_window)
4258 {
4259   GtkRequisition req;
4260
4261   gtk_widget_size_request (tip_window, &req);
4262   gtk_paint_flat_box (tip_window->style, tip_window->window,
4263                       GTK_STATE_NORMAL, GTK_SHADOW_OUT,
4264                       NULL, GTK_WIDGET(tip_window), "tooltip",
4265                       0, 0, req.width, req.height);
4266
4267   return FALSE;
4268 }
4269
4270 static void
4271 destroy_hover_window (GtkSheetHoverTitle *h)
4272 {
4273   gtk_widget_destroy (h->window);
4274   g_free (h);
4275 }
4276
4277 static GtkSheetHoverTitle *
4278 create_hover_window (void)
4279 {
4280   GtkSheetHoverTitle *hw = g_malloc (sizeof (*hw));
4281
4282   hw->window = gtk_window_new (GTK_WINDOW_POPUP);
4283
4284 #if GTK_CHECK_VERSION (2, 9, 0)
4285   gtk_window_set_type_hint (GTK_WINDOW (hw->window),
4286                             GDK_WINDOW_TYPE_HINT_TOOLTIP);
4287 #endif
4288
4289   gtk_widget_set_app_paintable (hw->window, TRUE);
4290   gtk_window_set_resizable (GTK_WINDOW (hw->window), FALSE);
4291   gtk_widget_set_name (hw->window, "gtk-tooltips");
4292   gtk_container_set_border_width (GTK_CONTAINER (hw->window), 4);
4293
4294   g_signal_connect (hw->window,
4295                     "expose_event",
4296                     G_CALLBACK (gtk_sheet_subtitle_paint_window),
4297                     NULL);
4298
4299   hw->label = gtk_label_new (NULL);
4300
4301
4302   gtk_label_set_line_wrap (GTK_LABEL (hw->label), TRUE);
4303   gtk_misc_set_alignment (GTK_MISC (hw->label), 0.5, 0.5);
4304
4305   gtk_container_add (GTK_CONTAINER (hw->window), hw->label);
4306
4307   gtk_widget_show (hw->label);
4308
4309   g_signal_connect (hw->window,
4310                     "destroy",
4311                     G_CALLBACK (gtk_widget_destroyed),
4312                     &hw->window);
4313
4314   return hw;
4315 }
4316
4317 #define HOVER_WINDOW_Y_OFFSET 2
4318
4319 static void
4320 show_subtitle (GtkSheet *sheet, gint row, gint column, const gchar *subtitle)
4321 {
4322   gint x, y;
4323   gint px, py;
4324   gint width;
4325
4326   if ( ! subtitle )
4327     return;
4328
4329   gtk_label_set_text (GTK_LABEL (sheet->hover_window->label),
4330                       subtitle);
4331
4332
4333   sheet->hover_window->row = row;
4334   sheet->hover_window->column = column;
4335
4336   gdk_window_get_origin (GTK_WIDGET (sheet)->window, &x, &y);
4337
4338   gtk_widget_get_pointer (GTK_WIDGET (sheet), &px, &py);
4339
4340   gtk_widget_show (sheet->hover_window->window);
4341
4342   width = GTK_WIDGET (sheet->hover_window->label)->allocation.width;
4343
4344   if (row == -1 )
4345     {
4346       x += px;
4347       x -= width / 2;
4348       y += sheet->column_title_area.y;
4349       y += sheet->column_title_area.height;
4350       y += HOVER_WINDOW_Y_OFFSET;
4351     }
4352
4353   if ( column == -1 )
4354     {
4355       y += py;
4356       x += sheet->row_title_area.x;
4357       x += sheet->row_title_area.width * 2 / 3.0;
4358     }
4359
4360   gtk_window_move (GTK_WINDOW (sheet->hover_window->window),
4361                    x, y);
4362 }
4363
4364 static gboolean
4365 motion_timeout_callback (gpointer data)
4366 {
4367   GtkSheet *sheet = GTK_SHEET (data);
4368   gint x, y;
4369   gint row, column;
4370   gtk_widget_get_pointer (GTK_WIDGET (sheet), &x, &y);
4371
4372   if ( gtk_sheet_get_pixel_info (sheet, x, y, &row, &column) )
4373     {
4374       if (sheet->row_title_under)
4375         {
4376           GSheetRow *row_geo = sheet->row_geometry;
4377           gchar *text;
4378
4379           text = g_sheet_row_get_subtitle (row_geo, row);
4380
4381           show_subtitle (sheet, row, -1, text);
4382           g_free (text);
4383         }
4384
4385       if (sheet->column_title_under)
4386         {
4387           GSheetColumn *col_geo = sheet->column_geometry;
4388           gchar *text;
4389
4390           text = g_sheet_column_get_subtitle (col_geo, column);
4391
4392           show_subtitle (sheet, -1, column, text );
4393
4394           g_free (text);
4395         }
4396     }
4397
4398   return FALSE;
4399 }
4400
4401 static gboolean
4402 gtk_sheet_motion (GtkWidget *widget,  GdkEventMotion *event)
4403 {
4404   GtkSheet *sheet;
4405   GdkModifierType mods;
4406   GdkCursorType new_cursor;
4407   gint x, y;
4408   gint row, column;
4409
4410   g_return_val_if_fail (widget != NULL, FALSE);
4411   g_return_val_if_fail (GTK_IS_SHEET (widget), FALSE);
4412   g_return_val_if_fail (event != NULL, FALSE);
4413
4414   sheet = GTK_SHEET (widget);
4415
4416   /* selections on the sheet */
4417   x = event->x;
4418   y = event->y;
4419
4420   if (!GTK_WIDGET_VISIBLE (sheet->hover_window->window))
4421     {
4422       if ( sheet->motion_timer > 0 )
4423         g_source_remove (sheet->motion_timer);
4424       sheet->motion_timer =
4425         g_timeout_add (TIMEOUT_HOVER, motion_timeout_callback, sheet);
4426     }
4427   else
4428     {
4429       gint row, column;
4430       gint wx, wy;
4431       gtk_widget_get_pointer (widget, &wx, &wy);
4432
4433       if ( gtk_sheet_get_pixel_info (sheet, wx, wy, &row, &column) )
4434         {
4435           if ( row != sheet->hover_window->row || column != sheet->hover_window->column)
4436             {
4437               gtk_widget_hide (sheet->hover_window->window);
4438             }
4439         }
4440     }
4441
4442   if (event->window == sheet->column_title_window &&
4443       gtk_sheet_columns_resizable (sheet))
4444     {
4445       if (!GTK_SHEET_IN_SELECTION (sheet) &&
4446           on_column_boundary (sheet, x, &column))
4447         {
4448           new_cursor = GDK_SB_H_DOUBLE_ARROW;
4449           if (new_cursor != sheet->cursor_drag->type)
4450             {
4451               gdk_cursor_unref (sheet->cursor_drag);
4452               sheet->cursor_drag = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW);
4453               gdk_window_set_cursor (sheet->column_title_window,
4454                                      sheet->cursor_drag);
4455             }
4456         }
4457       else
4458         {
4459           new_cursor = GDK_TOP_LEFT_ARROW;
4460           if (!GTK_SHEET_IN_XDRAG (sheet) &&
4461               new_cursor != sheet->cursor_drag->type)
4462             {
4463               gdk_cursor_unref (sheet->cursor_drag);
4464               sheet->cursor_drag = gdk_cursor_new (GDK_TOP_LEFT_ARROW);
4465               gdk_window_set_cursor (sheet->column_title_window,
4466                                      sheet->cursor_drag);
4467             }
4468         }
4469     }
4470
4471   if (event->window == sheet->row_title_window &&
4472       gtk_sheet_rows_resizable (sheet))
4473     {
4474       if (!GTK_SHEET_IN_SELECTION (sheet) && POSSIBLE_YDRAG (sheet, y, &column))
4475         {
4476           new_cursor = GDK_SB_V_DOUBLE_ARROW;
4477           if (new_cursor != sheet->cursor_drag->type)
4478             {
4479               gdk_cursor_unref (sheet->cursor_drag);
4480               sheet->cursor_drag = gdk_cursor_new (GDK_SB_V_DOUBLE_ARROW);
4481               gdk_window_set_cursor (sheet->row_title_window, sheet->cursor_drag);
4482             }
4483         }
4484       else
4485         {
4486           new_cursor = GDK_TOP_LEFT_ARROW;
4487           if (!GTK_SHEET_IN_YDRAG (sheet) &&
4488               new_cursor != sheet->cursor_drag->type)
4489             {
4490               gdk_cursor_unref (sheet->cursor_drag);
4491               sheet->cursor_drag = gdk_cursor_new (GDK_TOP_LEFT_ARROW);
4492               gdk_window_set_cursor (sheet->row_title_window, sheet->cursor_drag);
4493             }
4494         }
4495     }
4496
4497   new_cursor = GDK_PLUS;
4498   if ( event->window == sheet->sheet_window &&
4499        !POSSIBLE_DRAG (sheet, x, y, &row, &column) &&
4500        !GTK_SHEET_IN_DRAG (sheet) &&
4501        !POSSIBLE_RESIZE (sheet, x, y, &row, &column) &&
4502        !GTK_SHEET_IN_RESIZE (sheet) &&
4503        new_cursor != sheet->cursor_drag->type)
4504     {
4505       gdk_cursor_unref (sheet->cursor_drag);
4506       sheet->cursor_drag = gdk_cursor_new (GDK_PLUS);
4507       gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
4508     }
4509
4510   new_cursor = GDK_TOP_LEFT_ARROW;
4511   if ( event->window == sheet->sheet_window &&
4512        ! (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
4513           GTK_SHEET_IN_RESIZE (sheet)) &&
4514        (POSSIBLE_DRAG (sheet, x, y, &row, &column) ||
4515         GTK_SHEET_IN_DRAG (sheet)) &&
4516        new_cursor != sheet->cursor_drag->type)
4517     {
4518       gdk_cursor_unref (sheet->cursor_drag);
4519       sheet->cursor_drag = gdk_cursor_new (GDK_TOP_LEFT_ARROW);
4520       gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
4521     }
4522
4523   new_cursor = GDK_SIZING;
4524   if ( event->window == sheet->sheet_window &&
4525        sheet->selection_mode != GTK_SELECTION_NONE &&
4526        !GTK_SHEET_IN_DRAG (sheet) &&
4527        (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
4528         GTK_SHEET_IN_RESIZE (sheet)) &&
4529        new_cursor != sheet->cursor_drag->type)
4530     {
4531       gdk_cursor_unref (sheet->cursor_drag);
4532       sheet->cursor_drag = gdk_cursor_new (GDK_SIZING);
4533       gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
4534     }
4535
4536
4537   gdk_window_get_pointer (widget->window, &x, &y, &mods);
4538   if (! (mods & GDK_BUTTON1_MASK)) return FALSE;
4539
4540   if (GTK_SHEET_IN_XDRAG (sheet))
4541     {
4542         x = event->x;
4543
4544       new_column_width (sheet, sheet->drag_cell.col, &x);
4545 #if 0
4546       if (x != sheet->x_drag)
4547         {
4548           draw_xor_vline (sheet);
4549           sheet->x_drag = x;
4550           draw_xor_vline (sheet);
4551         }
4552 #endif
4553       return TRUE;
4554     }
4555
4556   if (GTK_SHEET_IN_YDRAG (sheet))
4557     {
4558       if (event->is_hint || event->window != widget->window)
4559         gtk_widget_get_pointer (widget, NULL, &y);
4560       else
4561         y = event->y;
4562
4563       new_row_height (sheet, sheet->drag_cell.row, &y);
4564       if (y != sheet->y_drag)
4565         {
4566           draw_xor_hline (sheet);
4567           sheet->y_drag = y;
4568           draw_xor_hline (sheet);
4569         }
4570       return TRUE;
4571     }
4572
4573   if (GTK_SHEET_IN_DRAG (sheet))
4574     {
4575       GtkSheetRange aux;
4576       column = COLUMN_FROM_XPIXEL (sheet, x)- sheet->drag_cell.col;
4577       row = yyy_row_ypixel_to_row (sheet, y) - sheet->drag_cell.row;
4578       if (sheet->state == GTK_SHEET_COLUMN_SELECTED) row = 0;
4579       if (sheet->state == GTK_SHEET_ROW_SELECTED) column = 0;
4580       sheet->x_drag = x;
4581       sheet->y_drag = y;
4582       aux = sheet->range;
4583       if (aux.row0 + row >= 0 && aux.rowi + row < g_sheet_row_get_row_count (sheet->row_geometry) &&
4584           aux.col0 + column >= 0 && aux.coli + column < g_sheet_column_get_column_count (sheet->column_geometry))
4585         {
4586           aux = sheet->drag_range;
4587           sheet->drag_range.row0 = sheet->range.row0 + row;
4588           sheet->drag_range.col0 = sheet->range.col0 + column;
4589           sheet->drag_range.rowi = sheet->range.rowi + row;
4590           sheet->drag_range.coli = sheet->range.coli + column;
4591           if (aux.row0 != sheet->drag_range.row0 ||
4592               aux.col0 != sheet->drag_range.col0)
4593             {
4594               draw_xor_rectangle (sheet, aux);
4595               draw_xor_rectangle (sheet, sheet->drag_range);
4596             }
4597         }
4598       return TRUE;
4599     }
4600
4601   if (GTK_SHEET_IN_RESIZE (sheet))
4602     {
4603       GtkSheetRange aux;
4604       gint v_h, current_col, current_row, col_threshold, row_threshold;
4605       v_h = 1;
4606       if (abs (x - COLUMN_LEFT_XPIXEL (sheet, sheet->drag_cell.col)) >
4607           abs (y - g_sheet_row_start_pixel (sheet->row_geometry, sheet->drag_cell.row))) v_h = 2;
4608
4609       current_col = COLUMN_FROM_XPIXEL (sheet, x);
4610       current_row = yyy_row_ypixel_to_row (sheet, y);
4611       column = current_col - sheet->drag_cell.col;
4612       row = current_row - sheet->drag_cell.row;
4613
4614       /*use half of column width resp. row height as threshold to
4615         expand selection*/
4616       col_threshold = COLUMN_LEFT_XPIXEL (sheet, current_col) +
4617         g_sheet_column_get_width (sheet->column_geometry, current_col) / 2;
4618       if (column > 0)
4619         {
4620           if (x < col_threshold)
4621             column -= 1;
4622         }
4623       else if (column < 0)
4624         {
4625           if (x > col_threshold)
4626             column +=1;
4627         }
4628       row_threshold = g_sheet_row_start_pixel (sheet->row_geometry, current_row) +
4629         g_sheet_row_get_height (sheet->row_geometry, current_row)/2;
4630       if (row > 0)
4631         {
4632           if (y < row_threshold)
4633             row -= 1;
4634         }
4635       else if (row < 0)
4636         {
4637           if (y > row_threshold)
4638             row +=1;
4639         }
4640
4641       if (sheet->state == GTK_SHEET_COLUMN_SELECTED) row = 0;
4642       if (sheet->state == GTK_SHEET_ROW_SELECTED) column = 0;
4643       sheet->x_drag = x;
4644       sheet->y_drag = y;
4645       aux = sheet->range;
4646
4647       if (v_h == 1)
4648         column = 0;
4649       else
4650         row = 0;
4651
4652       if (aux.row0 + row >= 0 && aux.rowi + row < g_sheet_row_get_row_count (sheet->row_geometry) &&
4653           aux.col0 + column >= 0 && aux.coli + column < g_sheet_column_get_column_count (sheet->column_geometry))
4654         {
4655           aux = sheet->drag_range;
4656           sheet->drag_range = sheet->range;
4657
4658           if (row < 0) sheet->drag_range.row0 = sheet->range.row0 + row;
4659           if (row > 0) sheet->drag_range.rowi = sheet->range.rowi + row;
4660           if (column < 0) sheet->drag_range.col0 = sheet->range.col0 + column;
4661           if (column > 0) sheet->drag_range.coli = sheet->range.coli + column;
4662
4663           if (aux.row0 != sheet->drag_range.row0 ||
4664               aux.rowi != sheet->drag_range.rowi ||
4665               aux.col0 != sheet->drag_range.col0 ||
4666               aux.coli != sheet->drag_range.coli)
4667             {
4668               draw_xor_rectangle (sheet, aux);
4669               draw_xor_rectangle (sheet, sheet->drag_range);
4670             }
4671         }
4672       return TRUE;
4673     }
4674
4675   gtk_sheet_get_pixel_info (sheet, x, y, &row, &column);
4676
4677   if (sheet->state == GTK_SHEET_NORMAL && row == sheet->active_cell.row &&
4678       column == sheet->active_cell.col) return TRUE;
4679
4680   if (GTK_SHEET_IN_SELECTION (sheet) && mods&GDK_BUTTON1_MASK)
4681     gtk_sheet_extend_selection (sheet, row, column);
4682
4683   return TRUE;
4684 }
4685
4686 static gboolean
4687 gtk_sheet_crossing_notify (GtkWidget *widget,
4688                            GdkEventCrossing *event)
4689 {
4690   GtkSheet *sheet = GTK_SHEET (widget);
4691
4692   if (event->window == sheet->column_title_window)
4693     sheet->column_title_under = event->type == GDK_ENTER_NOTIFY;
4694   else if (event->window == sheet->row_title_window)
4695     sheet->row_title_under = event->type == GDK_ENTER_NOTIFY;
4696
4697   return TRUE;
4698 }
4699
4700
4701 static gboolean
4702 gtk_sheet_move_query (GtkSheet *sheet, gint row, gint column)
4703 {
4704   guint height, width;
4705   gint new_row = row;
4706   gint new_col = column;
4707
4708   gint row_move = FALSE;
4709   gint column_move = FALSE;
4710   gfloat row_align = -1.0;
4711   gfloat col_align = -1.0;
4712
4713   height = sheet->sheet_window_height;
4714   width = sheet->sheet_window_width;
4715
4716   if (row >= MAX_VISIBLE_ROW (sheet) && sheet->state != GTK_SHEET_COLUMN_SELECTED)
4717     {
4718       row_align = 1.;
4719       new_row = MIN (g_sheet_row_get_row_count (sheet->row_geometry) - 1, row + 1);
4720       row_move = TRUE;
4721       if (MAX_VISIBLE_ROW (sheet) == g_sheet_row_get_row_count (sheet->row_geometry) - 1 &&
4722           g_sheet_row_start_pixel (sheet->row_geometry, g_sheet_row_get_row_count (sheet->row_geometry) - 1) +
4723           g_sheet_row_get_height (sheet->row_geometry, g_sheet_row_get_row_count (sheet->row_geometry) - 1) < height)
4724         {
4725           row_move = FALSE;
4726           row_align = -1.;
4727         }
4728     }
4729   if (row < MIN_VISIBLE_ROW (sheet) && sheet->state != GTK_SHEET_COLUMN_SELECTED)
4730     {
4731       row_align= 0.;
4732       row_move = TRUE;
4733     }
4734   if (column >= MAX_VISIBLE_COLUMN (sheet) && sheet->state != GTK_SHEET_ROW_SELECTED)
4735     {
4736       col_align = 1.;
4737       new_col = MIN (g_sheet_column_get_column_count (sheet->column_geometry) - 1, column + 1);
4738       column_move = TRUE;
4739       if (MAX_VISIBLE_COLUMN (sheet) == (g_sheet_column_get_column_count (sheet->column_geometry) - 1) &&
4740           COLUMN_LEFT_XPIXEL (sheet, g_sheet_column_get_column_count (sheet->column_geometry) - 1) +
4741           g_sheet_column_get_width (sheet->column_geometry, g_sheet_column_get_column_count (sheet->column_geometry) - 1) < width)
4742         {
4743           column_move = FALSE;
4744           col_align = -1.;
4745         }
4746     }
4747   if (column < MIN_VISIBLE_COLUMN (sheet) && sheet->state != GTK_SHEET_ROW_SELECTED)
4748     {
4749       col_align = 0.0;
4750       column_move = TRUE;
4751     }
4752
4753   if (row_move || column_move)
4754     {
4755       gtk_sheet_moveto (sheet, new_row, new_col, row_align, col_align);
4756     }
4757
4758   return (row_move || column_move);
4759 }
4760
4761 static void
4762 gtk_sheet_extend_selection (GtkSheet *sheet, gint row, gint column)
4763 {
4764   GtkSheetRange range;
4765   gint state;
4766   gint r, c;
4767
4768   if (row == sheet->selection_cell.row && column == sheet->selection_cell.col)
4769     return;
4770
4771   if (sheet->selection_mode == GTK_SELECTION_SINGLE) return;
4772
4773   gtk_sheet_move_query (sheet, row, column);
4774   gtk_widget_grab_focus (GTK_WIDGET (sheet));
4775
4776   if (GTK_SHEET_IN_DRAG (sheet)) return;
4777
4778   state = sheet->state;
4779
4780   switch (sheet->state)
4781     {
4782     case GTK_SHEET_ROW_SELECTED:
4783       column = g_sheet_column_get_column_count (sheet->column_geometry) - 1;
4784       break;
4785     case GTK_SHEET_COLUMN_SELECTED:
4786       row = g_sheet_row_get_row_count (sheet->row_geometry) - 1;
4787       break;
4788     case GTK_SHEET_NORMAL:
4789       sheet->state = GTK_SHEET_RANGE_SELECTED;
4790       r = sheet->active_cell.row;
4791       c = sheet->active_cell.col;
4792       sheet->range.col0 = c;
4793       sheet->range.row0 = r;
4794       sheet->range.coli = c;
4795       sheet->range.rowi = r;
4796       gtk_sheet_range_draw_selection (sheet, sheet->range);
4797     case GTK_SHEET_RANGE_SELECTED:
4798       sheet->state = GTK_SHEET_RANGE_SELECTED;
4799     }
4800
4801   sheet->selection_cell.row = row;
4802   sheet->selection_cell.col = column;
4803
4804   range.col0 = MIN (column, sheet->active_cell.col);
4805   range.coli = MAX (column, sheet->active_cell.col);
4806   range.row0 = MIN (row, sheet->active_cell.row);
4807   range.rowi = MAX (row, sheet->active_cell.row);
4808
4809   if (range.row0 != sheet->range.row0 || range.rowi != sheet->range.rowi ||
4810       range.col0 != sheet->range.col0 || range.coli != sheet->range.coli ||
4811       state == GTK_SHEET_NORMAL)
4812     gtk_sheet_real_select_range (sheet, &range);
4813
4814 }
4815
4816 static gint
4817 gtk_sheet_entry_key_press (GtkWidget *widget,
4818                            GdkEventKey *key)
4819 {
4820   gboolean focus;
4821   g_signal_emit_by_name (widget, "key_press_event", key, &focus);
4822   return focus;
4823 }
4824
4825 static gint
4826 gtk_sheet_key_press (GtkWidget *widget,
4827                      GdkEventKey *key)
4828 {
4829   GtkSheet *sheet;
4830   gint row, col;
4831   gint state;
4832   gboolean extend_selection = FALSE;
4833   gboolean force_move = FALSE;
4834   gboolean in_selection = FALSE;
4835   gboolean veto = TRUE;
4836   gint scroll = 1;
4837
4838   sheet = GTK_SHEET (widget);
4839
4840   if (key->state & GDK_CONTROL_MASK || key->keyval == GDK_Control_L ||
4841       key->keyval == GDK_Control_R) return FALSE;
4842
4843   extend_selection = (key->state & GDK_SHIFT_MASK) || key->keyval == GDK_Shift_L
4844     || key->keyval == GDK_Shift_R;
4845
4846   state = sheet->state;
4847   in_selection = GTK_SHEET_IN_SELECTION (sheet);
4848   GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
4849
4850   switch (key->keyval)
4851     {
4852     case GDK_Return: case GDK_KP_Enter:
4853       if (sheet->state == GTK_SHEET_NORMAL &&
4854           !GTK_SHEET_IN_SELECTION (sheet))
4855         g_signal_stop_emission_by_name (gtk_sheet_get_entry (sheet),
4856                                         "key-press-event");
4857       row = sheet->active_cell.row;
4858       col = sheet->active_cell.col;
4859       if (sheet->state == GTK_SHEET_COLUMN_SELECTED)
4860         row = MIN_VISIBLE_ROW (sheet)- 1;
4861       if (sheet->state == GTK_SHEET_ROW_SELECTED)
4862         col = MIN_VISIBLE_COLUMN (sheet);
4863       if (row < g_sheet_row_get_row_count (sheet->row_geometry) - 1)
4864         {
4865           row = row + scroll;
4866           while (!g_sheet_row_get_visibility (sheet->row_geometry, row) && row < g_sheet_row_get_row_count (sheet->row_geometry) - 1)
4867             row++;
4868         }
4869       gtk_sheet_click_cell (sheet, row, col, &veto);
4870       extend_selection = FALSE;
4871       break;
4872     case GDK_ISO_Left_Tab:
4873       row = sheet->active_cell.row;
4874       col = sheet->active_cell.col;
4875       if (sheet->state == GTK_SHEET_ROW_SELECTED)
4876         col = MIN_VISIBLE_COLUMN (sheet)- 1;
4877       if (sheet->state == GTK_SHEET_COLUMN_SELECTED)
4878         row = MIN_VISIBLE_ROW (sheet);
4879       if (col > 0)
4880         {
4881           col = col - scroll;
4882           while (! g_sheet_column_get_visibility (sheet->column_geometry, col) && col > 0) col--;
4883           col = MAX (0, col);
4884         }
4885       gtk_sheet_click_cell (sheet, row, col, &veto);
4886       extend_selection = FALSE;
4887       break;
4888     case GDK_Tab:
4889       row = sheet->active_cell.row;
4890       col = sheet->active_cell.col;
4891       if (sheet->state == GTK_SHEET_ROW_SELECTED)
4892         col = MIN_VISIBLE_COLUMN (sheet)- 1;
4893       if (sheet->state == GTK_SHEET_COLUMN_SELECTED)
4894         row = MIN_VISIBLE_ROW (sheet);
4895       if (col < g_sheet_column_get_column_count (sheet->column_geometry) - 1)
4896         {
4897           col = col + scroll;
4898           while (! g_sheet_column_get_visibility (sheet->column_geometry, col) &&
4899                  col < g_sheet_column_get_column_count (sheet->column_geometry) - 1)
4900             col++;
4901         }
4902       gtk_sheet_click_cell (sheet, row, col, &veto);
4903       extend_selection = FALSE;
4904       break;
4905     case GDK_Page_Up:
4906       scroll = MAX_VISIBLE_ROW (sheet)- MIN_VISIBLE_ROW (sheet)+1;
4907     case GDK_Up:
4908       if (extend_selection)
4909         {
4910           if (state == GTK_STATE_NORMAL)
4911             {
4912               row = sheet->active_cell.row;
4913               col = sheet->active_cell.col;
4914               gtk_sheet_click_cell (sheet, row, col, &veto);
4915               if (!veto) break;
4916             }
4917           if (sheet->selection_cell.row > 0)
4918             {
4919               row = sheet->selection_cell.row - scroll;
4920               while (!g_sheet_row_get_visibility (sheet->row_geometry, row) && row > 0) row--;
4921               row = MAX (0, row);
4922               gtk_sheet_extend_selection (sheet, row, sheet->selection_cell.col);
4923             }
4924           return TRUE;
4925         }
4926       col = sheet->active_cell.col;
4927       row = sheet->active_cell.row;
4928       if (state == GTK_SHEET_COLUMN_SELECTED)
4929         row = MIN_VISIBLE_ROW (sheet);
4930       if (state == GTK_SHEET_ROW_SELECTED)
4931         col = MIN_VISIBLE_COLUMN (sheet);
4932       row = row - scroll;
4933       while (!g_sheet_row_get_visibility (sheet->row_geometry, row) && row > 0) row--;
4934       row = MAX (0, row);
4935       gtk_sheet_click_cell (sheet, row, col, &veto);
4936       extend_selection = FALSE;
4937       break;
4938     case GDK_Page_Down:
4939       scroll = MAX_VISIBLE_ROW (sheet)- MIN_VISIBLE_ROW (sheet)+1;
4940     case GDK_Down:
4941       if (extend_selection)
4942         {
4943           if (state == GTK_STATE_NORMAL)
4944             {
4945               row = sheet->active_cell.row;
4946               col = sheet->active_cell.col;
4947               gtk_sheet_click_cell (sheet, row, col, &veto);
4948               if (!veto) break;
4949             }
4950           if (sheet->selection_cell.row < g_sheet_row_get_row_count (sheet->row_geometry) - 1)
4951             {
4952               row = sheet->selection_cell.row + scroll;
4953               while (!g_sheet_row_get_visibility (sheet->row_geometry, row) && row < g_sheet_row_get_row_count (sheet->row_geometry) - 1) row++;
4954               row = MIN (g_sheet_row_get_row_count (sheet->row_geometry) - 1, row);
4955               gtk_sheet_extend_selection (sheet, row, sheet->selection_cell.col);
4956             }
4957           return TRUE;
4958         }
4959       col = sheet->active_cell.col;
4960       row = sheet->active_cell.row;
4961       if (sheet->active_cell.row < g_sheet_row_get_row_count (sheet->row_geometry) - 1)
4962         {
4963           if (state == GTK_SHEET_COLUMN_SELECTED)
4964             row = MIN_VISIBLE_ROW (sheet)- 1;
4965           if (state == GTK_SHEET_ROW_SELECTED)
4966             col = MIN_VISIBLE_COLUMN (sheet);
4967           row = row + scroll;
4968           while (!g_sheet_row_get_visibility (sheet->row_geometry, row) && row < g_sheet_row_get_row_count (sheet->row_geometry) - 1) row++;
4969           row = MIN (g_sheet_row_get_row_count (sheet->row_geometry) - 1, row);
4970         }
4971       gtk_sheet_click_cell (sheet, row, col, &veto);
4972       extend_selection = FALSE;
4973       break;
4974     case GDK_Right:
4975       if (extend_selection)
4976         {
4977           if (state == GTK_STATE_NORMAL)
4978             {
4979               row = sheet->active_cell.row;
4980               col = sheet->active_cell.col;
4981               gtk_sheet_click_cell (sheet, row, col, &veto);
4982               if (!veto) break;
4983             }
4984           if (sheet->selection_cell.col < g_sheet_column_get_column_count (sheet->column_geometry) - 1)
4985             {
4986               col = sheet->selection_cell.col + 1;
4987               while (! g_sheet_column_get_visibility (sheet->column_geometry, col) && col < g_sheet_column_get_column_count (sheet->column_geometry) - 1)
4988                 col++;
4989               gtk_sheet_extend_selection (sheet, sheet->selection_cell.row, col);
4990             }
4991           return TRUE;
4992         }
4993       col = sheet->active_cell.col;
4994       row = sheet->active_cell.row;
4995       if (sheet->active_cell.col < g_sheet_column_get_column_count (sheet->column_geometry) - 1)
4996         {
4997           col ++;
4998           if (state == GTK_SHEET_ROW_SELECTED)
4999             col = MIN_VISIBLE_COLUMN (sheet)- 1;
5000           if (state == GTK_SHEET_COLUMN_SELECTED)
5001             row = MIN_VISIBLE_ROW (sheet);
5002           while (! g_sheet_column_get_visibility (sheet->column_geometry, col) && col < g_sheet_column_get_column_count (sheet->column_geometry) - 1) col++;
5003           if (strlen (gtk_entry_get_text (GTK_ENTRY (gtk_sheet_get_entry (sheet)))) == 0
5004               || force_move)
5005             {
5006               gtk_sheet_click_cell (sheet, row, col, &veto);
5007             }
5008           else
5009             return FALSE;
5010         }
5011       extend_selection = FALSE;
5012       break;
5013     case GDK_Left:
5014       if (extend_selection)
5015         {
5016           if (state == GTK_STATE_NORMAL)
5017             {
5018               row = sheet->active_cell.row;
5019               col = sheet->active_cell.col;
5020               gtk_sheet_click_cell (sheet, row, col, &veto);
5021               if (!veto) break;
5022             }
5023           if (sheet->selection_cell.col > 0)
5024             {
5025               col = sheet->selection_cell.col - 1;
5026               while (! g_sheet_column_get_visibility (sheet->column_geometry, col) && col > 0) col--;
5027               gtk_sheet_extend_selection (sheet, sheet->selection_cell.row, col);
5028             }
5029           return TRUE;
5030         }
5031       col = sheet->active_cell.col - 1;
5032       row = sheet->active_cell.row;
5033       if (state == GTK_SHEET_ROW_SELECTED)
5034         col = MIN_VISIBLE_COLUMN (sheet)- 1;
5035       if (state == GTK_SHEET_COLUMN_SELECTED)
5036         row = MIN_VISIBLE_ROW (sheet);
5037       while (! g_sheet_column_get_visibility (sheet->column_geometry, col) && col > 0) col--;
5038       col = MAX (0, col);
5039
5040       if (strlen (gtk_entry_get_text (GTK_ENTRY (gtk_sheet_get_entry (sheet)))) == 0
5041           || force_move)
5042         {
5043           gtk_sheet_click_cell (sheet, row, col, &veto);
5044         }
5045       else
5046         return FALSE;
5047       extend_selection = FALSE;
5048       break;
5049     case GDK_Home:
5050       row = 0;
5051       while (!g_sheet_row_get_visibility (sheet->row_geometry, row) && row < g_sheet_row_get_row_count (sheet->row_geometry) - 1) row++;
5052       gtk_sheet_click_cell (sheet, row, sheet->active_cell.col, &veto);
5053       extend_selection = FALSE;
5054       break;
5055     case GDK_End:
5056       row = g_sheet_row_get_row_count (sheet->row_geometry) - 1;
5057       while (!g_sheet_row_get_visibility (sheet->row_geometry, row) && row > 0) row--;
5058       gtk_sheet_click_cell (sheet, row, sheet->active_cell.col, &veto);
5059       extend_selection = FALSE;
5060       break;
5061     default:
5062       if (in_selection)
5063         {
5064           GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
5065           if (extend_selection) return TRUE;
5066         }
5067       if (state == GTK_SHEET_ROW_SELECTED)
5068         sheet->active_cell.col = MIN_VISIBLE_COLUMN (sheet);
5069       if (state == GTK_SHEET_COLUMN_SELECTED)
5070         sheet->active_cell.row = MIN_VISIBLE_ROW (sheet);
5071       return FALSE;
5072     }
5073
5074   if (extend_selection) return TRUE;
5075
5076   gtk_sheet_activate_cell (sheet, sheet->active_cell.row,
5077                            sheet->active_cell.col);
5078
5079   return TRUE;
5080 }
5081
5082 static void
5083 gtk_sheet_size_request (GtkWidget * widget,
5084                         GtkRequisition * requisition)
5085 {
5086   GtkSheet *sheet;
5087
5088   g_return_if_fail (widget != NULL);
5089   g_return_if_fail (GTK_IS_SHEET (widget));
5090   g_return_if_fail (requisition != NULL);
5091
5092   sheet = GTK_SHEET (widget);
5093
5094   requisition->width = 3*DEFAULT_COLUMN_WIDTH;
5095   requisition->height = 3*DEFAULT_ROW_HEIGHT (widget);
5096
5097   /* compute the size of the column title area */
5098   if (sheet->column_titles_visible)
5099     requisition->height += sheet->column_title_area.height;
5100
5101   /* compute the size of the row title area */
5102   if (sheet->row_titles_visible)
5103     requisition->width += sheet->row_title_area.width;
5104 }
5105
5106
5107 static void
5108 gtk_sheet_size_allocate (GtkWidget * widget,
5109                          GtkAllocation * allocation)
5110 {
5111   GtkSheet *sheet;
5112   GtkAllocation sheet_allocation;
5113   gint border_width;
5114
5115   g_return_if_fail (widget != NULL);
5116   g_return_if_fail (GTK_IS_SHEET (widget));
5117   g_return_if_fail (allocation != NULL);
5118
5119   sheet = GTK_SHEET (widget);
5120   widget->allocation = *allocation;
5121   border_width = GTK_CONTAINER (widget)->border_width;
5122
5123   if (GTK_WIDGET_REALIZED (widget))
5124     gdk_window_move_resize (widget->window,
5125                             allocation->x + border_width,
5126                             allocation->y + border_width,
5127                             allocation->width - 2 * border_width,
5128                             allocation->height - 2 * border_width);
5129
5130   /* use internal allocation structure for all the math
5131    * because it's easier than always subtracting the container
5132    * border width */
5133   sheet->internal_allocation.x = 0;
5134   sheet->internal_allocation.y = 0;
5135   sheet->internal_allocation.width = allocation->width - 2 * border_width;
5136   sheet->internal_allocation.height = allocation->height - 2 * border_width;
5137
5138   sheet_allocation.x = 0;
5139   sheet_allocation.y = 0;
5140   sheet_allocation.width = allocation->width - 2 * border_width;
5141   sheet_allocation.height = allocation->height - 2 * border_width;
5142
5143   sheet->sheet_window_width = sheet_allocation.width;
5144   sheet->sheet_window_height = sheet_allocation.height;
5145
5146   if (GTK_WIDGET_REALIZED (widget))
5147     gdk_window_move_resize (sheet->sheet_window,
5148                             sheet_allocation.x,
5149                             sheet_allocation.y,
5150                             sheet_allocation.width,
5151                             sheet_allocation.height);
5152
5153   /* position the window which holds the column title buttons */
5154   sheet->column_title_area.x = 0;
5155   sheet->column_title_area.y = 0;
5156
5157   if (sheet->row_titles_visible)
5158     {
5159     sheet->column_title_area.x = sheet->row_title_area.width;
5160       sheet->sheet_window_width -= sheet->row_title_area.width;
5161     }
5162
5163   sheet->column_title_area.width = sheet_allocation.width ;
5164
5165
5166   if (GTK_WIDGET_REALIZED (widget) && sheet->column_titles_visible)
5167     gdk_window_move_resize (sheet->column_title_window,
5168                             sheet->column_title_area.x,
5169                             sheet->column_title_area.y,
5170                             sheet->column_title_area.width,
5171                             sheet->column_title_area.height);
5172
5173
5174   /* column button allocation */
5175   size_allocate_column_title_buttons (sheet);
5176
5177   /* position the window which holds the row title buttons */
5178   sheet->row_title_area.x = 0;
5179   sheet->row_title_area.y = 0;
5180   if (sheet->column_titles_visible)
5181     {
5182     sheet->row_title_area.y = sheet->column_title_area.height;
5183       sheet->sheet_window_height -= sheet->column_title_area.height;
5184     }
5185
5186   sheet->row_title_area.height = sheet_allocation.height -
5187     sheet->row_title_area.y;
5188
5189   if (GTK_WIDGET_REALIZED (widget) && sheet->row_titles_visible)
5190     gdk_window_move_resize (sheet->row_title_window,
5191                             sheet->row_title_area.x,
5192                             sheet->row_title_area.y,
5193                             sheet->row_title_area.width,
5194                             sheet->row_title_area.height);
5195
5196
5197   /* row button allocation */
5198   size_allocate_row_title_buttons (sheet);
5199   size_allocate_column_title_buttons (sheet);
5200
5201   /* re - scale backing pixmap */
5202   gtk_sheet_make_backing_pixmap (sheet);
5203
5204   /* set the scrollbars adjustments */
5205   adjust_scrollbars (sheet);
5206 }
5207
5208 static void
5209 size_allocate_column_title_buttons (GtkSheet * sheet)
5210 {
5211   gint i;
5212   gint x, width;
5213
5214   if (!sheet->column_titles_visible) return;
5215   if (!GTK_WIDGET_REALIZED (sheet))
5216     return;
5217
5218
5219   width = sheet->sheet_window_width;
5220   x = 0;
5221
5222   if (sheet->row_titles_visible)
5223     {
5224       x = sheet->row_title_area.width;
5225     }
5226
5227   if (sheet->column_title_area.width != width || sheet->column_title_area.x != x)
5228     {
5229       sheet->column_title_area.width = width;
5230       sheet->column_title_area.x = x;
5231       gdk_window_move_resize (sheet->column_title_window,
5232                               sheet->column_title_area.x,
5233                               sheet->column_title_area.y,
5234                               sheet->column_title_area.width,
5235                               sheet->column_title_area.height);
5236     }
5237
5238   if (MAX_VISIBLE_COLUMN (sheet) == g_sheet_column_get_column_count (sheet->column_geometry) - 1)
5239     gdk_window_clear_area (sheet->column_title_window,
5240                            0, 0,
5241                            sheet->column_title_area.width,
5242                            sheet->column_title_area.height);
5243
5244   if (!GTK_WIDGET_DRAWABLE (sheet)) return;
5245
5246   size_allocate_global_button (sheet);
5247
5248   for (i = MIN_VISIBLE_COLUMN (sheet); i <= MAX_VISIBLE_COLUMN (sheet); i++)
5249     gtk_sheet_column_title_button_draw (sheet, i);
5250 }
5251
5252 static void
5253 size_allocate_row_title_buttons (GtkSheet * sheet)
5254 {
5255   gint i;
5256   gint y, height;
5257
5258   if (!sheet->row_titles_visible) return;
5259   if (!GTK_WIDGET_REALIZED (sheet))
5260     return;
5261
5262   height = sheet->sheet_window_height;
5263   y = 0;
5264
5265   if (sheet->column_titles_visible)
5266     {
5267       y = sheet->column_title_area.height;
5268     }
5269
5270   if (sheet->row_title_area.height != height || sheet->row_title_area.y != y)
5271     {
5272       sheet->row_title_area.y = y;
5273       sheet->row_title_area.height = height;
5274       gdk_window_move_resize (sheet->row_title_window,
5275                               sheet->row_title_area.x,
5276                               sheet->row_title_area.y,
5277                               sheet->row_title_area.width,
5278                               sheet->row_title_area.height);
5279     }
5280   if (MAX_VISIBLE_ROW (sheet) == g_sheet_row_get_row_count (sheet->row_geometry) - 1)
5281     gdk_window_clear_area (sheet->row_title_window,
5282                            0, 0,
5283                            sheet->row_title_area.width,
5284                            sheet->row_title_area.height);
5285
5286   if (!GTK_WIDGET_DRAWABLE (sheet)) return;
5287
5288   size_allocate_global_button (sheet);
5289
5290   for (i = MIN_VISIBLE_ROW (sheet); i <= MAX_VISIBLE_ROW (sheet); i++)
5291     {
5292       if ( i >= g_sheet_row_get_row_count (sheet->row_geometry))
5293         break;
5294       gtk_sheet_row_title_button_draw (sheet, i);
5295     }
5296 }
5297
5298
5299 static void
5300 gtk_sheet_size_allocate_entry (GtkSheet *sheet)
5301 {
5302   GtkAllocation shentry_allocation;
5303   GtkSheetCellAttr attributes = { 0 };
5304   GtkEntry *sheet_entry;
5305   GtkStyle *style = NULL, *previous_style = NULL;
5306   gint row, col;
5307   gint size, max_size, text_size, column_width;
5308   const gchar *text;
5309
5310   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
5311   if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
5312
5313   sheet_entry = GTK_ENTRY (gtk_sheet_get_entry (sheet));
5314
5315   if ( ! gtk_sheet_get_attributes (sheet, sheet->active_cell.row,
5316                                    sheet->active_cell.col,
5317                                    &attributes) )
5318     return ;
5319
5320   if ( GTK_WIDGET_REALIZED (sheet->entry_widget) )
5321     {
5322       if (!GTK_WIDGET (sheet_entry)->style)
5323         gtk_widget_ensure_style (GTK_WIDGET (sheet_entry));
5324
5325       previous_style = GTK_WIDGET (sheet_entry)->style;
5326
5327       style = gtk_style_copy (previous_style);
5328       style->bg[GTK_STATE_NORMAL] = attributes.background;
5329       style->fg[GTK_STATE_NORMAL] = attributes.foreground;
5330       style->text[GTK_STATE_NORMAL] = attributes.foreground;
5331       style->bg[GTK_STATE_ACTIVE] = attributes.background;
5332       style->fg[GTK_STATE_ACTIVE] = attributes.foreground;
5333       style->text[GTK_STATE_ACTIVE] = attributes.foreground;
5334
5335       pango_font_description_free (style->font_desc);
5336       g_assert (attributes.font_desc);
5337       style->font_desc = pango_font_description_copy (attributes.font_desc);
5338
5339       GTK_WIDGET (sheet_entry)->style = style;
5340       gtk_widget_size_request (sheet->entry_widget, NULL);
5341       GTK_WIDGET (sheet_entry)->style = previous_style;
5342
5343       if (style != previous_style)
5344         {
5345           if (!GTK_IS_ITEM_ENTRY (sheet->entry_widget))
5346             {
5347               style->bg[GTK_STATE_NORMAL] = previous_style->bg[GTK_STATE_NORMAL];
5348               style->fg[GTK_STATE_NORMAL] = previous_style->fg[GTK_STATE_NORMAL];
5349               style->bg[GTK_STATE_ACTIVE] = previous_style->bg[GTK_STATE_ACTIVE];
5350               style->fg[GTK_STATE_ACTIVE] = previous_style->fg[GTK_STATE_ACTIVE];
5351             }
5352           gtk_widget_set_style (GTK_WIDGET (sheet_entry), style);
5353           g_object_unref (style);
5354         }
5355     }
5356
5357   if (GTK_IS_ITEM_ENTRY (sheet_entry))
5358     max_size = GTK_ITEM_ENTRY (sheet_entry)->text_max_size;
5359   else
5360     max_size = 0;
5361
5362   text_size = 0;
5363   text = gtk_entry_get_text (GTK_ENTRY (sheet_entry));
5364   if (text && strlen (text) > 0)
5365     text_size = STRING_WIDTH (GTK_WIDGET (sheet), attributes.font_desc, text);
5366
5367   column_width = g_sheet_column_get_width (sheet->column_geometry, sheet->active_cell.col);
5368
5369   size = MIN (text_size, max_size);
5370   size = MAX (size, column_width - 2 * COLUMN_TITLES_HEIGHT);
5371
5372   row = sheet->active_cell.row;
5373   col = sheet->active_cell.col;
5374
5375   shentry_allocation.x = COLUMN_LEFT_XPIXEL (sheet, sheet->active_cell.col);
5376   shentry_allocation.y = g_sheet_row_start_pixel (sheet->row_geometry, sheet->active_cell.row);
5377   if ( sheet->column_titles_visible)
5378     shentry_allocation.y += sheet->column_title_area.height;
5379
5380   shentry_allocation.y -= sheet->vadjustment->value;
5381
5382   if ( sheet->row_titles_visible)
5383     shentry_allocation.x += sheet->row_title_area.width;
5384
5385   shentry_allocation.x -= sheet->hadjustment->value;
5386
5387   shentry_allocation.width = column_width;
5388   shentry_allocation.height = g_sheet_row_get_height (sheet->row_geometry, sheet->active_cell.row);
5389
5390   if (GTK_IS_ITEM_ENTRY (sheet->entry_widget))
5391     {
5392       shentry_allocation.height -= 2 * COLUMN_TITLES_HEIGHT;
5393       shentry_allocation.y += COLUMN_TITLES_HEIGHT;
5394       shentry_allocation.width = size;
5395
5396       switch (GTK_ITEM_ENTRY (sheet_entry)->justification)
5397         {
5398         case GTK_JUSTIFY_CENTER:
5399           shentry_allocation.x += column_width / 2 - size / 2;
5400           break;
5401         case GTK_JUSTIFY_RIGHT:
5402           shentry_allocation.x += column_width - size - COLUMN_TITLES_HEIGHT;
5403           break;
5404         case GTK_JUSTIFY_LEFT:
5405         case GTK_JUSTIFY_FILL:
5406           shentry_allocation.x += COLUMN_TITLES_HEIGHT;
5407           break;
5408         }
5409     }
5410
5411   if (!GTK_IS_ITEM_ENTRY (sheet->entry_widget))
5412     {
5413       shentry_allocation.x += 2;
5414       shentry_allocation.y += 2;
5415       shentry_allocation.width -= MIN (shentry_allocation.width, 3);
5416       shentry_allocation.height -= MIN (shentry_allocation.height, 3);
5417     }
5418
5419   gtk_widget_size_allocate (sheet->entry_widget, &shentry_allocation);
5420
5421   if (previous_style == style) g_object_unref (previous_style);
5422 }
5423
5424 static void
5425 gtk_sheet_entry_set_max_size (GtkSheet *sheet)
5426 {
5427   gint i;
5428   gint size = 0;
5429   gint sizel = 0, sizer = 0;
5430   gint row, col;
5431   GtkJustification justification;
5432   gchar *s = NULL;
5433
5434   row = sheet->active_cell.row;
5435   col = sheet->active_cell.col;
5436
5437   if ( ! GTK_IS_ITEM_ENTRY (sheet->entry_widget) )
5438     return;
5439
5440   justification = GTK_ITEM_ENTRY (sheet->entry_widget)->justification;
5441
5442   switch (justification)
5443     {
5444     case GTK_JUSTIFY_FILL:
5445     case GTK_JUSTIFY_LEFT:
5446       for (i = col + 1; i <= MAX_VISIBLE_COLUMN (sheet); i++)
5447         {
5448           if ((s = gtk_sheet_cell_get_text (sheet, row, i)))
5449             {
5450               g_free (s);
5451               break;
5452             }
5453           size +=g_sheet_column_get_width (sheet->column_geometry, i);
5454         }
5455       size = MIN (size, sheet->sheet_window_width - COLUMN_LEFT_XPIXEL (sheet, col));
5456       break;
5457     case GTK_JUSTIFY_RIGHT:
5458       for (i = col - 1; i >= MIN_VISIBLE_COLUMN (sheet); i--)
5459         {
5460           if ((s = gtk_sheet_cell_get_text (sheet, row, i)))
5461             {
5462               g_free (s);
5463               break;
5464             }
5465           size +=g_sheet_column_get_width (sheet->column_geometry, i);
5466         }
5467       break;
5468     case GTK_JUSTIFY_CENTER:
5469       for (i = col + 1; i <= MAX_VISIBLE_COLUMN (sheet); i++)
5470         {
5471           sizer += g_sheet_column_get_width (sheet->column_geometry, i);
5472         }
5473       for (i = col - 1; i >= MIN_VISIBLE_COLUMN (sheet); i--)
5474         {
5475           if ((s = gtk_sheet_cell_get_text (sheet, row, i)))
5476             {
5477               g_free (s);
5478               break;
5479             }
5480           sizel +=g_sheet_column_get_width (sheet->column_geometry, i);
5481         }
5482       size = 2 * MIN (sizel, sizer);
5483       break;
5484     }
5485
5486   if (size != 0)
5487     size += g_sheet_column_get_width (sheet->column_geometry, col);
5488   GTK_ITEM_ENTRY (sheet->entry_widget)->text_max_size = size;
5489 }
5490
5491
5492 static void
5493 create_sheet_entry (GtkSheet *sheet)
5494 {
5495   if (sheet->entry_widget)
5496     {
5497       gtk_widget_unparent (sheet->entry_widget);
5498     }
5499
5500   if (sheet->entry_type)
5501     {
5502       sheet->entry_container = g_object_new (sheet->entry_type, NULL);
5503       g_object_ref_sink (sheet->entry_container);
5504       sheet->entry_widget = gtk_sheet_get_entry (sheet);
5505
5506       if  ( NULL == sheet->entry_widget)
5507         {
5508           g_warning ("Entry type is %s. It must be GtkEntry subclass, or a widget containing one. "
5509                      "Using default", g_type_name (sheet->entry_type));
5510           g_object_unref (sheet->entry_container);
5511           sheet->entry_widget = sheet->entry_container = gtk_item_entry_new ();
5512         }
5513       else
5514         {
5515           sheet->entry_widget = sheet->entry_container ;
5516         }
5517     }
5518   else
5519     {
5520       sheet->entry_widget = sheet->entry_container = gtk_item_entry_new ();
5521       g_object_ref_sink (sheet->entry_container);
5522     }
5523
5524   gtk_widget_size_request (sheet->entry_widget, NULL);
5525
5526   if (GTK_WIDGET_REALIZED (sheet))
5527     {
5528       gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
5529       gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
5530       gtk_widget_realize (sheet->entry_widget);
5531     }
5532
5533   g_signal_connect_swapped (sheet->entry_widget, "key_press_event",
5534                             G_CALLBACK (gtk_sheet_entry_key_press),
5535                             sheet);
5536
5537   gtk_widget_show (sheet->entry_widget);
5538 }
5539
5540
5541 /* Finds the last child widget that happens to be of type GtkEntry */
5542 static void
5543 find_entry (GtkWidget *w, gpointer user_data)
5544 {
5545   GtkWidget **entry = user_data;
5546   if ( GTK_IS_ENTRY (w))
5547     {
5548       *entry = w;
5549     }
5550 }
5551
5552 GtkWidget *
5553 gtk_sheet_get_entry (GtkSheet *sheet)
5554 {
5555   GtkWidget *parent;
5556   GtkWidget *entry = NULL;
5557
5558   g_return_val_if_fail (sheet != NULL, NULL);
5559   g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);
5560   g_return_val_if_fail (sheet->entry_widget != NULL, NULL);
5561
5562   if (GTK_IS_ENTRY (sheet->entry_container))
5563     return (sheet->entry_container);
5564
5565   parent = sheet->entry_container;
5566
5567   if (GTK_IS_CONTAINER (parent))
5568     {
5569       gtk_container_forall (GTK_CONTAINER (parent), find_entry, &entry);
5570
5571       if (GTK_IS_ENTRY (entry))
5572         return entry;
5573     }
5574
5575   if (!GTK_IS_ENTRY (entry)) return NULL;
5576
5577   return (entry);
5578
5579 }
5580
5581 GtkWidget *
5582 gtk_sheet_get_entry_widget (GtkSheet *sheet)
5583 {
5584   g_return_val_if_fail (sheet != NULL, NULL);
5585   g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);
5586   g_return_val_if_fail (sheet->entry_widget != NULL, NULL);
5587
5588   return (sheet->entry_widget);
5589 }
5590
5591
5592 static void
5593 gtk_sheet_button_draw (GtkSheet *sheet, GdkWindow *window,
5594                        GtkSheetButton *button, gboolean is_sensitive,
5595                        GdkRectangle allocation)
5596 {
5597   GtkShadowType shadow_type;
5598   gint text_width = 0, text_height = 0;
5599   PangoAlignment align = PANGO_ALIGN_LEFT;
5600
5601   gboolean rtl ;
5602
5603   gint state = 0;
5604   gint len = 0;
5605   gchar *line = 0;
5606
5607   g_return_if_fail (sheet != NULL);
5608   g_return_if_fail (button != NULL);
5609
5610
5611   rtl = gtk_widget_get_direction (GTK_WIDGET (sheet)) == GTK_TEXT_DIR_RTL;
5612
5613   gdk_window_clear_area (window,
5614                          allocation.x, allocation.y,
5615                          allocation.width, allocation.height);
5616
5617   gtk_paint_box (sheet->button->style, window,
5618                  GTK_STATE_NORMAL, GTK_SHADOW_OUT,
5619                  &allocation, GTK_WIDGET (sheet->button),
5620                  "buttondefault",
5621                  allocation.x, allocation.y,
5622                  allocation.width, allocation.height);
5623
5624   state = button->state;
5625   if (!is_sensitive) state = GTK_STATE_INSENSITIVE;
5626
5627   if (state == GTK_STATE_ACTIVE)
5628     shadow_type = GTK_SHADOW_IN;
5629   else
5630     shadow_type = GTK_SHADOW_OUT;
5631
5632   if (state != GTK_STATE_NORMAL && state != GTK_STATE_INSENSITIVE)
5633     gtk_paint_box (sheet->button->style, window,
5634                    button->state, shadow_type,
5635                    &allocation, GTK_WIDGET (sheet->button),
5636                    "button",
5637                    allocation.x, allocation.y,
5638                    allocation.width, allocation.height);
5639
5640   if (button->label_visible)
5641     {
5642
5643       text_height = DEFAULT_ROW_HEIGHT (GTK_WIDGET (sheet)) -
5644         2 * COLUMN_TITLES_HEIGHT;
5645
5646       gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
5647                                  &allocation);
5648       gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc,
5649                                  &allocation);
5650
5651       allocation.y += 2 * sheet->button->style->ythickness;
5652
5653
5654       if (button->label && strlen (button->label)>0)
5655         {
5656           gchar *words = 0;
5657           PangoLayout *layout = NULL;
5658           gint real_x = allocation.x, real_y = allocation.y;
5659
5660           words = button->label;
5661           line = g_new (gchar, 1);
5662           line[0]='\0';
5663
5664           while (words && *words != '\0')
5665             {
5666               if (*words != '\n')
5667                 {
5668                   len = strlen (line);
5669                   line = g_realloc (line, len + 2);
5670                   line[len]=*words;
5671                   line[len + 1]='\0';
5672                 }
5673               if (*words == '\n' || * (words + 1) == '\0')
5674                 {
5675                   text_width = STRING_WIDTH (GTK_WIDGET (sheet), GTK_WIDGET (sheet)->style->font_desc, line);
5676
5677                   layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), line);
5678                   switch (button->justification)
5679                     {
5680                     case GTK_JUSTIFY_LEFT:
5681                       real_x = allocation.x + COLUMN_TITLES_HEIGHT;
5682                       align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
5683                       break;
5684                     case GTK_JUSTIFY_RIGHT:
5685                       real_x = allocation.x + allocation.width - text_width - COLUMN_TITLES_HEIGHT;
5686                       align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
5687                       break;
5688                     case GTK_JUSTIFY_CENTER:
5689                     default:
5690                       real_x = allocation.x + (allocation.width - text_width)/2;
5691                       align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
5692                       pango_layout_set_justify (layout, TRUE);
5693                     }
5694                   pango_layout_set_alignment (layout, align);
5695                   gtk_paint_layout (GTK_WIDGET (sheet)->style,
5696                                     window,
5697                                     state,
5698                                     FALSE,
5699                                     &allocation,
5700                                     GTK_WIDGET (sheet),
5701                                     "label",
5702                                     real_x, real_y,
5703                                     layout);
5704                   g_object_unref (layout);
5705
5706                   real_y += text_height + 2;
5707
5708                   g_free (line);
5709                   line = g_new (gchar, 1);
5710                   line[0]='\0';
5711                 }
5712               words++;
5713             }
5714           g_free (line);
5715         }
5716
5717       gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
5718                                  NULL);
5719       gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc, NULL);
5720
5721     }
5722
5723   gtk_sheet_button_free (button);
5724 }
5725
5726 static void
5727 gtk_sheet_column_title_button_draw (GtkSheet *sheet, gint column)
5728 {
5729   GdkRectangle allocation;
5730   GtkSheetButton *button = NULL;
5731   gboolean is_sensitive = FALSE;
5732
5733   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
5734
5735   if (!sheet->column_titles_visible) return;
5736   if (!g_sheet_column_get_visibility (sheet->column_geometry, column)) return;
5737
5738   if (column < MIN_VISIBLE_COLUMN (sheet)) return;
5739   if (column > MAX_VISIBLE_COLUMN (sheet)) return;
5740
5741   button = g_sheet_column_get_button (sheet->column_geometry, column);
5742   allocation.y = 0;
5743       allocation.x = COLUMN_LEFT_XPIXEL (sheet, column) + CELL_SPACING;
5744   allocation.x -= sheet->hadjustment->value;
5745
5746   allocation.height = sheet->column_title_area.height;
5747   allocation.width = g_sheet_column_get_width (sheet->column_geometry, column);
5748   is_sensitive = g_sheet_column_get_sensitivity (sheet->column_geometry, column);
5749
5750   gtk_sheet_button_draw (sheet, sheet->column_title_window,
5751                          button, is_sensitive, allocation);
5752     }
5753
5754
5755 static void
5756 gtk_sheet_row_title_button_draw (GtkSheet *sheet, gint row)
5757 {
5758   GdkRectangle allocation;
5759   GtkSheetButton *button = NULL;
5760   gboolean is_sensitive = FALSE;
5761
5762
5763   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
5764
5765   if (!sheet->row_titles_visible) return;
5766   if (!g_sheet_row_get_visibility (sheet->row_geometry, row)) return;
5767
5768   if (row < MIN_VISIBLE_ROW (sheet)) return;
5769   if (row > MAX_VISIBLE_ROW (sheet)) return;
5770
5771   button = g_sheet_row_get_button (sheet->row_geometry, row);
5772   allocation.x = 0;
5773   allocation.y = g_sheet_row_start_pixel (sheet->row_geometry, row) + CELL_SPACING;
5774   allocation.y -= sheet->vadjustment->value;
5775
5776   allocation.width = sheet->row_title_area.width;
5777   allocation.height = g_sheet_row_get_height (sheet->row_geometry, row);
5778   is_sensitive = g_sheet_row_get_sensitivity (sheet->row_geometry, row);
5779
5780   gtk_sheet_button_draw (sheet, sheet->row_title_window,
5781                          button, is_sensitive, allocation);
5782 }
5783
5784 /* SCROLLBARS
5785  *
5786  * functions:
5787  * adjust_scrollbars
5788  * vadjustment_value_changed
5789  * hadjustment_value_changed */
5790
5791 static void
5792 adjust_scrollbars (GtkSheet * sheet)
5793 {
5794   if (sheet->vadjustment)
5795     {
5796       sheet->vadjustment->step_increment =
5797         1 ; // DEFAULT_ROW_HEIGHT (GTK_WIDGET (sheet));
5798
5799       sheet->vadjustment->page_increment =
5800         sheet->sheet_window_height - DEFAULT_ROW_HEIGHT (GTK_WIDGET (sheet));
5801
5802       sheet->vadjustment->upper = DEFAULT_ROW_HEIGHT (GTK_WIDGET (sheet))
5803         * g_sheet_row_get_row_count (sheet->row_geometry);
5804
5805
5806       sheet->vadjustment->lower = 0;
5807       sheet->vadjustment->page_size = sheet->sheet_window_height;
5808
5809       g_signal_emit_by_name (sheet->vadjustment, "changed");
5810     }
5811
5812   if (sheet->hadjustment)
5813     {
5814       gint last_col;
5815       sheet->hadjustment->step_increment = 1 ; //DEFAULT_COLUMN_WIDTH;
5816
5817       sheet->hadjustment->page_increment = sheet->sheet_window_width ;
5818
5819       last_col = g_sheet_column_get_column_count (sheet->column_geometry) - 1;
5820
5821       sheet->hadjustment->upper =
5822         g_sheet_column_start_pixel (sheet->column_geometry, last_col)
5823         +
5824         g_sheet_column_get_width (sheet->column_geometry, last_col)
5825         ;
5826
5827       sheet->hadjustment->lower = 0;
5828       sheet->hadjustment->page_size = sheet->sheet_window_width;
5829
5830       g_signal_emit_by_name (sheet->hadjustment, "changed");
5831     }
5832 }
5833
5834 static void
5835 vadjustment_value_changed (GtkAdjustment * adjustment,
5836                            gpointer data)
5837 {
5838   GtkSheet *sheet = GTK_SHEET (data);
5839
5840   g_return_if_fail (adjustment != NULL);
5841
5842   if ( ! GTK_WIDGET_REALIZED (sheet)) return;
5843
5844   gtk_widget_hide (sheet->entry_widget);
5845   gtk_sheet_range_draw (sheet, NULL);
5846   size_allocate_row_title_buttons (sheet);
5847   //  size_allocate_global_button (sheet);
5848 }
5849
5850
5851 static void
5852 hadjustment_value_changed (GtkAdjustment * adjustment,
5853                            gpointer data)
5854 {
5855   GtkSheet *sheet = GTK_SHEET (data);
5856
5857   g_return_if_fail (adjustment != NULL);
5858
5859   if ( ! GTK_WIDGET_REALIZED (sheet)) return;
5860
5861   gtk_widget_hide (sheet->entry_widget);
5862   gtk_sheet_range_draw (sheet, NULL);
5863   size_allocate_column_title_buttons (sheet);
5864   //  size_allocate_global_button (sheet);
5865 }
5866
5867
5868 /* COLUMN RESIZING */
5869 static void
5870 draw_xor_vline (GtkSheet * sheet)
5871 {
5872   gint xpos = sheet->x_drag;
5873
5874   if (sheet->row_titles_visible)
5875     xpos += sheet->row_title_area.width;
5876
5877   gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
5878                  xpos,
5879                  sheet->column_title_area.height,
5880                  xpos,
5881                  sheet->sheet_window_height + CELL_SPACING);
5882 }
5883
5884 /* ROW RESIZING */
5885 static void
5886 draw_xor_hline (GtkSheet * sheet)
5887
5888 {
5889   gint ypos = sheet->y_drag;
5890
5891   if (sheet->column_titles_visible)
5892     ypos += sheet->column_title_area.height;
5893
5894   gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
5895                  sheet->row_title_area.width,
5896                  ypos,
5897                  sheet->sheet_window_width + CELL_SPACING,
5898                  ypos);
5899 }
5900
5901 /* SELECTED RANGE */
5902 static void
5903 draw_xor_rectangle (GtkSheet *sheet, GtkSheetRange range)
5904 {
5905   gint i = 0;
5906   GdkRectangle clip_area, area;
5907   GdkGCValues values;
5908
5909   area.x = COLUMN_LEFT_XPIXEL (sheet, range.col0);
5910   area.y = g_sheet_row_start_pixel (sheet->row_geometry, range.row0);
5911   area.width = COLUMN_LEFT_XPIXEL (sheet, range.coli)- area.x+
5912     g_sheet_column_get_width (sheet->column_geometry, range.coli);
5913   area.height = g_sheet_row_start_pixel (sheet->row_geometry, range.rowi)- area.y +
5914     g_sheet_row_get_height (sheet->row_geometry, range.rowi);
5915
5916   clip_area.x = sheet->row_title_area.width;
5917   clip_area.y = sheet->column_title_area.height;
5918   clip_area.width = sheet->sheet_window_width;
5919   clip_area.height = sheet->sheet_window_height;
5920
5921   if (!sheet->row_titles_visible) clip_area.x = 0;
5922   if (!sheet->column_titles_visible) clip_area.y = 0;
5923
5924   if (area.x < 0)
5925     {
5926       area.width = area.width + area.x;
5927       area.x = 0;
5928     }
5929   if (area.width > clip_area.width) area.width = clip_area.width + 10;
5930   if (area.y < 0)
5931     {
5932       area.height = area.height + area.y;
5933       area.y = 0;
5934     }
5935   if (area.height > clip_area.height) area.height = clip_area.height + 10;
5936
5937   clip_area.x--;
5938   clip_area.y--;
5939   clip_area.width += 3;
5940   clip_area.height += 3;
5941
5942   gdk_gc_get_values (sheet->xor_gc, &values);
5943
5944   gdk_gc_set_clip_rectangle (sheet->xor_gc, &clip_area);
5945
5946     gdk_draw_rectangle (sheet->sheet_window,
5947                         sheet->xor_gc,
5948                         FALSE,
5949                         area.x + i, area.y + i,
5950                         area.width - 2 * i, area.height - 2 * i);
5951
5952
5953   gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
5954
5955   gdk_gc_set_foreground (sheet->xor_gc, &values.foreground);
5956 }
5957
5958
5959 /* this function returns the new width of the column being resized given
5960  * the COLUMN and X position of the cursor; the x cursor position is passed
5961  * in as a pointer and automaticaly corrected if it's outside the acceptable
5962  * range */
5963 static guint
5964 new_column_width (GtkSheet *sheet, gint column, gint *x)
5965 {
5966   gint left_pos = COLUMN_LEFT_XPIXEL (sheet, column)
5967     - sheet->hadjustment->value;
5968
5969   gint width = *x - left_pos;
5970
5971   if ( width < sheet->column_requisition)
5972     {
5973       width = sheet->column_requisition;
5974       *x = left_pos + width;
5975     }
5976
5977   g_sheet_column_set_width (sheet->column_geometry, column, width);
5978
5979   size_allocate_column_title_buttons (sheet);
5980
5981   return width;
5982 }
5983
5984 /* this function returns the new height of the row being resized given
5985  * the row and y position of the cursor; the y cursor position is passed
5986  * in as a pointer and automaticaly corrected if it's beyond min / max limits */
5987 static guint
5988 new_row_height (GtkSheet *sheet, gint row, gint *y)
5989 {
5990   gint height;
5991   guint min_height;
5992
5993   gint cy = *y;
5994   min_height = sheet->row_requisition;
5995
5996   /* you can't shrink a row to less than its minimum height */
5997   if (cy < g_sheet_row_start_pixel (sheet->row_geometry, row) + min_height)
5998
5999     {
6000       *y = cy = g_sheet_row_start_pixel (sheet->row_geometry, row) + min_height;
6001     }
6002
6003   /* calculate new row height making sure it doesn't end up
6004    * less than the minimum height */
6005   height = (cy - g_sheet_row_start_pixel (sheet->row_geometry, row));
6006   if (height < min_height)
6007     height = min_height;
6008
6009   g_sheet_row_set_height (sheet->row_geometry, row, height);
6010   size_allocate_row_title_buttons (sheet);
6011
6012   return height;
6013 }
6014
6015 static void
6016 gtk_sheet_set_column_width (GtkSheet * sheet,
6017                             gint column,
6018                             guint width)
6019 {
6020   guint min_width;
6021
6022   g_return_if_fail (sheet != NULL);
6023   g_return_if_fail (GTK_IS_SHEET (sheet));
6024
6025   if (column < 0 || column >= g_sheet_column_get_column_count (sheet->column_geometry))
6026     return;
6027
6028   gtk_sheet_column_size_request (sheet, column, &min_width);
6029   if (width < min_width) return;
6030
6031   g_sheet_column_set_width (sheet->column_geometry, column, width);
6032
6033   if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
6034     {
6035       size_allocate_column_title_buttons (sheet);
6036       adjust_scrollbars (sheet);
6037       gtk_sheet_size_allocate_entry (sheet);
6038       gtk_sheet_range_draw (sheet, NULL);
6039     }
6040
6041   g_signal_emit (sheet, sheet_signals[CHANGED], 0, -1, column);
6042 }
6043
6044
6045
6046 void
6047 gtk_sheet_set_row_height (GtkSheet * sheet,
6048                           gint row,
6049                           guint height)
6050 {
6051   guint min_height;
6052
6053   g_return_if_fail (sheet != NULL);
6054   g_return_if_fail (GTK_IS_SHEET (sheet));
6055
6056   if (row < 0 || row >= g_sheet_row_get_row_count (sheet->row_geometry))
6057     return;
6058
6059   gtk_sheet_row_size_request (sheet, row, &min_height);
6060   if (height < min_height) return;
6061
6062   g_sheet_row_set_height (sheet->row_geometry, row, height);
6063
6064   if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) )
6065     {
6066       size_allocate_row_title_buttons (sheet);
6067       adjust_scrollbars (sheet);
6068       gtk_sheet_size_allocate_entry (sheet);
6069       gtk_sheet_range_draw (sheet, NULL);
6070     }
6071
6072   g_signal_emit (sheet, sheet_signals[CHANGED], 0, row, - 1);
6073 }
6074 gboolean
6075 gtk_sheet_get_attributes (const GtkSheet *sheet, gint row, gint col,
6076                           GtkSheetCellAttr *attributes)
6077 {
6078   const GdkColor *fg, *bg;
6079   const GtkJustification *j ;
6080   const PangoFontDescription *font_desc ;
6081   const GtkSheetCellBorder *border ;
6082
6083   g_return_val_if_fail (sheet != NULL, FALSE);
6084   g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE);
6085
6086   if (row < 0 || col < 0) return FALSE;
6087
6088   init_attributes (sheet, col, attributes);
6089
6090   if ( !sheet->model)
6091     return FALSE;
6092
6093   attributes->is_editable = g_sheet_model_is_editable (sheet->model, row, col);
6094   attributes->is_visible = g_sheet_model_is_visible (sheet->model, row, col);
6095
6096   fg = g_sheet_model_get_foreground (sheet->model, row, col);
6097   if ( fg )
6098     attributes->foreground = *fg;
6099
6100   bg = g_sheet_model_get_background (sheet->model, row, col);
6101   if ( bg )
6102     attributes->background = *bg;
6103
6104   j = g_sheet_model_get_justification (sheet->model, row, col);
6105   if (j) attributes->justification = *j;
6106
6107   font_desc = g_sheet_model_get_font_desc (sheet->model, row, col);
6108   if ( font_desc ) attributes->font_desc = font_desc;
6109
6110   border = g_sheet_model_get_cell_border (sheet->model, row, col);
6111
6112   if ( border ) attributes->border = *border;
6113
6114   return TRUE;
6115 }
6116
6117 static void
6118 init_attributes (const GtkSheet *sheet, gint col, GtkSheetCellAttr *attributes)
6119 {
6120   /* DEFAULT VALUES */
6121   attributes->foreground = GTK_WIDGET (sheet)->style->black;
6122   attributes->background = sheet->color[BG_COLOR];
6123   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
6124     {
6125       attributes->background = sheet->color[BG_COLOR];
6126     }
6127   attributes->justification = g_sheet_column_get_justification (sheet->column_geometry, col);
6128   attributes->border.width = 0;
6129   attributes->border.line_style = GDK_LINE_SOLID;
6130   attributes->border.cap_style = GDK_CAP_NOT_LAST;
6131   attributes->border.join_style = GDK_JOIN_MITER;
6132   attributes->border.mask = 0;
6133   attributes->border.color = GTK_WIDGET (sheet)->style->black;
6134   attributes->is_editable = TRUE;
6135   attributes->is_visible = TRUE;
6136   attributes->font_desc = GTK_WIDGET (sheet)->style->font_desc;
6137 }
6138
6139 static void
6140 label_size_request (GtkSheet *sheet, gchar *label, GtkRequisition *req)
6141 {
6142   gchar *words;
6143   gchar word[1000];
6144   gint n = 0;
6145   gint row_height = DEFAULT_ROW_HEIGHT (GTK_WIDGET (sheet)) - 2 * COLUMN_TITLES_HEIGHT + 2;
6146
6147   req->height = 0;
6148   req->width = 0;
6149   words = label;
6150
6151   while (words && *words != '\0')
6152     {
6153       if (*words == '\n' || * (words + 1) == '\0')
6154         {
6155           req->height += row_height;
6156
6157           word[n] = '\0';
6158           req->width = MAX (req->width, STRING_WIDTH (GTK_WIDGET (sheet), GTK_WIDGET (sheet)->style->font_desc, word));
6159           n = 0;
6160         }
6161       else
6162         {
6163           word[n++] = *words;
6164         }
6165       words++;
6166     }
6167
6168   if (n > 0) req->height -= 2;
6169 }
6170
6171 static void
6172 gtk_sheet_button_size_request    (GtkSheet *sheet,
6173                                   const GtkSheetButton *button,
6174                                   GtkRequisition *button_requisition)
6175 {
6176   GtkRequisition requisition;
6177   GtkRequisition label_requisition;
6178
6179   if (gtk_sheet_autoresize (sheet) && button->label && strlen (button->label) > 0)
6180     {
6181       label_size_request (sheet, button->label, &label_requisition);
6182       label_requisition.width += 2 * COLUMN_TITLES_HEIGHT;
6183       label_requisition.height += 2 * COLUMN_TITLES_HEIGHT;
6184     }
6185   else
6186     {
6187       label_requisition.height = DEFAULT_ROW_HEIGHT (GTK_WIDGET (sheet));
6188       label_requisition.width = COLUMN_MIN_WIDTH;
6189     }
6190
6191       requisition.height = DEFAULT_ROW_HEIGHT (GTK_WIDGET (sheet));
6192       requisition.width = COLUMN_MIN_WIDTH;
6193
6194
6195   *button_requisition = requisition;
6196   button_requisition->width = MAX (requisition.width, label_requisition.width);
6197   button_requisition->height = MAX (requisition.height, label_requisition.height);
6198
6199 }
6200
6201 static void
6202 gtk_sheet_row_size_request (GtkSheet *sheet,
6203                             gint row,
6204                             guint *requisition)
6205 {
6206   GtkRequisition button_requisition;
6207
6208   gtk_sheet_button_size_request (sheet,
6209                                  g_sheet_row_get_button (sheet->row_geometry, row),
6210                                  &button_requisition);
6211
6212   *requisition = button_requisition.height;
6213
6214   sheet->row_requisition = * requisition;
6215 }
6216
6217 static void
6218 gtk_sheet_column_size_request (GtkSheet *sheet,
6219                                gint col,
6220                                guint *requisition)
6221 {
6222   GtkRequisition button_requisition;
6223
6224   GtkSheetButton *button = g_sheet_column_get_button (sheet->column_geometry, col);
6225
6226   gtk_sheet_button_size_request (sheet,
6227                                  button,
6228                                  &button_requisition);
6229
6230   gtk_sheet_button_free (button);
6231
6232   *requisition = button_requisition.width;
6233
6234   sheet->column_requisition = *requisition;
6235 }
6236
6237
6238 static void
6239 gtk_sheet_forall (GtkContainer *container,
6240                   gboolean include_internals,
6241                   GtkCallback callback,
6242                   gpointer callback_data)
6243 {
6244   GtkSheet *sheet = GTK_SHEET (container);
6245
6246   g_return_if_fail (callback != NULL);
6247
6248   if (sheet->button && sheet->button->parent)
6249     (* callback) (sheet->button, callback_data);
6250
6251   if (sheet->entry_container && GTK_IS_CONTAINER (sheet->entry_container))
6252     (* callback) (sheet->entry_container, callback_data);
6253 }
6254
6255
6256 GSheetModel *
6257 gtk_sheet_get_model (const GtkSheet *sheet)
6258 {
6259   g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);
6260
6261   return sheet->model;
6262 }
6263
6264
6265 GtkSheetButton *
6266 gtk_sheet_button_new (void)
6267 {
6268   GtkSheetButton *button = g_malloc (sizeof (GtkSheetButton));
6269
6270   button->state = GTK_STATE_NORMAL;
6271   button->label = NULL;
6272   button->label_visible = TRUE;
6273   button->justification = GTK_JUSTIFY_FILL;
6274
6275   return button;
6276 }
6277
6278
6279 void
6280 gtk_sheet_button_free (GtkSheetButton *button)
6281 {
6282   if (!button) return ;
6283
6284   g_free (button->label);
6285   g_free (button);
6286 }
6287
6288
6289 static void
6290 append_cell_text (GString *string, const GtkSheet *sheet, gint r, gint c)
6291 {
6292   gchar *celltext = gtk_sheet_cell_get_text (sheet, r, c);
6293
6294   if ( NULL == celltext)
6295     return;
6296
6297   g_string_append (string, celltext);
6298   g_free (celltext);
6299 }
6300
6301
6302 static GString *
6303 range_to_text (const GtkSheet *sheet)
6304 {
6305   gint r, c;
6306   GString *string;
6307
6308   if ( !gtk_sheet_range_isvisible (sheet, sheet->range))
6309     return NULL;
6310
6311   string = g_string_sized_new (80);
6312
6313   for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
6314     {
6315       for (c = sheet->range.col0; c < sheet->range.coli; ++c)
6316         {
6317           append_cell_text (string, sheet, r, c);
6318           g_string_append (string, "\t");
6319         }
6320       append_cell_text (string, sheet, r, c);
6321       if ( r < sheet->range.rowi)
6322         g_string_append (string, "\n");
6323     }
6324
6325   return string;
6326 }
6327
6328 static GString *
6329 range_to_html (const GtkSheet *sheet)
6330 {
6331   gint r, c;
6332   GString *string;
6333
6334   if ( !gtk_sheet_range_isvisible (sheet, sheet->range))
6335     return NULL;
6336
6337   string = g_string_sized_new (480);
6338
6339   g_string_append (string, "<html>\n");
6340   g_string_append (string, "<body>\n");
6341   g_string_append (string, "<table>\n");
6342   for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
6343     {
6344       g_string_append (string, "<tr>\n");
6345       for (c = sheet->range.col0; c <= sheet->range.coli; ++c)
6346         {
6347           g_string_append (string, "<td>");
6348           append_cell_text (string, sheet, r, c);
6349           g_string_append (string, "</td>\n");
6350         }
6351       g_string_append (string, "</tr>\n");
6352     }
6353   g_string_append (string, "</table>\n");
6354   g_string_append (string, "</body>\n");
6355   g_string_append (string, "</html>\n");
6356
6357   return string;
6358 }
6359
6360 enum {
6361   SELECT_FMT_NULL,
6362   SELECT_FMT_TEXT,
6363   SELECT_FMT_HTML
6364 };
6365
6366 static void
6367 primary_get_cb (GtkClipboard     *clipboard,
6368                 GtkSelectionData *selection_data,
6369                 guint             info,
6370                 gpointer          data)
6371 {
6372   GtkSheet *sheet = GTK_SHEET (data);
6373   GString *string = NULL;
6374
6375   switch (info)
6376     {
6377     case SELECT_FMT_TEXT:
6378       string = range_to_text (sheet);
6379       break;
6380     case SELECT_FMT_HTML:
6381       string = range_to_html (sheet);
6382       break;
6383     default:
6384       g_assert_not_reached ();
6385     }
6386
6387   gtk_selection_data_set (selection_data, selection_data->target,
6388                           8,
6389                           (const guchar *) string->str, string->len);
6390   g_string_free (string, TRUE);
6391 }
6392
6393 static void
6394 primary_clear_cb (GtkClipboard *clipboard,
6395                   gpointer      data)
6396 {
6397   GtkSheet *sheet = GTK_SHEET (data);
6398   if ( ! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
6399     return;
6400
6401   gtk_sheet_real_unselect_range (sheet, NULL);
6402 }
6403
6404 static void
6405 gtk_sheet_update_primary_selection (GtkSheet *sheet)
6406 {
6407   static const GtkTargetEntry targets[] = {
6408     { "UTF8_STRING",   0, SELECT_FMT_TEXT },
6409     { "STRING",        0, SELECT_FMT_TEXT },
6410     { "TEXT",          0, SELECT_FMT_TEXT },
6411     { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
6412     { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
6413     { "text/plain",    0, SELECT_FMT_TEXT },
6414     { "text/html",     0, SELECT_FMT_HTML }
6415   };
6416
6417   GtkClipboard *clipboard;
6418
6419   if (!GTK_WIDGET_REALIZED (sheet))
6420     return;
6421
6422   clipboard = gtk_widget_get_clipboard (GTK_WIDGET (sheet),
6423                                         GDK_SELECTION_PRIMARY);
6424
6425   if (gtk_sheet_range_isvisible (sheet, sheet->range))
6426     {
6427       if (!gtk_clipboard_set_with_owner (clipboard, targets,
6428                                          G_N_ELEMENTS (targets),
6429                                          primary_get_cb, primary_clear_cb,
6430                                          G_OBJECT (sheet)))
6431         primary_clear_cb (clipboard, sheet);
6432     }
6433   else
6434     {
6435       if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (sheet))
6436         gtk_clipboard_clear (clipboard);
6437     }
6438 }