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