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