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