2 * Copyright (C) 2006, 2008 Free Software Foundation
4 * This version of GtkSheet has been *heavily* modified, for the specific
5 * requirements of PSPPIRE. The changes are copyright by the
6 * Free Software Foundation. The copyright notice for the original work is
10 /* GtkSheet widget for Gtk+.
11 * Copyright (C) 1999-2001 Adrian E. Feiguin <adrian@ifir.ifir.edu.ar>
13 * Based on GtkClist widget by Jay Painter, but major changes.
14 * Memory allocation routines inspired on SC (Spreadsheet Calculator)
16 * This library is free software; you can redistribute it and/or
17 * modify it under the terms of the GNU Lesser General Public
18 * License as published by the Free Software Foundation; either
19 * version 2.1 of the License, or (at your option) any later version.
21 * This library is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 * Lesser General Public License for more details.
26 * You should have received a copy of the GNU Lesser General Public
27 * License along with this library; if not, write to the Free Software
28 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
33 * @short_description: spreadsheet widget for gtk2
35 * GtkSheet is a matrix widget for GTK+. It consists of an scrollable grid of
36 * cells where you can allocate text. Cell contents can be edited interactively
37 * through a specially designed entry, GtkItemEntry.
47 #include <gdk/gdkkeysyms.h>
48 #include <gtk/gtksignal.h>
49 #include <gtk/gtkbutton.h>
50 #include <gtk/gtkadjustment.h>
51 #include <gtk/gtktypeutils.h>
52 #include <gtk/gtkentry.h>
53 #include <gtk/gtkcontainer.h>
54 #include <pango/pango.h>
56 #include "gtkextra-marshal.h"
57 #include "gsheetmodel.h"
58 #include <libpspp/misc.h>
64 GTK_SHEET_IN_XDRAG = 1 << 1,
65 GTK_SHEET_IN_YDRAG = 1 << 2,
66 GTK_SHEET_IN_DRAG = 1 << 3,
67 GTK_SHEET_IN_SELECTION = 1 << 4,
68 GTK_SHEET_IN_RESIZE = 1 << 5
71 #define GTK_SHEET_FLAGS(sheet) (GTK_SHEET (sheet)->flags)
72 #define GTK_SHEET_SET_FLAGS(sheet,flag) (GTK_SHEET_FLAGS (sheet) |= (flag))
73 #define GTK_SHEET_UNSET_FLAGS(sheet,flag) (GTK_SHEET_FLAGS (sheet) &= ~ (flag))
75 #define GTK_SHEET_IN_XDRAG(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_XDRAG)
76 #define GTK_SHEET_IN_YDRAG(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_YDRAG)
77 #define GTK_SHEET_IN_DRAG(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_DRAG)
78 #define GTK_SHEET_IN_SELECTION(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_SELECTION)
79 #define GTK_SHEET_IN_RESIZE(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_RESIZE)
81 #define CELL_SPACING 1
83 #define TIMEOUT_HOVER 300
84 #define COLUMN_MIN_WIDTH 10
85 #define COLUMN_TITLES_HEIGHT 4
86 #define DEFAULT_COLUMN_WIDTH 80
87 #define DEFAULT_ROW_HEIGHT 25
89 static void gtk_sheet_update_primary_selection (GtkSheet *sheet);
90 static void draw_column_title_buttons_range (GtkSheet *sheet, gint first, gint n);
91 static void draw_row_title_buttons_range (GtkSheet *sheet, gint first, gint n);
94 static void gtk_sheet_set_row_height (GtkSheet *sheet,
98 static void destroy_hover_window (GtkSheetHoverTitle *);
99 static GtkSheetHoverTitle *create_hover_window (void);
101 static GtkStateType gtk_sheet_cell_get_state (GtkSheet *sheet, gint row, gint col);
105 dispose_string (const GtkSheet *sheet, gchar *text)
107 GSheetModel *model = gtk_sheet_get_model (sheet);
112 if (g_sheet_model_free_strings (model))
116 /* Return the row containing pixel Y */
118 yyy_row_ypixel_to_row (const GtkSheet *sheet, gint y)
120 GSheetRow *geo = sheet->row_geometry;
122 g_return_val_if_fail (y >= 0, -1);
124 return g_sheet_row_pixel_to_row (geo, y);
127 /* Return the lowest row number which is wholly or partially on
128 the visible range of the sheet */
130 min_visible_row (const GtkSheet *sheet)
132 return yyy_row_ypixel_to_row (sheet, sheet->vadjustment->value);
136 min_fully_visible_row (const GtkSheet *sheet)
138 glong row = yyy_row_ypixel_to_row (sheet, sheet->vadjustment->value);
140 if ( g_sheet_row_start_pixel (sheet->row_geometry, row) < sheet->vadjustment->value)
149 max_visible_row (const GtkSheet *sheet)
151 return yyy_row_ypixel_to_row (sheet,
152 sheet->vadjustment->value +
153 sheet->vadjustment->page_size);
158 max_fully_visible_row (const GtkSheet *sheet)
160 glong row = max_visible_row (sheet);
162 if ( g_sheet_row_start_pixel (sheet->row_geometry, row)
164 g_sheet_row_get_height (sheet->row_geometry, row)
165 > sheet->vadjustment->value)
172 /* returns the column index from a x pixel location */
174 column_from_xpixel (const GtkSheet *sheet, gint x)
179 if (x < 0) return -1;
181 i < g_sheet_column_get_column_count (sheet->column_geometry); i++)
184 x <= (cx + g_sheet_column_get_width (sheet->column_geometry, i)))
187 cx += g_sheet_column_get_width (sheet->column_geometry, i);
191 return g_sheet_column_get_column_count (sheet->column_geometry) - 1;
195 /* Returns the lowest column number which is wholly or partially
198 min_visible_column (const GtkSheet *sheet)
200 return column_from_xpixel (sheet, sheet->hadjustment->value);
204 min_fully_visible_column (const GtkSheet *sheet)
206 glong col = min_visible_column (sheet);
208 if ( g_sheet_column_start_pixel (sheet->column_geometry, col) < sheet->hadjustment->value)
215 /* Returns the highest column number which is wholly or partially
218 max_visible_column (const GtkSheet *sheet)
220 return column_from_xpixel (sheet,
221 sheet->hadjustment->value +
222 sheet->hadjustment->page_size);
226 max_fully_visible_column (const GtkSheet *sheet)
228 glong col = max_visible_column (sheet);
230 if ( g_sheet_column_start_pixel (sheet->column_geometry, col)
232 g_sheet_column_get_width (sheet->column_geometry, col)
233 > sheet->hadjustment->value)
241 /* The size of the region (in pixels) around the row/column boundaries
242 where the height/width may be grabbed to change size */
246 on_column_boundary (const GtkSheet *sheet, gint x, gint *column)
250 x += sheet->hadjustment->value;
252 col = column_from_xpixel (sheet, x);
254 if ( column_from_xpixel (sheet, x - DRAG_WIDTH / 2) < col )
260 if ( column_from_xpixel (sheet, x + DRAG_WIDTH / 2) > col )
269 static inline gboolean
270 POSSIBLE_YDRAG (const GtkSheet *sheet, gint y, gint *drag_row)
274 y += sheet->vadjustment->value;
275 row = yyy_row_ypixel_to_row (sheet, y);
278 ydrag = g_sheet_row_start_pixel (sheet->row_geometry, row) + CELL_SPACING;
279 if (y <= ydrag + DRAG_WIDTH / 2 && row != 0)
282 return g_sheet_row_get_sensitivity (sheet->row_geometry, row - 1);
285 ydrag += g_sheet_row_get_height (sheet->row_geometry, row);
287 if (y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2)
288 return g_sheet_row_get_sensitivity (sheet->row_geometry, row);
293 static inline gboolean
294 POSSIBLE_DRAG (const GtkSheet *sheet, gint x, gint y,
295 gint *drag_row, gint *drag_column)
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 )
304 *drag_column = column_from_xpixel (sheet, x);
305 *drag_row = yyy_row_ypixel_to_row (sheet, y);
307 if (x >= g_sheet_column_start_pixel (sheet->column_geometry, sheet->range.col0) - DRAG_WIDTH / 2 &&
308 x <= g_sheet_column_start_pixel (sheet->column_geometry, sheet->range.coli) +
309 g_sheet_column_get_width (sheet->column_geometry, sheet->range.coli) + DRAG_WIDTH / 2)
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)
314 *drag_row = sheet->range.row0;
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)
321 *drag_row = sheet->range.rowi;
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)
330 xdrag = g_sheet_column_start_pixel (sheet->column_geometry, sheet->range.col0);
331 if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2)
333 *drag_column = sheet->range.col0;
336 xdrag = g_sheet_column_start_pixel (sheet->column_geometry, 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)
340 *drag_column = sheet->range.coli;
348 static inline gboolean
349 POSSIBLE_RESIZE (const GtkSheet *sheet, gint x, gint y,
350 gint *drag_row, gint *drag_column)
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 )
359 xdrag = g_sheet_column_start_pixel (sheet->column_geometry, sheet->range.coli)+
360 g_sheet_column_get_width (sheet->column_geometry, sheet->range.coli);
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);
365 if (sheet->state == GTK_SHEET_COLUMN_SELECTED)
366 ydrag = g_sheet_row_start_pixel (sheet->row_geometry, min_visible_row (sheet));
368 if (sheet->state == GTK_SHEET_ROW_SELECTED)
369 xdrag = g_sheet_column_start_pixel (sheet->column_geometry, min_visible_column (sheet));
371 *drag_column = column_from_xpixel (sheet, x);
372 *drag_row = yyy_row_ypixel_to_row (sheet, y);
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;
382 rectangle_from_range (GtkSheet *sheet, const GtkSheetRange *range,
385 g_return_val_if_fail (range, FALSE);
387 r->x = g_sheet_column_start_pixel (sheet->column_geometry, range->col0);
388 r->x -= round (sheet->hadjustment->value);
390 if ( sheet->row_titles_visible)
391 r->x += sheet->row_title_area.width;
394 r->y = g_sheet_row_start_pixel (sheet->row_geometry, range->row0);
395 r->y -= round (sheet->vadjustment->value);
397 if ( sheet->column_titles_visible)
398 r->y += sheet->column_title_area.height;
400 r->width = g_sheet_column_start_pixel (sheet->column_geometry, range->coli) -
401 g_sheet_column_start_pixel (sheet->column_geometry, range->col0) +
402 g_sheet_column_get_width (sheet->column_geometry, range->coli);
404 r->height = g_sheet_row_start_pixel (sheet->row_geometry, range->rowi) -
405 g_sheet_row_start_pixel (sheet->row_geometry, range->row0) +
406 g_sheet_row_get_height (sheet->row_geometry, range->rowi);
412 rectangle_from_cell (GtkSheet *sheet, gint row, gint col,
416 g_return_val_if_fail (row >= 0, FALSE);
417 g_return_val_if_fail (col >= 0, FALSE);
419 range.row0 = range.rowi = row;
420 range.col0 = range.coli = col;
422 return rectangle_from_range (sheet, &range, r);
426 static void gtk_sheet_class_init (GtkSheetClass *klass);
427 static void gtk_sheet_init (GtkSheet *sheet);
428 static void gtk_sheet_dispose (GObject *object);
429 static void gtk_sheet_finalize (GObject *object);
430 static void gtk_sheet_style_set (GtkWidget *widget,
431 GtkStyle *previous_style);
432 static void gtk_sheet_realize (GtkWidget *widget);
433 static void gtk_sheet_unrealize (GtkWidget *widget);
434 static void gtk_sheet_map (GtkWidget *widget);
435 static void gtk_sheet_unmap (GtkWidget *widget);
436 static gint gtk_sheet_expose (GtkWidget *widget,
437 GdkEventExpose *event);
438 static void gtk_sheet_forall (GtkContainer *container,
439 gboolean include_internals,
440 GtkCallback callback,
441 gpointer callback_data);
443 static void gtk_sheet_set_scroll_adjustments (GtkSheet *sheet,
444 GtkAdjustment *hadjustment,
445 GtkAdjustment *vadjustment);
447 static gint gtk_sheet_button_press (GtkWidget *widget,
448 GdkEventButton *event);
449 static gint gtk_sheet_button_release (GtkWidget *widget,
450 GdkEventButton *event);
451 static gint gtk_sheet_motion (GtkWidget *widget,
452 GdkEventMotion *event);
453 static gboolean gtk_sheet_crossing_notify (GtkWidget *widget,
454 GdkEventCrossing *event);
455 static gint gtk_sheet_entry_key_press (GtkWidget *widget,
457 static gboolean gtk_sheet_key_press (GtkWidget *widget,
459 static void gtk_sheet_size_request (GtkWidget *widget,
460 GtkRequisition *requisition);
461 static void gtk_sheet_size_allocate (GtkWidget *widget,
462 GtkAllocation *allocation);
466 static gboolean gtk_sheet_range_isvisible (const GtkSheet *sheet,
467 GtkSheetRange range);
468 static gboolean gtk_sheet_cell_isvisible (GtkSheet *sheet,
469 gint row, gint column);
470 /* Drawing Routines */
473 static void gtk_sheet_cell_draw (GtkSheet *sheet, gint row, gint column);
476 /* draw visible part of range. If range == NULL then draw the whole screen */
477 static void gtk_sheet_range_draw (GtkSheet *sheet,
478 const GtkSheetRange *range);
480 /* highlight the visible part of the selected range */
481 static void gtk_sheet_range_draw_selection (GtkSheet *sheet,
482 GtkSheetRange range);
486 static void gtk_sheet_real_select_range (GtkSheet *sheet,
487 const GtkSheetRange *range);
488 static void gtk_sheet_real_unselect_range (GtkSheet *sheet,
489 const GtkSheetRange *range);
490 static void gtk_sheet_extend_selection (GtkSheet *sheet,
491 gint row, gint column);
492 static void gtk_sheet_new_selection (GtkSheet *sheet,
493 GtkSheetRange *range);
494 static void gtk_sheet_draw_border (GtkSheet *sheet,
495 GtkSheetRange range);
497 /* Active Cell handling */
499 static void gtk_sheet_entry_changed (GtkWidget *widget,
501 static void gtk_sheet_hide_active_cell (GtkSheet *sheet);
502 static void change_active_cell (GtkSheet *sheet,
504 static void gtk_sheet_draw_active_cell (GtkSheet *sheet);
505 static void gtk_sheet_show_active_cell (GtkSheet *sheet);
506 static gboolean gtk_sheet_click_cell (GtkSheet *sheet,
513 static void adjust_scrollbars (GtkSheet *sheet);
514 static void vadjustment_value_changed (GtkAdjustment *adjustment,
516 static void hadjustment_value_changed (GtkAdjustment *adjustment,
520 static void draw_xor_vline (GtkSheet *sheet);
521 static void draw_xor_hline (GtkSheet *sheet);
522 static void draw_xor_rectangle (GtkSheet *sheet,
523 GtkSheetRange range);
525 static guint new_column_width (GtkSheet *sheet,
528 static guint new_row_height (GtkSheet *sheet,
533 static void create_global_button (GtkSheet *sheet);
534 static void global_button_clicked (GtkWidget *widget,
538 static void create_sheet_entry (GtkSheet *sheet);
539 static void gtk_sheet_size_allocate_entry (GtkSheet *sheet);
541 /* Sheet button gadgets */
543 static void draw_column_title_buttons (GtkSheet *sheet);
544 static void draw_row_title_buttons (GtkSheet *sheet);
547 static void size_allocate_global_button (GtkSheet *sheet);
548 static void gtk_sheet_button_size_request (GtkSheet *sheet,
549 const GtkSheetButton *button,
550 GtkRequisition *requisition);
552 static void gtk_sheet_real_cell_clear (GtkSheet *sheet,
557 static void gtk_sheet_column_size_request (GtkSheet *sheet,
560 static void gtk_sheet_row_size_request (GtkSheet *sheet,
582 static GtkContainerClass *parent_class = NULL;
583 static guint sheet_signals[LAST_SIGNAL] = { 0 };
587 gtk_sheet_get_type ()
589 static GType sheet_type = 0;
593 static const GTypeInfo sheet_info =
595 sizeof (GtkSheetClass),
598 (GClassInitFunc) gtk_sheet_class_init,
603 (GInstanceInitFunc) gtk_sheet_init,
608 g_type_register_static (GTK_TYPE_BIN, "GtkSheet",
616 static GtkSheetRange*
617 gtk_sheet_range_copy (const GtkSheetRange *range)
619 GtkSheetRange *new_range;
621 g_return_val_if_fail (range != NULL, NULL);
623 new_range = g_new (GtkSheetRange, 1);
631 gtk_sheet_range_free (GtkSheetRange *range)
633 g_return_if_fail (range != NULL);
639 gtk_sheet_range_get_type (void)
641 static GType sheet_range_type = 0;
643 if (!sheet_range_type)
646 g_boxed_type_register_static ("GtkSheetRange",
647 (GBoxedCopyFunc) gtk_sheet_range_copy,
648 (GBoxedFreeFunc) gtk_sheet_range_free);
651 return sheet_range_type;
655 gtk_sheet_cell_copy (const GtkSheetCell *cell)
657 GtkSheetCell *new_cell;
659 g_return_val_if_fail (cell != NULL, NULL);
661 new_cell = g_new (GtkSheetCell, 1);
669 gtk_sheet_cell_free (GtkSheetCell *cell)
671 g_return_if_fail (cell != NULL);
677 gtk_sheet_cell_get_type (void)
679 static GType sheet_cell_type = 0;
681 if (!sheet_cell_type)
684 g_boxed_type_register_static ("GtkSheetCell",
685 (GBoxedCopyFunc) gtk_sheet_cell_copy,
686 (GBoxedFreeFunc) gtk_sheet_cell_free);
689 return sheet_cell_type;
703 gtk_sheet_set_row_geometry (GtkSheet *sheet, GSheetRow *geo)
705 if ( sheet->row_geometry ) g_object_unref (sheet->row_geometry);
707 sheet->row_geometry = geo;
709 if ( sheet->row_geometry ) g_object_ref (sheet->row_geometry);
713 gtk_sheet_set_column_geometry (GtkSheet *sheet, GSheetColumn *geo)
715 if ( sheet->column_geometry ) g_object_unref (sheet->column_geometry);
717 sheet->column_geometry = geo;
719 if ( sheet->column_geometry ) g_object_ref (sheet->column_geometry);
724 gtk_sheet_set_property (GObject *object,
730 GtkSheet *sheet = GTK_SHEET (object);
735 gtk_sheet_set_row_geometry (sheet, g_value_get_pointer (value));
738 gtk_sheet_set_column_geometry (sheet, g_value_get_pointer (value));
741 gtk_sheet_set_model (sheet, g_value_get_pointer (value));
744 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
750 gtk_sheet_get_property (GObject *object,
755 GtkSheet *sheet = GTK_SHEET (object);
760 g_value_set_pointer (value, sheet->row_geometry);
763 g_value_set_pointer (value, sheet->column_geometry);
766 g_value_set_pointer (value, sheet->model);
769 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
776 gtk_sheet_class_init (GtkSheetClass *klass)
778 GObjectClass *object_class = G_OBJECT_CLASS (klass);
780 GParamSpec *row_geo_spec ;
781 GParamSpec *col_geo_spec ;
782 GParamSpec *model_spec ;
784 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
785 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
787 parent_class = g_type_class_peek_parent (klass);
790 * GtkSheet::select-row
791 * @sheet: the sheet widget that emitted the signal
792 * @row: the newly selected row index
794 * A row has been selected.
796 sheet_signals[SELECT_ROW] =
797 g_signal_new ("select-row",
798 G_TYPE_FROM_CLASS (object_class),
800 offsetof (GtkSheetClass, select_row),
802 g_cclosure_marshal_VOID__INT,
809 * GtkSheet::select - column
810 * @sheet: the sheet widget that emitted the signal
811 * @column: the newly selected column index
813 * A column has been selected.
815 sheet_signals[SELECT_COLUMN] =
816 g_signal_new ("select-column",
817 G_TYPE_FROM_CLASS (object_class),
819 offsetof (GtkSheetClass, select_column),
821 g_cclosure_marshal_VOID__INT,
828 * GtkSheet::double-click-row
829 * @sheet: the sheet widget that emitted the signal
830 * @row: the row that was double clicked.
832 * A row's title button has been double clicked
834 sheet_signals[DOUBLE_CLICK_ROW] =
835 g_signal_new ("double-click-row",
836 G_TYPE_FROM_CLASS (object_class),
840 g_cclosure_marshal_VOID__INT,
847 * GtkSheet::double-click-column
848 * @sheet: the sheet widget that emitted the signal
849 * @column: the column that was double clicked.
851 * A column's title button has been double clicked
853 sheet_signals[DOUBLE_CLICK_COLUMN] =
854 g_signal_new ("double-click-column",
855 G_TYPE_FROM_CLASS (object_class),
859 g_cclosure_marshal_VOID__INT,
866 * GtkSheet::button-event-column
867 * @sheet: the sheet widget that emitted the signal
868 * @column: the column on which the event occured.
870 * A button event occured on a column title button
872 sheet_signals[BUTTON_EVENT_COLUMN] =
873 g_signal_new ("button-event-column",
874 G_TYPE_FROM_CLASS (object_class),
878 gtkextra_VOID__INT_POINTER,
887 * GtkSheet::button-event-row
888 * @sheet: the sheet widget that emitted the signal
889 * @column: the column on which the event occured.
891 * A button event occured on a row title button
893 sheet_signals[BUTTON_EVENT_ROW] =
894 g_signal_new ("button-event-row",
895 G_TYPE_FROM_CLASS (object_class),
899 gtkextra_VOID__INT_POINTER,
907 sheet_signals[SELECT_RANGE] =
908 g_signal_new ("select-range",
909 G_TYPE_FROM_CLASS (object_class),
911 offsetof (GtkSheetClass, select_range),
913 g_cclosure_marshal_VOID__BOXED,
916 GTK_TYPE_SHEET_RANGE);
919 sheet_signals[RESIZE_RANGE] =
920 g_signal_new ("resize-range",
921 G_TYPE_FROM_CLASS (object_class),
923 offsetof (GtkSheetClass, resize_range),
925 gtkextra_VOID__BOXED_BOXED,
928 GTK_TYPE_SHEET_RANGE, GTK_TYPE_SHEET_RANGE
931 sheet_signals[MOVE_RANGE] =
932 g_signal_new ("move-range",
933 G_TYPE_FROM_CLASS (object_class),
935 offsetof (GtkSheetClass, move_range),
937 gtkextra_VOID__BOXED_BOXED,
940 GTK_TYPE_SHEET_RANGE, GTK_TYPE_SHEET_RANGE
943 sheet_signals[TRAVERSE] =
944 g_signal_new ("traverse",
945 G_TYPE_FROM_CLASS (object_class),
947 offsetof (GtkSheetClass, traverse),
949 gtkextra_BOOLEAN__BOXED_POINTER,
955 sheet_signals[ACTIVATE] =
956 g_signal_new ("activate",
957 G_TYPE_FROM_CLASS (object_class),
959 offsetof (GtkSheetClass, activate),
961 gtkextra_VOID__INT_INT_INT_INT,
963 G_TYPE_INT, G_TYPE_INT,
964 G_TYPE_INT, G_TYPE_INT);
966 widget_class->set_scroll_adjustments_signal =
967 g_signal_new ("set-scroll-adjustments",
968 G_TYPE_FROM_CLASS (object_class),
970 offsetof (GtkSheetClass, set_scroll_adjustments),
972 gtkextra_VOID__OBJECT_OBJECT,
973 G_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
976 container_class->add = NULL;
977 container_class->remove = NULL;
978 container_class->forall = gtk_sheet_forall;
980 object_class->dispose = gtk_sheet_dispose;
981 object_class->finalize = gtk_sheet_finalize;
985 g_param_spec_pointer ("row-geometry",
987 "A pointer to the model of the row geometry",
988 G_PARAM_READABLE | G_PARAM_WRITABLE );
991 g_param_spec_pointer ("column-geometry",
993 "A pointer to the model of the column geometry",
994 G_PARAM_READABLE | G_PARAM_WRITABLE );
997 g_param_spec_pointer ("model",
999 "A pointer to the data model",
1000 G_PARAM_READABLE | G_PARAM_WRITABLE );
1003 object_class->set_property = gtk_sheet_set_property;
1004 object_class->get_property = gtk_sheet_get_property;
1006 g_object_class_install_property (object_class,
1010 g_object_class_install_property (object_class,
1014 g_object_class_install_property (object_class,
1019 widget_class->realize = gtk_sheet_realize;
1020 widget_class->unrealize = gtk_sheet_unrealize;
1021 widget_class->map = gtk_sheet_map;
1022 widget_class->unmap = gtk_sheet_unmap;
1023 widget_class->style_set = gtk_sheet_style_set;
1024 widget_class->button_press_event = gtk_sheet_button_press;
1025 widget_class->button_release_event = gtk_sheet_button_release;
1026 widget_class->motion_notify_event = gtk_sheet_motion;
1027 widget_class->enter_notify_event = gtk_sheet_crossing_notify;
1028 widget_class->leave_notify_event = gtk_sheet_crossing_notify;
1029 widget_class->key_press_event = gtk_sheet_key_press;
1030 widget_class->expose_event = gtk_sheet_expose;
1031 widget_class->size_request = gtk_sheet_size_request;
1032 widget_class->size_allocate = gtk_sheet_size_allocate;
1033 widget_class->focus_in_event = NULL;
1034 widget_class->focus_out_event = NULL;
1036 klass->set_scroll_adjustments = gtk_sheet_set_scroll_adjustments;
1037 klass->select_row = NULL;
1038 klass->select_column = NULL;
1039 klass->select_range = NULL;
1040 klass->resize_range = NULL;
1041 klass->move_range = NULL;
1042 klass->traverse = NULL;
1043 klass->activate = NULL;
1044 klass->changed = NULL;
1048 gtk_sheet_init (GtkSheet *sheet)
1050 sheet->model = NULL;
1051 sheet->column_geometry = NULL;
1052 sheet->row_geometry = NULL;
1055 sheet->selection_mode = GTK_SELECTION_NONE;
1056 sheet->state = GTK_SHEET_NORMAL;
1058 GTK_WIDGET_UNSET_FLAGS (sheet, GTK_NO_WINDOW);
1059 GTK_WIDGET_SET_FLAGS (sheet, GTK_CAN_FOCUS);
1061 sheet->column_title_window = NULL;
1062 sheet->column_title_area.x = 0;
1063 sheet->column_title_area.y = 0;
1064 sheet->column_title_area.width = 0;
1065 sheet->column_title_area.height = DEFAULT_ROW_HEIGHT;
1067 sheet->row_title_window = NULL;
1068 sheet->row_title_area.x = 0;
1069 sheet->row_title_area.y = 0;
1070 sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
1071 sheet->row_title_area.height = 0;
1074 sheet->active_cell.row = 0;
1075 sheet->active_cell.col = 0;
1076 sheet->selection_cell.row = 0;
1077 sheet->selection_cell.col = 0;
1079 sheet->range.row0 = 0;
1080 sheet->range.rowi = 0;
1081 sheet->range.col0 = 0;
1082 sheet->range.coli = 0;
1084 sheet->state = GTK_SHEET_NORMAL;
1086 sheet->sheet_window = NULL;
1087 sheet->entry_widget = NULL;
1088 sheet->entry_container = NULL;
1089 sheet->entry_handler_id = 0;
1090 sheet->button = NULL;
1092 sheet->hadjustment = NULL;
1093 sheet->vadjustment = NULL;
1095 sheet->cursor_drag = NULL;
1097 sheet->xor_gc = NULL;
1098 sheet->fg_gc = NULL;
1099 sheet->bg_gc = NULL;
1102 sheet->show_grid = TRUE;
1104 sheet->motion_timer = 0;
1106 sheet->columns_resizable = TRUE;
1107 sheet->rows_resizable = TRUE;
1109 sheet->row_titles_visible = TRUE;
1110 sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
1112 sheet->column_titles_visible = TRUE;
1115 /* create sheet entry */
1116 sheet->entry_type = 0;
1117 create_sheet_entry (sheet);
1119 /* create global selection button */
1120 create_global_button (sheet);
1124 /* Callback which occurs whenever columns are inserted / deleted in the model */
1126 columns_inserted_deleted_callback (GSheetModel *model, gint first_column,
1130 GtkSheet *sheet = GTK_SHEET (data);
1132 GtkSheetRange range;
1133 gint model_columns = g_sheet_model_get_column_count (model);
1136 /* Need to update all the columns starting from the first column and onwards.
1137 * Previous column are unchanged, so don't need to be updated.
1139 range.col0 = first_column;
1141 range.coli = g_sheet_column_get_column_count (sheet->column_geometry) - 1;
1142 range.rowi = g_sheet_row_get_row_count (sheet->row_geometry) - 1;
1144 adjust_scrollbars (sheet);
1146 if (sheet->active_cell.col >= model_columns)
1147 change_active_cell (sheet, sheet->active_cell.row, model_columns - 1);
1149 draw_column_title_buttons_range (sheet,
1150 first_column, max_visible_column (sheet));
1152 gtk_sheet_range_draw (sheet, &range);
1156 /* Callback which occurs whenever rows are inserted / deleted in the model */
1158 rows_inserted_deleted_callback (GSheetModel *model, gint first_row,
1159 gint n_rows, gpointer data)
1161 GtkSheet *sheet = GTK_SHEET (data);
1163 GtkSheetRange range;
1165 gint model_rows = g_sheet_model_get_row_count (model);
1167 /* Need to update all the rows starting from the first row and onwards.
1168 * Previous rows are unchanged, so don't need to be updated.
1170 range.row0 = first_row;
1172 range.rowi = g_sheet_row_get_row_count (sheet->row_geometry) - 1;
1173 range.coli = g_sheet_column_get_column_count (sheet->column_geometry) - 1;
1175 adjust_scrollbars (sheet);
1177 if (sheet->active_cell.row >= model_rows)
1178 change_active_cell (sheet, model_rows - 1, sheet->active_cell.col);
1180 draw_row_title_buttons_range (sheet, first_row, max_visible_row (sheet));
1182 gtk_sheet_range_draw (sheet, &range);
1186 If row0 or rowi are negative, then all rows will be updated.
1187 If col0 or coli are negative, then all columns will be updated.
1190 range_update_callback (GSheetModel *m, gint row0, gint col0,
1191 gint rowi, gint coli, gpointer data)
1193 GtkSheet *sheet = GTK_SHEET (data);
1195 GtkSheetRange range;
1202 if ( !GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1205 if ( max_visible_row (sheet) > g_sheet_model_get_row_count (sheet->model)
1207 max_visible_column (sheet) > g_sheet_model_get_column_count (sheet->model))
1209 gtk_sheet_moveto (sheet, 0, 0, 0, 0);
1212 if ( ( row0 < 0 && col0 < 0 ) || ( rowi < 0 && coli < 0 ) )
1214 gtk_sheet_range_draw (sheet, NULL);
1215 adjust_scrollbars (sheet);
1217 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
1218 max_visible_row (sheet));
1220 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
1221 max_visible_column (sheet));
1225 else if ( row0 < 0 || rowi < 0 )
1227 range.row0 = min_visible_row (sheet);
1228 range.rowi = max_visible_row (sheet);
1230 else if ( col0 < 0 || coli < 0 )
1232 range.col0 = min_visible_column (sheet);
1233 range.coli = max_visible_column (sheet);
1236 gtk_sheet_range_draw (sheet, &range);
1242 * @rows: initial number of rows
1243 * @columns: initial number of columns
1244 * @title: sheet title
1245 * @model: the model to use for the sheet data
1247 * Creates a new sheet widget with the given number of rows and columns.
1249 * Returns: the new sheet widget
1252 gtk_sheet_new (GSheetRow *vgeo, GSheetColumn *hgeo, GSheetModel *model)
1254 GtkWidget *widget = g_object_new (GTK_TYPE_SHEET,
1255 "row-geometry", vgeo,
1256 "column-geometry", hgeo,
1264 * gtk_sheet_set_model
1265 * @sheet: the sheet to set the model for
1266 * @model: the model to use for the sheet data
1268 * Sets the model for a GtkSheet
1272 gtk_sheet_set_model (GtkSheet *sheet, GSheetModel *model)
1274 g_return_if_fail (GTK_IS_SHEET (sheet));
1276 if (sheet->model ) g_object_unref (sheet->model);
1278 sheet->model = model;
1282 g_object_ref (model);
1284 g_signal_connect (model, "range_changed",
1285 G_CALLBACK (range_update_callback), sheet);
1287 g_signal_connect (model, "rows_inserted",
1288 G_CALLBACK (rows_inserted_deleted_callback), sheet);
1290 g_signal_connect (model, "rows_deleted",
1291 G_CALLBACK (rows_inserted_deleted_callback), sheet);
1293 g_signal_connect (model, "columns_inserted",
1294 G_CALLBACK (columns_inserted_deleted_callback), sheet);
1296 g_signal_connect (model, "columns_deleted",
1297 G_CALLBACK (columns_inserted_deleted_callback), sheet);
1303 gtk_sheet_change_entry (GtkSheet *sheet, GtkType entry_type)
1307 g_return_if_fail (sheet != NULL);
1308 g_return_if_fail (GTK_IS_SHEET (sheet));
1310 state = sheet->state;
1312 if (sheet->state == GTK_SHEET_NORMAL)
1313 gtk_sheet_hide_active_cell (sheet);
1315 sheet->entry_type = entry_type;
1317 create_sheet_entry (sheet);
1319 if (state == GTK_SHEET_NORMAL)
1321 gtk_sheet_show_active_cell (sheet);
1327 gtk_sheet_show_grid (GtkSheet *sheet, gboolean show)
1329 g_return_if_fail (sheet != NULL);
1330 g_return_if_fail (GTK_IS_SHEET (sheet));
1332 if (show == sheet->show_grid) return;
1334 sheet->show_grid = show;
1336 gtk_sheet_range_draw (sheet, NULL);
1340 gtk_sheet_grid_visible (GtkSheet *sheet)
1342 g_return_val_if_fail (sheet != NULL, 0);
1343 g_return_val_if_fail (GTK_IS_SHEET (sheet), 0);
1345 return sheet->show_grid;
1349 gtk_sheet_get_columns_count (GtkSheet *sheet)
1351 g_return_val_if_fail (sheet != NULL, 0);
1352 g_return_val_if_fail (GTK_IS_SHEET (sheet), 0);
1354 return g_sheet_column_get_column_count (sheet->column_geometry);
1358 gtk_sheet_set_column_width (GtkSheet *sheet,
1364 gtk_sheet_show_column_titles (GtkSheet *sheet)
1366 if (sheet->column_titles_visible) return;
1368 sheet->column_titles_visible = TRUE;
1370 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1373 gdk_window_show (sheet->column_title_window);
1374 gdk_window_move_resize (sheet->column_title_window,
1375 sheet->column_title_area.x,
1376 sheet->column_title_area.y,
1377 sheet->column_title_area.width,
1378 sheet->column_title_area.height);
1380 adjust_scrollbars (sheet);
1382 if (sheet->vadjustment)
1383 g_signal_emit_by_name (sheet->vadjustment,
1385 size_allocate_global_button (sheet);
1390 gtk_sheet_show_row_titles (GtkSheet *sheet)
1392 if (sheet->row_titles_visible) return;
1394 sheet->row_titles_visible = TRUE;
1397 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1399 gdk_window_show (sheet->row_title_window);
1400 gdk_window_move_resize (sheet->row_title_window,
1401 sheet->row_title_area.x,
1402 sheet->row_title_area.y,
1403 sheet->row_title_area.width,
1404 sheet->row_title_area.height);
1406 adjust_scrollbars (sheet);
1409 if (sheet->hadjustment)
1410 g_signal_emit_by_name (sheet->hadjustment,
1412 size_allocate_global_button (sheet);
1416 gtk_sheet_hide_column_titles (GtkSheet *sheet)
1418 if (!sheet->column_titles_visible) return;
1420 sheet->column_titles_visible = FALSE;
1422 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1424 if (sheet->column_title_window)
1425 gdk_window_hide (sheet->column_title_window);
1426 if (GTK_WIDGET_VISIBLE (sheet->button))
1427 gtk_widget_hide (sheet->button);
1429 adjust_scrollbars (sheet);
1432 if (sheet->vadjustment)
1433 g_signal_emit_by_name (sheet->vadjustment,
1438 gtk_sheet_hide_row_titles (GtkSheet *sheet)
1440 if (!sheet->row_titles_visible) return;
1442 sheet->row_titles_visible = FALSE;
1444 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1446 if (sheet->row_title_window)
1447 gdk_window_hide (sheet->row_title_window);
1449 if (GTK_WIDGET_VISIBLE (sheet->button))
1450 gtk_widget_hide (sheet->button);
1452 adjust_scrollbars (sheet);
1455 if (sheet->hadjustment)
1456 g_signal_emit_by_name (sheet->hadjustment,
1461 /* Scroll the sheet so that the cell ROW, COLUMN is visible.
1462 If {ROW,COL}_ALIGN is zero, then the cell will be placed
1463 at the {top,left} of the sheet. If it's 1, then it'll
1464 be placed at the {bottom,right}.
1465 ROW or COL may be -1, in which case scrolling in that dimension
1469 gtk_sheet_moveto (GtkSheet *sheet,
1477 g_return_if_fail (row_align >= 0);
1478 g_return_if_fail (col_align >= 0);
1480 g_return_if_fail (row_align <= 1);
1481 g_return_if_fail (col_align <= 1);
1483 g_return_if_fail (col <
1484 g_sheet_column_get_column_count (sheet->column_geometry));
1485 g_return_if_fail (row <
1486 g_sheet_row_get_row_count (sheet->row_geometry));
1488 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
1493 gint y = g_sheet_row_start_pixel (sheet->row_geometry, row);
1495 gtk_adjustment_set_value (sheet->vadjustment, y - height * row_align);
1501 gint x = g_sheet_column_start_pixel (sheet->column_geometry, col);
1503 gtk_adjustment_set_value (sheet->hadjustment, x - width * col_align);
1509 gtk_sheet_columns_resizable (GtkSheet *sheet)
1511 g_return_val_if_fail (sheet != NULL, FALSE);
1512 g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE);
1514 return sheet->columns_resizable;
1519 gtk_sheet_rows_resizable (GtkSheet *sheet)
1521 g_return_val_if_fail (sheet != NULL, FALSE);
1522 g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE);
1524 return sheet->rows_resizable;
1529 gtk_sheet_select_row (GtkSheet *sheet, gint row)
1531 g_return_if_fail (sheet != NULL);
1532 g_return_if_fail (GTK_IS_SHEET (sheet));
1534 if (row < 0 || row >= g_sheet_row_get_row_count (sheet->row_geometry))
1537 if (sheet->state != GTK_SHEET_NORMAL)
1538 gtk_sheet_real_unselect_range (sheet, NULL);
1540 sheet->state = GTK_SHEET_ROW_SELECTED;
1541 sheet->range.row0 = row;
1542 sheet->range.col0 = 0;
1543 sheet->range.rowi = row;
1544 sheet->range.coli = g_sheet_column_get_column_count (sheet->column_geometry) - 1;
1545 sheet->active_cell.row = row;
1546 sheet->active_cell.col = 0;
1548 g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, row);
1549 gtk_sheet_real_select_range (sheet, NULL);
1554 gtk_sheet_select_column (GtkSheet *sheet, gint column)
1556 g_return_if_fail (sheet != NULL);
1557 g_return_if_fail (GTK_IS_SHEET (sheet));
1559 if (column < 0 || column >= g_sheet_column_get_column_count (sheet->column_geometry))
1562 if (sheet->state != GTK_SHEET_NORMAL)
1563 gtk_sheet_real_unselect_range (sheet, NULL);
1565 sheet->state = GTK_SHEET_COLUMN_SELECTED;
1566 sheet->range.row0 = 0;
1567 sheet->range.col0 = column;
1568 sheet->range.rowi = g_sheet_row_get_row_count (sheet->row_geometry) - 1;
1569 sheet->range.coli = column;
1570 sheet->active_cell.row = 0;
1571 sheet->active_cell.col = column;
1573 g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, column);
1574 gtk_sheet_real_select_range (sheet, NULL);
1581 gtk_sheet_range_isvisible (const GtkSheet *sheet,
1582 GtkSheetRange range)
1584 g_return_val_if_fail (sheet != NULL, FALSE);
1586 if (range.row0 < 0 || range.row0 >= g_sheet_row_get_row_count (sheet->row_geometry))
1589 if (range.rowi < 0 || range.rowi >= g_sheet_row_get_row_count (sheet->row_geometry))
1592 if (range.col0 < 0 || range.col0 >= g_sheet_column_get_column_count (sheet->column_geometry))
1595 if (range.coli < 0 || range.coli >= g_sheet_column_get_column_count (sheet->column_geometry))
1598 if (range.rowi < min_visible_row (sheet))
1601 if (range.row0 > max_visible_row (sheet))
1604 if (range.coli < min_visible_column (sheet))
1607 if (range.col0 > max_visible_column (sheet))
1614 gtk_sheet_cell_isvisible (GtkSheet *sheet,
1615 gint row, gint column)
1617 GtkSheetRange range;
1620 range.col0 = column;
1622 range.coli = column;
1624 return gtk_sheet_range_isvisible (sheet, range);
1628 gtk_sheet_get_visible_range (GtkSheet *sheet, GtkSheetRange *range)
1630 g_return_if_fail (sheet != NULL);
1631 g_return_if_fail (GTK_IS_SHEET (sheet)) ;
1632 g_return_if_fail (range != NULL);
1634 range->row0 = min_visible_row (sheet);
1635 range->col0 = min_visible_column (sheet);
1636 range->rowi = max_visible_row (sheet);
1637 range->coli = max_visible_column (sheet);
1642 gtk_sheet_set_scroll_adjustments (GtkSheet *sheet,
1643 GtkAdjustment *hadjustment,
1644 GtkAdjustment *vadjustment)
1646 if ( sheet->vadjustment != vadjustment )
1648 if (sheet->vadjustment)
1649 g_object_unref (sheet->vadjustment);
1650 sheet->vadjustment = vadjustment;
1651 g_object_ref (vadjustment);
1653 g_signal_connect (sheet->vadjustment, "value_changed",
1654 G_CALLBACK (vadjustment_value_changed),
1658 if ( sheet->hadjustment != hadjustment )
1660 if (sheet->hadjustment)
1661 g_object_unref (sheet->hadjustment);
1662 sheet->hadjustment = hadjustment;
1663 g_object_ref (hadjustment);
1665 g_signal_connect (sheet->hadjustment, "value_changed",
1666 G_CALLBACK (hadjustment_value_changed),
1672 gtk_sheet_finalize (GObject *object)
1676 g_return_if_fail (object != NULL);
1677 g_return_if_fail (GTK_IS_SHEET (object));
1679 sheet = GTK_SHEET (object);
1681 if (G_OBJECT_CLASS (parent_class)->finalize)
1682 (*G_OBJECT_CLASS (parent_class)->finalize) (object);
1686 gtk_sheet_dispose (GObject *object)
1688 GtkSheet *sheet = GTK_SHEET (object);
1690 g_return_if_fail (object != NULL);
1691 g_return_if_fail (GTK_IS_SHEET (object));
1693 if ( sheet->dispose_has_run )
1696 sheet->dispose_has_run = TRUE;
1698 if (sheet->model) g_object_unref (sheet->model);
1699 if (sheet->row_geometry) g_object_unref (sheet->row_geometry);
1700 if (sheet->column_geometry) g_object_unref (sheet->column_geometry);
1702 g_object_unref (sheet->entry_container);
1703 sheet->entry_container = NULL;
1705 g_object_unref (sheet->button);
1706 sheet->button = NULL;
1708 /* unref adjustments */
1709 if (sheet->hadjustment)
1711 g_signal_handlers_disconnect_matched (sheet->hadjustment,
1712 G_SIGNAL_MATCH_DATA,
1716 g_object_unref (sheet->hadjustment);
1717 sheet->hadjustment = NULL;
1720 if (sheet->vadjustment)
1722 g_signal_handlers_disconnect_matched (sheet->vadjustment,
1723 G_SIGNAL_MATCH_DATA,
1727 g_object_unref (sheet->vadjustment);
1729 sheet->vadjustment = NULL;
1732 if (G_OBJECT_CLASS (parent_class)->dispose)
1733 (*G_OBJECT_CLASS (parent_class)->dispose) (object);
1737 gtk_sheet_style_set (GtkWidget *widget,
1738 GtkStyle *previous_style)
1742 g_return_if_fail (widget != NULL);
1743 g_return_if_fail (GTK_IS_SHEET (widget));
1745 if (GTK_WIDGET_CLASS (parent_class)->style_set)
1746 (*GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous_style);
1748 sheet = GTK_SHEET (widget);
1750 if (GTK_WIDGET_REALIZED (widget))
1752 gtk_style_set_background (widget->style, widget->window, widget->state);
1758 gtk_sheet_realize (GtkWidget *widget)
1761 GdkWindowAttr attributes;
1762 const gint attributes_mask =
1763 GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_CURSOR;
1765 GdkGCValues values, auxvalues;
1766 GdkColormap *colormap;
1767 GdkDisplay *display;
1769 g_return_if_fail (widget != NULL);
1770 g_return_if_fail (GTK_IS_SHEET (widget));
1772 sheet = GTK_SHEET (widget);
1774 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1776 colormap = gtk_widget_get_colormap (widget);
1777 display = gtk_widget_get_display (widget);
1779 attributes.window_type = GDK_WINDOW_CHILD;
1780 attributes.x = widget->allocation.x;
1781 attributes.y = widget->allocation.y;
1782 attributes.width = widget->allocation.width;
1783 attributes.height = widget->allocation.height;
1784 attributes.wclass = GDK_INPUT_OUTPUT;
1786 attributes.visual = gtk_widget_get_visual (widget);
1787 attributes.colormap = colormap;
1789 attributes.event_mask = gtk_widget_get_events (widget);
1790 attributes.event_mask |= (GDK_EXPOSURE_MASK |
1791 GDK_BUTTON_PRESS_MASK |
1792 GDK_BUTTON_RELEASE_MASK |
1793 GDK_KEY_PRESS_MASK |
1794 GDK_ENTER_NOTIFY_MASK |
1795 GDK_LEAVE_NOTIFY_MASK |
1796 GDK_POINTER_MOTION_MASK |
1797 GDK_POINTER_MOTION_HINT_MASK);
1799 attributes.cursor = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
1802 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1804 gdk_window_set_user_data (widget->window, sheet);
1806 widget->style = gtk_style_attach (widget->style, widget->window);
1808 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1810 gdk_color_parse ("white", &sheet->color[BG_COLOR]);
1811 gdk_colormap_alloc_color (colormap, &sheet->color[BG_COLOR], FALSE,
1813 gdk_color_parse ("gray", &sheet->color[GRID_COLOR]);
1814 gdk_colormap_alloc_color (colormap, &sheet->color[GRID_COLOR], FALSE,
1819 attributes.width = sheet->column_title_area.width;
1820 attributes.height = sheet->column_title_area.height;
1823 /* column - title window */
1824 sheet->column_title_window =
1825 gdk_window_new (widget->window, &attributes, attributes_mask);
1826 gdk_window_set_user_data (sheet->column_title_window, sheet);
1827 gtk_style_set_background (widget->style, sheet->column_title_window,
1833 attributes.width = sheet->row_title_area.width;
1834 attributes.height = sheet->row_title_area.height;
1836 /* row - title window */
1837 sheet->row_title_window = gdk_window_new (widget->window,
1838 &attributes, attributes_mask);
1839 gdk_window_set_user_data (sheet->row_title_window, sheet);
1840 gtk_style_set_background (widget->style, sheet->row_title_window,
1843 /* sheet - window */
1844 attributes.cursor = gdk_cursor_new_for_display (display, GDK_PLUS);
1849 sheet->sheet_window = gdk_window_new (widget->window,
1850 &attributes, attributes_mask);
1851 gdk_window_set_user_data (sheet->sheet_window, sheet);
1853 gdk_cursor_unref (attributes.cursor);
1855 gdk_window_set_background (sheet->sheet_window, &widget->style->white);
1856 gdk_window_show (sheet->sheet_window);
1859 sheet->fg_gc = gdk_gc_new (widget->window);
1860 sheet->bg_gc = gdk_gc_new (widget->window);
1862 gdk_gc_get_values (sheet->fg_gc, &auxvalues);
1864 values.foreground = widget->style->white;
1865 values.function = GDK_INVERT;
1866 values.subwindow_mode = GDK_INCLUDE_INFERIORS;
1867 values.line_width = 3;
1869 sheet->xor_gc = gdk_gc_new_with_values (widget->window,
1878 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
1879 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
1881 gtk_widget_set_parent_window (sheet->button, sheet->sheet_window);
1882 gtk_widget_set_parent (sheet->button, GTK_WIDGET (sheet));
1885 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
1887 if (sheet->column_titles_visible)
1888 gdk_window_show (sheet->column_title_window);
1889 if (sheet->row_titles_visible)
1890 gdk_window_show (sheet->row_title_window);
1892 sheet->hover_window = create_hover_window ();
1894 draw_row_title_buttons (sheet);
1895 draw_column_title_buttons (sheet);
1897 gtk_sheet_update_primary_selection (sheet);
1901 create_global_button (GtkSheet *sheet)
1903 sheet->button = gtk_button_new_with_label (" ");
1905 g_object_ref_sink (sheet->button);
1907 g_signal_connect (sheet->button,
1909 G_CALLBACK (global_button_clicked),
1914 size_allocate_global_button (GtkSheet *sheet)
1916 GtkAllocation allocation;
1918 if (!sheet->column_titles_visible) return;
1919 if (!sheet->row_titles_visible) return;
1921 gtk_widget_size_request (sheet->button, NULL);
1925 allocation.width = sheet->row_title_area.width;
1926 allocation.height = sheet->column_title_area.height;
1928 gtk_widget_size_allocate (sheet->button, &allocation);
1929 gtk_widget_show (sheet->button);
1933 global_button_clicked (GtkWidget *widget, gpointer data)
1935 gtk_sheet_click_cell (GTK_SHEET (data), -1, -1);
1936 gtk_widget_grab_focus (GTK_WIDGET (data));
1941 gtk_sheet_unrealize (GtkWidget *widget)
1945 g_return_if_fail (widget != NULL);
1946 g_return_if_fail (GTK_IS_SHEET (widget));
1948 sheet = GTK_SHEET (widget);
1950 gdk_cursor_unref (sheet->cursor_drag);
1951 sheet->cursor_drag = NULL;
1953 gdk_colormap_free_colors (gtk_widget_get_colormap (widget),
1954 sheet->color, n_COLORS);
1956 g_object_unref (sheet->xor_gc);
1957 g_object_unref (sheet->fg_gc);
1958 g_object_unref (sheet->bg_gc);
1960 destroy_hover_window (sheet->hover_window);
1962 gdk_window_destroy (sheet->sheet_window);
1963 gdk_window_destroy (sheet->column_title_window);
1964 gdk_window_destroy (sheet->row_title_window);
1966 gtk_widget_unparent (sheet->entry_widget);
1967 if (sheet->button != NULL)
1968 gtk_widget_unparent (sheet->button);
1970 if (GTK_WIDGET_CLASS (parent_class)->unrealize)
1971 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
1975 gtk_sheet_map (GtkWidget *widget)
1977 GtkSheet *sheet = GTK_SHEET (widget);
1979 g_return_if_fail (widget != NULL);
1980 g_return_if_fail (GTK_IS_SHEET (widget));
1982 if (!GTK_WIDGET_MAPPED (widget))
1984 GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
1986 gdk_window_show (widget->window);
1987 gdk_window_show (sheet->sheet_window);
1989 if (sheet->column_titles_visible)
1991 draw_column_title_buttons (sheet);
1992 gdk_window_show (sheet->column_title_window);
1994 if (sheet->row_titles_visible)
1996 draw_row_title_buttons (sheet);
1997 gdk_window_show (sheet->row_title_window);
2000 if (!GTK_WIDGET_MAPPED (sheet->entry_widget)
2001 && sheet->active_cell.row >= 0
2002 && sheet->active_cell.col >= 0 )
2004 gtk_widget_show (sheet->entry_widget);
2005 gtk_widget_map (sheet->entry_widget);
2008 if (GTK_WIDGET_VISIBLE (sheet->button) &&
2009 !GTK_WIDGET_MAPPED (sheet->button))
2011 gtk_widget_show (sheet->button);
2012 gtk_widget_map (sheet->button);
2015 if (GTK_BIN (sheet->button)->child)
2016 if (GTK_WIDGET_VISIBLE (GTK_BIN (sheet->button)->child) &&
2017 !GTK_WIDGET_MAPPED (GTK_BIN (sheet->button)->child))
2018 gtk_widget_map (GTK_BIN (sheet->button)->child);
2020 gtk_sheet_range_draw (sheet, NULL);
2021 change_active_cell (sheet,
2022 sheet->active_cell.row,
2023 sheet->active_cell.col);
2028 gtk_sheet_unmap (GtkWidget *widget)
2030 GtkSheet *sheet = GTK_SHEET (widget);
2032 if (!GTK_WIDGET_MAPPED (widget))
2035 GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
2037 gdk_window_hide (sheet->sheet_window);
2038 if (sheet->column_titles_visible)
2039 gdk_window_hide (sheet->column_title_window);
2040 if (sheet->row_titles_visible)
2041 gdk_window_hide (sheet->row_title_window);
2042 gdk_window_hide (widget->window);
2044 if (GTK_WIDGET_MAPPED (sheet->entry_widget))
2045 gtk_widget_unmap (sheet->entry_widget);
2047 if (GTK_WIDGET_MAPPED (sheet->button))
2048 gtk_widget_unmap (sheet->button);
2053 gtk_sheet_cell_draw (GtkSheet *sheet, gint row, gint col)
2055 PangoLayout *layout;
2056 PangoRectangle text;
2061 GtkSheetCellAttr attributes;
2064 g_return_if_fail (sheet != NULL);
2066 /* bail now if we aren't yet drawable */
2067 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
2070 row >= g_sheet_row_get_row_count (sheet->row_geometry))
2074 col >= g_sheet_column_get_column_count (sheet->column_geometry))
2077 gtk_sheet_get_attributes (sheet, row, col, &attributes);
2079 /* select GC for background rectangle */
2080 gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground);
2081 gdk_gc_set_foreground (sheet->bg_gc, &attributes.background);
2083 rectangle_from_cell (sheet, row, col, &area);
2085 gdk_gc_set_line_attributes (sheet->fg_gc, 1, 0, 0, 0);
2087 if (sheet->show_grid)
2089 gdk_gc_set_foreground (sheet->bg_gc, &sheet->color[GRID_COLOR]);
2091 gdk_draw_rectangle (sheet->sheet_window,
2095 area.width, area.height);
2098 // gtk_sheet_cell_draw_label (sheet, row, col);
2101 label = gtk_sheet_cell_get_text (sheet, row, col);
2106 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), label);
2107 dispose_string (sheet, label);
2108 pango_layout_set_font_description (layout, attributes.font_desc);
2111 pango_layout_get_pixel_extents (layout, NULL, &text);
2113 gdk_gc_set_clip_rectangle (sheet->fg_gc, &area);
2115 font_height = pango_font_description_get_size (attributes.font_desc);
2116 if ( !pango_font_description_get_size_is_absolute (attributes.font_desc))
2117 font_height /= PANGO_SCALE;
2119 /* Centre the text vertically */
2120 area.y += (area.height - font_height) / 2.0;
2122 switch (attributes.justification)
2124 case GTK_JUSTIFY_RIGHT:
2125 area.x += area.width - text.width;
2127 case GTK_JUSTIFY_CENTER:
2128 area.x += (area.width - text.width) / 2.0;
2130 case GTK_JUSTIFY_LEFT:
2134 g_critical ("Unhandled justification %d in column %d\n",
2135 attributes.justification, col);
2139 gdk_draw_layout (sheet->sheet_window, sheet->fg_gc,
2144 gdk_gc_set_clip_rectangle (sheet->fg_gc, NULL);
2145 g_object_unref (layout);
2151 gtk_sheet_range_draw (GtkSheet *sheet, const GtkSheetRange *range)
2156 GtkSheetRange drawing_range;
2158 g_return_if_fail (sheet != NULL);
2159 g_return_if_fail (GTK_SHEET (sheet));
2161 if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (sheet))) return;
2162 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2163 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
2167 drawing_range.row0 = min_visible_row (sheet);
2168 drawing_range.col0 = min_visible_column (sheet);
2169 drawing_range.rowi = MIN (max_visible_row (sheet),
2170 g_sheet_row_get_row_count (sheet->row_geometry) - 1);
2171 drawing_range.coli = max_visible_column (sheet);
2172 gdk_drawable_get_size (sheet->sheet_window, &area.width, &area.height);
2173 area.x = area.y = 0;
2177 drawing_range.row0 = MAX (range->row0, min_visible_row (sheet));
2178 drawing_range.col0 = MAX (range->col0, min_visible_column (sheet));
2179 drawing_range.rowi = MIN (range->rowi, max_visible_row (sheet));
2180 drawing_range.coli = MIN (range->coli, max_visible_column (sheet));
2183 rectangle_from_range (sheet, &drawing_range, &area);
2187 gdk_draw_rectangle (sheet->sheet_window,
2188 GTK_WIDGET (sheet)->style->white_gc,
2191 area.width, area.height);
2193 for (i = drawing_range.row0; i <= drawing_range.rowi; i++)
2194 for (j = drawing_range.col0; j <= drawing_range.coli; j++)
2196 gtk_sheet_cell_draw (sheet, i, j);
2199 if (sheet->state != GTK_SHEET_NORMAL &&
2200 gtk_sheet_range_isvisible (sheet, sheet->range))
2201 gtk_sheet_range_draw_selection (sheet, drawing_range);
2203 if (sheet->state == GTK_STATE_NORMAL &&
2204 sheet->active_cell.row >= drawing_range.row0 &&
2205 sheet->active_cell.row <= drawing_range.rowi &&
2206 sheet->active_cell.col >= drawing_range.col0 &&
2207 sheet->active_cell.col <= drawing_range.coli)
2208 gtk_sheet_show_active_cell (sheet);
2212 gtk_sheet_range_draw_selection (GtkSheet *sheet, GtkSheetRange range)
2218 if (range.col0 > sheet->range.coli || range.coli < sheet->range.col0 ||
2219 range.row0 > sheet->range.rowi || range.rowi < sheet->range.row0)
2222 if (!gtk_sheet_range_isvisible (sheet, range)) return;
2223 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2227 range.col0 = MAX (sheet->range.col0, range.col0);
2228 range.coli = MIN (sheet->range.coli, range.coli);
2229 range.row0 = MAX (sheet->range.row0, range.row0);
2230 range.rowi = MIN (sheet->range.rowi, range.rowi);
2232 range.col0 = MAX (range.col0, min_visible_column (sheet));
2233 range.coli = MIN (range.coli, max_visible_column (sheet));
2234 range.row0 = MAX (range.row0, min_visible_row (sheet));
2235 range.rowi = MIN (range.rowi, max_visible_row (sheet));
2237 for (i = range.row0; i <= range.rowi; i++)
2239 for (j = range.col0; j <= range.coli; j++)
2241 if (gtk_sheet_cell_get_state (sheet, i, j) == GTK_STATE_SELECTED)
2243 rectangle_from_cell (sheet, i, j, &area);
2245 if (i == sheet->range.row0)
2247 area.y = area.y + 2;
2248 area.height = area.height - 2;
2250 if (i == sheet->range.rowi) area.height = area.height - 3;
2251 if (j == sheet->range.col0)
2253 area.x = area.x + 2;
2254 area.width = area.width - 2;
2256 if (j == sheet->range.coli) area.width = area.width - 3;
2258 if (i != sheet->active_cell.row || j != sheet->active_cell.col)
2260 gdk_draw_rectangle (sheet->sheet_window,
2263 area.x + 1, area.y + 1,
2264 area.width, area.height);
2271 gtk_sheet_draw_border (sheet, sheet->range);
2274 static void gtk_sheet_set_cell (GtkSheet *sheet, gint row, gint col,
2275 GtkJustification justification,
2280 safe_strcmp (const gchar *s1, const gchar *s2)
2282 if ( !s1 && !s2) return 0;
2283 if ( !s1) return - 1;
2284 if ( !s2) return +1;
2285 return strcmp (s1, s2);
2289 gtk_sheet_set_cell (GtkSheet *sheet, gint row, gint col,
2290 GtkJustification justification,
2293 GSheetModel *model ;
2296 g_return_if_fail (sheet != NULL);
2297 g_return_if_fail (GTK_IS_SHEET (sheet));
2299 if (col >= g_sheet_column_get_column_count (sheet->column_geometry)
2300 || row >= g_sheet_row_get_row_count (sheet->row_geometry))
2303 if (col < 0 || row < 0) return;
2305 model = gtk_sheet_get_model (sheet);
2307 old_text = g_sheet_model_get_string (model, row, col);
2309 if ( g_sheet_model_free_strings (model))
2315 gtk_sheet_cell_clear (GtkSheet *sheet, gint row, gint column)
2317 GtkSheetRange range;
2319 g_return_if_fail (sheet != NULL);
2320 g_return_if_fail (GTK_IS_SHEET (sheet));
2321 if (column >= g_sheet_column_get_column_count (sheet->column_geometry) ||
2322 row >= g_sheet_row_get_row_count (sheet->row_geometry)) return;
2324 if (column < 0 || row < 0) return;
2328 range.col0 = min_visible_column (sheet);
2329 range.coli = max_visible_column (sheet);
2331 gtk_sheet_real_cell_clear (sheet, row, column);
2333 gtk_sheet_range_draw (sheet, &range);
2337 gtk_sheet_real_cell_clear (GtkSheet *sheet, gint row, gint column)
2339 GSheetModel *model = gtk_sheet_get_model (sheet);
2341 gchar *old_text = gtk_sheet_cell_get_text (sheet, row, column);
2343 if (old_text && strlen (old_text) > 0 )
2345 g_sheet_model_datum_clear (model, row, column);
2348 dispose_string (sheet, old_text);
2352 gtk_sheet_cell_get_text (const GtkSheet *sheet, gint row, gint col)
2355 g_return_val_if_fail (sheet != NULL, NULL);
2356 g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);
2358 if (col >= g_sheet_column_get_column_count (sheet->column_geometry) || row >= g_sheet_row_get_row_count (sheet->row_geometry))
2360 if (col < 0 || row < 0) return NULL;
2362 model = gtk_sheet_get_model (sheet);
2367 return g_sheet_model_get_string (model, row, col);
2372 gtk_sheet_cell_get_state (GtkSheet *sheet, gint row, gint col)
2375 GtkSheetRange *range;
2377 g_return_val_if_fail (sheet != NULL, 0);
2378 g_return_val_if_fail (GTK_IS_SHEET (sheet), 0);
2379 if (col >= g_sheet_column_get_column_count (sheet->column_geometry) || row >= g_sheet_row_get_row_count (sheet->row_geometry)) return 0;
2380 if (col < 0 || row < 0) return 0;
2382 state = sheet->state;
2383 range = &sheet->range;
2387 case GTK_SHEET_NORMAL:
2388 return GTK_STATE_NORMAL;
2390 case GTK_SHEET_ROW_SELECTED:
2391 if (row >= range->row0 && row <= range->rowi)
2392 return GTK_STATE_SELECTED;
2394 case GTK_SHEET_COLUMN_SELECTED:
2395 if (col >= range->col0 && col <= range->coli)
2396 return GTK_STATE_SELECTED;
2398 case GTK_SHEET_RANGE_SELECTED:
2399 if (row >= range->row0 && row <= range->rowi && \
2400 col >= range->col0 && col <= range->coli)
2401 return GTK_STATE_SELECTED;
2404 return GTK_STATE_NORMAL;
2407 /* Convert X, Y (in pixels) to *ROW, *COLUMN
2408 If the function returns FALSE, then the results will be unreliable.
2411 gtk_sheet_get_pixel_info (GtkSheet *sheet,
2419 *column = -G_MAXINT;
2421 g_return_val_if_fail (sheet != NULL, 0);
2422 g_return_val_if_fail (GTK_IS_SHEET (sheet), 0);
2424 /* bounds checking, return false if the user clicked
2432 if ( sheet->column_titles_visible)
2433 y -= sheet->column_title_area.height;
2435 y += sheet->vadjustment->value;
2437 if ( y < 0 && sheet->column_titles_visible)
2443 trow = yyy_row_ypixel_to_row (sheet, y);
2444 if (trow > g_sheet_row_get_row_count (sheet->row_geometry))
2450 if ( sheet->row_titles_visible)
2451 x -= sheet->row_title_area.width;
2453 x += sheet->hadjustment->value;
2455 if ( x < 0 && sheet->row_titles_visible)
2461 tcol = column_from_xpixel (sheet, x);
2462 if (tcol > g_sheet_column_get_column_count (sheet->column_geometry))
2472 gtk_sheet_get_cell_area (GtkSheet *sheet,
2477 g_return_val_if_fail (sheet != NULL, 0);
2478 g_return_val_if_fail (GTK_IS_SHEET (sheet), 0);
2480 if (row >= g_sheet_row_get_row_count (sheet->row_geometry) || column >= g_sheet_column_get_column_count (sheet->column_geometry))
2483 area->x = (column == -1) ? 0 : g_sheet_column_start_pixel (sheet->column_geometry, column);
2484 area->y = (row == -1) ? 0 : g_sheet_row_start_pixel (sheet->row_geometry, row);
2486 area->width= (column == -1) ? sheet->row_title_area.width
2487 : g_sheet_column_get_width (sheet->column_geometry, column);
2489 area->height= (row == -1) ? sheet->column_title_area.height
2490 : g_sheet_row_get_height (sheet->row_geometry, row);
2496 gtk_sheet_set_active_cell (GtkSheet *sheet, gint row, gint col)
2498 g_return_if_fail (sheet != NULL);
2499 g_return_if_fail (GTK_IS_SHEET (sheet));
2501 if (row < -1 || col < -1)
2504 if (row >= g_sheet_row_get_row_count (sheet->row_geometry)
2506 col >= g_sheet_column_get_column_count (sheet->column_geometry))
2509 sheet->active_cell.row = row;
2510 sheet->active_cell.col = col;
2512 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2515 if ( row == -1 || col == -1)
2517 gtk_sheet_hide_active_cell (sheet);
2521 change_active_cell (sheet, row, col);
2525 gtk_sheet_get_active_cell (GtkSheet *sheet, gint *row, gint *column)
2527 g_return_if_fail (sheet != NULL);
2528 g_return_if_fail (GTK_IS_SHEET (sheet));
2530 if ( row ) *row = sheet->active_cell.row;
2531 if (column) *column = sheet->active_cell.col;
2535 gtk_sheet_entry_changed (GtkWidget *widget, gpointer data)
2540 GtkJustification justification;
2541 GtkSheetCellAttr attributes;
2543 g_return_if_fail (data != NULL);
2544 g_return_if_fail (GTK_IS_SHEET (data));
2546 sheet = GTK_SHEET (data);
2548 if (!GTK_WIDGET_VISIBLE (widget)) return;
2549 if (sheet->state != GTK_STATE_NORMAL) return;
2551 row = sheet->active_cell.row;
2552 col = sheet->active_cell.col;
2554 if (row < 0 || col < 0) return;
2556 sheet->active_cell.row = -1;
2557 sheet->active_cell.col = -1;
2559 text = gtk_entry_get_text (GTK_ENTRY (gtk_sheet_get_entry (sheet)));
2561 if (text && strlen (text) > 0)
2563 gtk_sheet_get_attributes (sheet, row, col, &attributes);
2564 justification = attributes.justification;
2565 gtk_sheet_set_cell (sheet, row, col, justification, text);
2568 sheet->active_cell.row = row;;
2569 sheet->active_cell.col = col;
2574 gtk_sheet_hide_active_cell (GtkSheet *sheet)
2578 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2581 if (sheet->active_cell.row < 0 ||
2582 sheet->active_cell.col < 0) return;
2584 gtk_widget_hide (sheet->entry_widget);
2585 gtk_widget_unmap (sheet->entry_widget);
2587 rectangle_from_cell (sheet,
2588 sheet->active_cell.row, sheet->active_cell.col,
2591 gdk_draw_rectangle (sheet->sheet_window,
2592 GTK_WIDGET (sheet)->style->white_gc,
2595 area.width, area.height);
2597 gtk_sheet_cell_draw (sheet, sheet->active_cell.row,
2598 sheet->active_cell.col);
2600 GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2604 change_active_cell (GtkSheet *sheet, gint row, gint col)
2606 gint old_row, old_col;
2607 glong old_handler_id = sheet->entry_handler_id;
2609 g_return_if_fail (GTK_IS_SHEET (sheet));
2611 if (row < 0 || col < 0)
2614 if ( row > g_sheet_row_get_row_count (sheet->row_geometry)
2615 || col > g_sheet_column_get_column_count (sheet->column_geometry))
2618 if (sheet->state != GTK_SHEET_NORMAL)
2620 sheet->state = GTK_SHEET_NORMAL;
2621 gtk_sheet_real_unselect_range (sheet, NULL);
2624 g_signal_handler_block (sheet->entry_widget, sheet->entry_handler_id);
2626 old_row = sheet->active_cell.row;
2627 old_col = sheet->active_cell.col;
2629 sheet->range.row0 = row;
2630 sheet->range.col0 = col;
2631 sheet->range.rowi = row;
2632 sheet->range.coli = col;
2633 sheet->active_cell.row = row;
2634 sheet->active_cell.col = col;
2635 sheet->selection_cell.row = row;
2636 sheet->selection_cell.col = col;
2638 GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
2640 gtk_sheet_show_active_cell (sheet);
2643 g_signal_emit (sheet, sheet_signals [ACTIVATE], 0,
2644 row, col, old_row, old_col);
2646 if ( old_handler_id == sheet->entry_handler_id)
2647 g_signal_handler_unblock (sheet->entry_widget, sheet->entry_handler_id);
2651 gtk_sheet_show_active_cell (GtkSheet *sheet)
2653 GtkEntry *sheet_entry;
2654 GtkSheetCellAttr attributes;
2658 g_return_if_fail (sheet != NULL);
2659 g_return_if_fail (GTK_IS_SHEET (sheet));
2661 row = sheet->active_cell.row;
2662 col = sheet->active_cell.col;
2664 /* Don't show the active cell, if there is no active cell: */
2665 if (! (row >= 0 && col >= 0)) /* e.g row or coll == -1. */
2668 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2669 if (sheet->state != GTK_SHEET_NORMAL) return;
2670 if (GTK_SHEET_IN_SELECTION (sheet)) return;
2672 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2674 sheet_entry = GTK_ENTRY (gtk_sheet_get_entry (sheet));
2676 gtk_sheet_get_attributes (sheet, row, col, &attributes);
2679 text = gtk_sheet_cell_get_text (sheet, row, col);
2681 text = g_strdup ("");
2683 gtk_entry_set_visibility (GTK_ENTRY (sheet_entry), attributes.is_visible);
2686 if ( GTK_IS_ENTRY (sheet_entry))
2688 const gchar *old_text = gtk_entry_get_text (GTK_ENTRY (sheet_entry));
2689 if (strcmp (old_text, text) != 0)
2690 gtk_entry_set_text (GTK_ENTRY (sheet_entry), text);
2692 switch (attributes.justification)
2694 case GTK_JUSTIFY_RIGHT:
2695 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 1.0);
2697 case GTK_JUSTIFY_CENTER:
2698 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.5);
2700 case GTK_JUSTIFY_LEFT:
2702 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.0);
2707 gtk_sheet_size_allocate_entry (sheet);
2709 gtk_widget_map (sheet->entry_widget);
2711 gtk_widget_grab_focus (GTK_WIDGET (sheet_entry));
2713 dispose_string (sheet, text);
2718 gtk_sheet_draw_active_cell (GtkSheet *sheet)
2721 GtkSheetRange range;
2723 if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (sheet))) return;
2724 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2726 row = sheet->active_cell.row;
2727 col = sheet->active_cell.col;
2729 if (row < 0 || col < 0) return;
2731 if (!gtk_sheet_cell_isvisible (sheet, row, col))
2734 range.col0 = range.coli = col;
2735 range.row0 = range.rowi = row;
2737 gtk_sheet_draw_border (sheet, range);
2743 gtk_sheet_new_selection (GtkSheet *sheet, GtkSheetRange *range)
2745 gint i, j, mask1, mask2;
2746 gint state, selected;
2747 gint x, y, width, height;
2748 GtkSheetRange new_range, aux_range;
2750 g_return_if_fail (sheet != NULL);
2752 if (range == NULL) range=&sheet->range;
2756 range->row0 = MIN (range->row0, sheet->range.row0);
2757 range->rowi = MAX (range->rowi, sheet->range.rowi);
2758 range->col0 = MIN (range->col0, sheet->range.col0);
2759 range->coli = MAX (range->coli, sheet->range.coli);
2761 range->row0 = MAX (range->row0, min_visible_row (sheet));
2762 range->rowi = MIN (range->rowi, max_visible_row (sheet));
2763 range->col0 = MAX (range->col0, min_visible_column (sheet));
2764 range->coli = MIN (range->coli, max_visible_column (sheet));
2766 aux_range.row0 = MAX (new_range.row0, min_visible_row (sheet));
2767 aux_range.rowi = MIN (new_range.rowi, max_visible_row (sheet));
2768 aux_range.col0 = MAX (new_range.col0, min_visible_column (sheet));
2769 aux_range.coli = MIN (new_range.coli, max_visible_column (sheet));
2771 for (i = range->row0; i <= range->rowi; i++)
2773 for (j = range->col0; j <= range->coli; j++)
2776 state = gtk_sheet_cell_get_state (sheet, i, j);
2777 selected= (i <= new_range.rowi && i >= new_range.row0 &&
2778 j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
2780 if (state == GTK_STATE_SELECTED && selected &&
2781 (i == sheet->range.row0 || i == sheet->range.rowi ||
2782 j == sheet->range.col0 || j == sheet->range.coli ||
2783 i == new_range.row0 || i == new_range.rowi ||
2784 j == new_range.col0 || j == new_range.coli))
2787 mask1 = i == sheet->range.row0 ? 1 : 0;
2788 mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1;
2789 mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1;
2790 mask1 = j == sheet->range.coli ? mask1 + 8 : mask1;
2792 mask2 = i == new_range.row0 ? 1 : 0;
2793 mask2 = i == new_range.rowi ? mask2 + 2 : mask2;
2794 mask2 = j == new_range.col0 ? mask2 + 4 : mask2;
2795 mask2 = j == new_range.coli ? mask2 + 8 : mask2;
2799 x = g_sheet_column_start_pixel (sheet->column_geometry, j);
2800 y = g_sheet_row_start_pixel (sheet->row_geometry, i);
2801 width = g_sheet_column_start_pixel (sheet->column_geometry, j)- x+
2802 g_sheet_column_get_width (sheet->column_geometry, j);
2803 height = g_sheet_row_start_pixel (sheet->row_geometry, i) - y + g_sheet_row_get_height (sheet->row_geometry, i);
2805 if (i == sheet->range.row0)
2808 height = height + 3;
2810 if (i == sheet->range.rowi) height = height + 3;
2811 if (j == sheet->range.col0)
2816 if (j == sheet->range.coli) width = width + 3;
2818 if (i != sheet->active_cell.row || j != sheet->active_cell.col)
2820 x = g_sheet_column_start_pixel (sheet->column_geometry, j);
2821 y = g_sheet_row_start_pixel (sheet->row_geometry, i);
2822 width = g_sheet_column_start_pixel (sheet->column_geometry, j)- x+
2823 g_sheet_column_get_width (sheet->column_geometry, j);
2825 height = g_sheet_row_start_pixel (sheet->row_geometry, i) - y + g_sheet_row_get_height (sheet->row_geometry, i);
2827 if (i == new_range.row0)
2830 height = height - 2;
2832 if (i == new_range.rowi) height = height - 3;
2833 if (j == new_range.col0)
2838 if (j == new_range.coli) width = width - 3;
2840 gdk_draw_rectangle (sheet->sheet_window,
2851 for (i = range->row0; i <= range->rowi; i++)
2853 for (j = range->col0; j <= range->coli; j++)
2856 state = gtk_sheet_cell_get_state (sheet, i, j);
2857 selected= (i <= new_range.rowi && i >= new_range.row0 &&
2858 j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
2860 if (state == GTK_STATE_SELECTED && !selected)
2863 x = g_sheet_column_start_pixel (sheet->column_geometry, j);
2864 y = g_sheet_row_start_pixel (sheet->row_geometry, i);
2865 width = g_sheet_column_start_pixel (sheet->column_geometry, j) - x + g_sheet_column_get_width (sheet->column_geometry, j);
2866 height = g_sheet_row_start_pixel (sheet->row_geometry, i) - y + g_sheet_row_get_height (sheet->row_geometry, i);
2868 if (i == sheet->range.row0)
2871 height = height + 3;
2873 if (i == sheet->range.rowi) height = height + 3;
2874 if (j == sheet->range.col0)
2879 if (j == sheet->range.coli) width = width + 3;
2885 for (i = range->row0; i <= range->rowi; i++)
2887 for (j = range->col0; j <= range->coli; j++)
2890 state = gtk_sheet_cell_get_state (sheet, i, j);
2891 selected= (i <= new_range.rowi && i >= new_range.row0 &&
2892 j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
2894 if (state != GTK_STATE_SELECTED && selected &&
2895 (i != sheet->active_cell.row || j != sheet->active_cell.col))
2898 x = g_sheet_column_start_pixel (sheet->column_geometry, j);
2899 y = g_sheet_row_start_pixel (sheet->row_geometry, i);
2900 width = g_sheet_column_start_pixel (sheet->column_geometry, j) - x + g_sheet_column_get_width (sheet->column_geometry, j);
2901 height = g_sheet_row_start_pixel (sheet->row_geometry, i) - y + g_sheet_row_get_height (sheet->row_geometry, i);
2903 if (i == new_range.row0)
2906 height = height - 2;
2908 if (i == new_range.rowi) height = height - 3;
2909 if (j == new_range.col0)
2914 if (j == new_range.coli) width = width - 3;
2916 gdk_draw_rectangle (sheet->sheet_window,
2927 for (i = aux_range.row0; i <= aux_range.rowi; i++)
2929 for (j = aux_range.col0; j <= aux_range.coli; j++)
2931 state = gtk_sheet_cell_get_state (sheet, i, j);
2933 mask1 = i == sheet->range.row0 ? 1 : 0;
2934 mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1;
2935 mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1;
2936 mask1 = j == sheet->range.coli ? mask1 + 8 : mask1;
2938 mask2 = i == new_range.row0 ? 1 : 0;
2939 mask2 = i == new_range.rowi ? mask2 + 2 : mask2;
2940 mask2 = j == new_range.col0 ? mask2 + 4 : mask2;
2941 mask2 = j == new_range.coli ? mask2 + 8 : mask2;
2942 if (mask2 != mask1 || (mask2 == mask1 && state != GTK_STATE_SELECTED))
2944 x = g_sheet_column_start_pixel (sheet->column_geometry, j);
2945 y = g_sheet_row_start_pixel (sheet->row_geometry, i);
2946 width = g_sheet_column_get_width (sheet->column_geometry, j);
2947 height = g_sheet_row_get_height (sheet->row_geometry, i);
2949 gdk_draw_rectangle (sheet->sheet_window,
2957 gdk_draw_rectangle (sheet->sheet_window,
2960 x + 1, y + height - 1,
2964 gdk_draw_rectangle (sheet->sheet_window,
2972 gdk_draw_rectangle (sheet->sheet_window,
2975 x + width - 1, y + 1,
2985 gtk_sheet_draw_border (GtkSheet *sheet, GtkSheetRange new_range)
2989 rectangle_from_range (sheet, &new_range, &area);
2991 gdk_draw_rectangle (sheet->sheet_window,
3002 gtk_sheet_real_select_range (GtkSheet *sheet,
3003 const GtkSheetRange *range)
3007 g_return_if_fail (sheet != NULL);
3009 if (range == NULL) range = &sheet->range;
3011 memcpy (&sheet->range, range, sizeof (*range));
3013 if (range->row0 < 0 || range->rowi < 0) return;
3014 if (range->col0 < 0 || range->coli < 0) return;
3016 state = sheet->state;
3019 if (range->coli != sheet->range.coli || range->col0 != sheet->range.col0 ||
3020 range->rowi != sheet->range.rowi || range->row0 != sheet->range.row0)
3022 gtk_sheet_new_selection (sheet, &sheet->range);
3026 gtk_sheet_range_draw_selection (sheet, sheet->range);
3030 gtk_sheet_update_primary_selection (sheet);
3032 g_signal_emit (sheet, sheet_signals[SELECT_RANGE], 0, &sheet->range);
3037 gtk_sheet_get_selected_range (GtkSheet *sheet, GtkSheetRange *range)
3039 g_return_if_fail (sheet != NULL);
3040 *range = sheet->range;
3045 gtk_sheet_select_range (GtkSheet *sheet, const GtkSheetRange *range)
3047 g_return_if_fail (sheet != NULL);
3049 if (range == NULL) range=&sheet->range;
3051 if (range->row0 < 0 || range->rowi < 0) return;
3052 if (range->col0 < 0 || range->coli < 0) return;
3055 if (sheet->state != GTK_SHEET_NORMAL)
3056 gtk_sheet_real_unselect_range (sheet, NULL);
3058 sheet->range.row0 = range->row0;
3059 sheet->range.rowi = range->rowi;
3060 sheet->range.col0 = range->col0;
3061 sheet->range.coli = range->coli;
3062 sheet->active_cell.row = range->row0;
3063 sheet->active_cell.col = range->col0;
3064 sheet->selection_cell.row = range->rowi;
3065 sheet->selection_cell.col = range->coli;
3067 sheet->state = GTK_SHEET_RANGE_SELECTED;
3068 gtk_sheet_real_select_range (sheet, NULL);
3072 gtk_sheet_unselect_range (GtkSheet *sheet)
3074 if (! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
3077 gtk_sheet_real_unselect_range (sheet, NULL);
3078 sheet->state = GTK_STATE_NORMAL;
3080 change_active_cell (sheet,
3081 sheet->active_cell.row, sheet->active_cell.col);
3086 gtk_sheet_real_unselect_range (GtkSheet *sheet,
3087 const GtkSheetRange *range)
3089 g_return_if_fail (sheet != NULL);
3090 g_return_if_fail (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)));
3093 range = &sheet->range;
3095 if (range->row0 < 0 || range->rowi < 0) return;
3096 if (range->col0 < 0 || range->coli < 0) return;
3098 g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, -1);
3099 g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, -1);
3102 if (gtk_sheet_range_isvisible (sheet, *range))
3103 gtk_sheet_draw_backing_pixmap (sheet, *range);
3106 sheet->range.row0 = -1;
3107 sheet->range.rowi = -1;
3108 sheet->range.col0 = -1;
3109 sheet->range.coli = -1;
3114 gtk_sheet_expose (GtkWidget *widget,
3115 GdkEventExpose *event)
3118 GtkSheetRange range;
3120 g_return_val_if_fail (widget != NULL, FALSE);
3121 g_return_val_if_fail (GTK_IS_SHEET (widget), FALSE);
3122 g_return_val_if_fail (event != NULL, FALSE);
3124 sheet = GTK_SHEET (widget);
3126 if (!GTK_WIDGET_DRAWABLE (widget))
3129 /* exposure events on the sheet */
3130 if (event->window == sheet->row_title_window &&
3131 sheet->row_titles_visible)
3133 draw_row_title_buttons_range (sheet,
3134 min_visible_row (sheet),
3135 max_visible_row (sheet));
3138 if (event->window == sheet->column_title_window &&
3139 sheet->column_titles_visible)
3141 draw_column_title_buttons_range (sheet,
3142 min_visible_column (sheet),
3143 max_visible_column (sheet));
3148 yyy_row_ypixel_to_row (sheet,
3149 event->area.y + sheet->vadjustment->value);
3153 yyy_row_ypixel_to_row (sheet,
3155 event->area.height + sheet->vadjustment->value);
3159 column_from_xpixel (sheet,
3160 event->area.x + sheet->hadjustment->value);
3164 column_from_xpixel (sheet,
3165 event->area.x + event->area.width +
3166 sheet->hadjustment->value);
3169 if (event->window == sheet->sheet_window)
3171 gtk_sheet_range_draw (sheet, &range);
3173 if (sheet->state != GTK_SHEET_NORMAL)
3175 if (gtk_sheet_range_isvisible (sheet, sheet->range))
3176 gtk_sheet_range_draw (sheet, &sheet->range);
3178 if (GTK_SHEET_IN_RESIZE (sheet) || GTK_SHEET_IN_DRAG (sheet))
3179 gtk_sheet_range_draw (sheet, &sheet->drag_range);
3181 if (gtk_sheet_range_isvisible (sheet, sheet->range))
3182 gtk_sheet_range_draw_selection (sheet, sheet->range);
3183 if (GTK_SHEET_IN_RESIZE (sheet) || GTK_SHEET_IN_DRAG (sheet))
3184 draw_xor_rectangle (sheet, sheet->drag_range);
3187 if ((!GTK_SHEET_IN_XDRAG (sheet)) && (!GTK_SHEET_IN_YDRAG (sheet)))
3189 if (sheet->state == GTK_SHEET_NORMAL)
3190 gtk_sheet_draw_active_cell (sheet);
3194 if (sheet->state != GTK_SHEET_NORMAL && GTK_SHEET_IN_SELECTION (sheet))
3195 gtk_widget_grab_focus (GTK_WIDGET (sheet));
3197 (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
3204 gtk_sheet_button_press (GtkWidget *widget,
3205 GdkEventButton *event)
3208 GdkModifierType mods;
3213 g_return_val_if_fail (widget != NULL, FALSE);
3214 g_return_val_if_fail (GTK_IS_SHEET (widget), FALSE);
3215 g_return_val_if_fail (event != NULL, FALSE);
3217 sheet = GTK_SHEET (widget);
3219 /* Cancel any pending tooltips */
3220 if (sheet->motion_timer)
3222 g_source_remove (sheet->motion_timer);
3223 sheet->motion_timer = 0;
3226 gtk_widget_get_pointer (widget, &x, &y);
3227 gtk_sheet_get_pixel_info (sheet, x, y, &row, &column);
3230 if (event->window == sheet->column_title_window)
3232 g_signal_emit (sheet,
3233 sheet_signals[BUTTON_EVENT_COLUMN], 0,
3236 if (g_sheet_column_get_sensitivity (sheet->column_geometry, column))
3238 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
3239 g_signal_emit (sheet,
3240 sheet_signals[DOUBLE_CLICK_COLUMN], 0, column);
3243 else if (event->window == sheet->row_title_window)
3245 g_signal_emit (sheet,
3246 sheet_signals[BUTTON_EVENT_ROW], 0,
3250 if (g_sheet_row_get_sensitivity (sheet->row_geometry, row))
3252 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
3253 g_signal_emit (sheet,
3254 sheet_signals[DOUBLE_CLICK_ROW], 0, row);
3258 gdk_window_get_pointer (widget->window, NULL, NULL, &mods);
3260 if (! (mods & GDK_BUTTON1_MASK)) return TRUE;
3263 /* press on resize windows */
3264 if (event->window == sheet->column_title_window &&
3265 gtk_sheet_columns_resizable (sheet))
3268 gtk_widget_get_pointer (widget, &sheet->x_drag, NULL);
3269 if ( sheet->row_titles_visible)
3270 sheet->x_drag -= sheet->row_title_area.width;
3273 sheet->x_drag = event->x;
3275 if (on_column_boundary (sheet, sheet->x_drag, &sheet->drag_cell.col))
3278 gtk_sheet_column_size_request (sheet, sheet->drag_cell.col, &req);
3279 GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_XDRAG);
3280 gdk_pointer_grab (sheet->column_title_window, FALSE,
3281 GDK_POINTER_MOTION_HINT_MASK |
3282 GDK_BUTTON1_MOTION_MASK |
3283 GDK_BUTTON_RELEASE_MASK,
3284 NULL, NULL, event->time);
3286 draw_xor_vline (sheet);
3291 if (event->window == sheet->row_title_window && gtk_sheet_rows_resizable (sheet))
3293 gtk_widget_get_pointer (widget, NULL, &sheet->y_drag);
3295 if (POSSIBLE_YDRAG (sheet, sheet->y_drag, &sheet->drag_cell.row))
3298 gtk_sheet_row_size_request (sheet, sheet->drag_cell.row, &req);
3299 GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_YDRAG);
3300 gdk_pointer_grab (sheet->row_title_window, FALSE,
3301 GDK_POINTER_MOTION_HINT_MASK |
3302 GDK_BUTTON1_MOTION_MASK |
3303 GDK_BUTTON_RELEASE_MASK,
3304 NULL, NULL, event->time);
3306 draw_xor_hline (sheet);
3311 /* the sheet itself does not handle other than single click events */
3312 if (event->type != GDK_BUTTON_PRESS) return FALSE;
3314 /* selections on the sheet */
3315 if (event->window == sheet->sheet_window)
3317 gtk_widget_get_pointer (widget, &x, &y);
3318 gtk_sheet_get_pixel_info (sheet, x, y, &row, &column);
3319 gdk_pointer_grab (sheet->sheet_window, FALSE,
3320 GDK_POINTER_MOTION_HINT_MASK |
3321 GDK_BUTTON1_MOTION_MASK |
3322 GDK_BUTTON_RELEASE_MASK,
3323 NULL, NULL, event->time);
3324 gtk_grab_add (GTK_WIDGET (sheet));
3326 /* This seems to be a kludge to work around a problem where the sheet
3327 scrolls to another position. The timeout scrolls it back to its
3328 original posn. JMD 3 July 2007
3330 gtk_widget_grab_focus (GTK_WIDGET (sheet));
3332 if (sheet->selection_mode != GTK_SELECTION_SINGLE &&
3333 sheet->selection_mode != GTK_SELECTION_NONE &&
3334 sheet->cursor_drag->type == GDK_SIZING &&
3335 !GTK_SHEET_IN_SELECTION (sheet) && !GTK_SHEET_IN_RESIZE (sheet))
3337 if (sheet->state == GTK_STATE_NORMAL)
3339 row = sheet->active_cell.row;
3340 column = sheet->active_cell.col;
3341 sheet->active_cell.row = row;
3342 sheet->active_cell.col = column;
3343 sheet->drag_range = sheet->range;
3344 sheet->state = GTK_SHEET_RANGE_SELECTED;
3345 gtk_sheet_select_range (sheet, &sheet->drag_range);
3349 if (row > sheet->range.rowi) row--;
3350 if (column > sheet->range.coli) column--;
3351 sheet->drag_cell.row = row;
3352 sheet->drag_cell.col = column;
3353 sheet->drag_range = sheet->range;
3354 draw_xor_rectangle (sheet, sheet->drag_range);
3355 GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_RESIZE);
3357 else if (sheet->cursor_drag->type == GDK_TOP_LEFT_ARROW &&
3358 !GTK_SHEET_IN_SELECTION (sheet)
3359 && ! GTK_SHEET_IN_DRAG (sheet)
3360 && sheet->active_cell.row >= 0
3361 && sheet->active_cell.col >= 0
3364 if (sheet->state == GTK_STATE_NORMAL)
3366 row = sheet->active_cell.row;
3367 column = sheet->active_cell.col;
3368 sheet->active_cell.row = row;
3369 sheet->active_cell.col = column;
3370 sheet->drag_range = sheet->range;
3371 sheet->state = GTK_SHEET_RANGE_SELECTED;
3372 gtk_sheet_select_range (sheet, &sheet->drag_range);
3376 if (row < sheet->range.row0) row++;
3377 if (row > sheet->range.rowi) row--;
3378 if (column < sheet->range.col0) column++;
3379 if (column > sheet->range.coli) column--;
3380 sheet->drag_cell.row = row;
3381 sheet->drag_cell.col = column;
3382 sheet->drag_range = sheet->range;
3383 draw_xor_rectangle (sheet, sheet->drag_range);
3384 GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_DRAG);
3388 veto = gtk_sheet_click_cell (sheet, row, column);
3389 if (veto) GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
3393 if (event->window == sheet->column_title_window)
3395 gtk_widget_get_pointer (widget, &x, &y);
3396 if ( sheet->row_titles_visible)
3397 x -= sheet->row_title_area.width;
3399 x += sheet->hadjustment->value;
3401 column = column_from_xpixel (sheet, x);
3403 if (g_sheet_column_get_sensitivity (sheet->column_geometry, column))
3405 veto = gtk_sheet_click_cell (sheet, -1, column);
3406 gtk_grab_add (GTK_WIDGET (sheet));
3407 gtk_widget_grab_focus (GTK_WIDGET (sheet));
3408 GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
3412 if (event->window == sheet->row_title_window)
3414 gtk_widget_get_pointer (widget, &x, &y);
3415 if ( sheet->column_titles_visible)
3416 y -= sheet->column_title_area.height;
3418 y += sheet->vadjustment->value;
3420 row = yyy_row_ypixel_to_row (sheet, y);
3421 if (g_sheet_row_get_sensitivity (sheet->row_geometry, row))
3423 veto = gtk_sheet_click_cell (sheet, row, -1);
3424 gtk_grab_add (GTK_WIDGET (sheet));
3425 gtk_widget_grab_focus (GTK_WIDGET (sheet));
3426 GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
3434 gtk_sheet_click_cell (GtkSheet *sheet, gint row, gint column)
3437 gboolean forbid_move;
3442 if (row >= g_sheet_row_get_row_count (sheet->row_geometry)
3443 || column >= g_sheet_column_get_column_count (sheet->column_geometry))
3448 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
3449 &sheet->active_cell,
3455 if (sheet->state == GTK_STATE_NORMAL)
3458 row = sheet->active_cell.row;
3459 column = sheet->active_cell.col;
3461 change_active_cell (sheet, row, column);
3465 if (row == -1 && column >= 0)
3467 gtk_sheet_select_column (sheet, column);
3471 if (column == -1 && row >= 0)
3473 gtk_sheet_select_row (sheet, row);
3477 if (row == -1 && column == -1)
3479 sheet->range.row0 = 0;
3480 sheet->range.col0 = 0;
3481 sheet->range.rowi = g_sheet_row_get_row_count (sheet->row_geometry) - 1;
3483 g_sheet_column_get_column_count (sheet->column_geometry) - 1;
3484 sheet->active_cell.row = 0;
3485 sheet->active_cell.col = 0;
3486 gtk_sheet_select_range (sheet, NULL);
3490 if (sheet->state != GTK_SHEET_NORMAL)
3492 sheet->state = GTK_SHEET_NORMAL;
3493 gtk_sheet_real_unselect_range (sheet, NULL);
3497 change_active_cell (sheet, row, column);
3500 sheet->active_cell.row = row;
3501 sheet->active_cell.col = column;
3502 sheet->selection_cell.row = row;
3503 sheet->selection_cell.col = column;
3504 sheet->range.row0 = row;
3505 sheet->range.col0 = column;
3506 sheet->range.rowi = row;
3507 sheet->range.coli = column;
3508 sheet->state = GTK_SHEET_NORMAL;
3509 GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
3510 gtk_sheet_draw_active_cell (sheet);
3515 gtk_sheet_button_release (GtkWidget *widget,
3516 GdkEventButton *event)
3519 GdkDisplay *display = gtk_widget_get_display (widget);
3521 GtkSheet *sheet = GTK_SHEET (widget);
3523 /* release on resize windows */
3524 if (GTK_SHEET_IN_XDRAG (sheet))
3526 gint xpos = event->x;
3528 GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_XDRAG);
3529 GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
3531 gdk_display_pointer_ungrab (display, event->time);
3532 draw_xor_vline (sheet);
3534 width = new_column_width (sheet, sheet->drag_cell.col, &xpos);
3536 gtk_sheet_set_column_width (sheet, sheet->drag_cell.col, width);
3540 if (GTK_SHEET_IN_YDRAG (sheet))
3542 GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_YDRAG);
3543 GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
3544 gtk_widget_get_pointer (widget, NULL, &y);
3545 gdk_display_pointer_ungrab (display, event->time);
3546 draw_xor_hline (sheet);
3548 gtk_sheet_set_row_height (sheet, sheet->drag_cell.row,
3549 new_row_height (sheet, sheet->drag_cell.row, &y));
3550 g_signal_emit_by_name (sheet->vadjustment, "value_changed");
3555 if (GTK_SHEET_IN_DRAG (sheet))
3557 GtkSheetRange old_range;
3558 draw_xor_rectangle (sheet, sheet->drag_range);
3559 GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_DRAG);
3560 gdk_display_pointer_ungrab (display, event->time);
3562 gtk_sheet_real_unselect_range (sheet, NULL);
3564 sheet->active_cell.row = sheet->active_cell.row +
3565 (sheet->drag_range.row0 - sheet->range.row0);
3566 sheet->active_cell.col = sheet->active_cell.col +
3567 (sheet->drag_range.col0 - sheet->range.col0);
3568 sheet->selection_cell.row = sheet->selection_cell.row +
3569 (sheet->drag_range.row0 - sheet->range.row0);
3570 sheet->selection_cell.col = sheet->selection_cell.col +
3571 (sheet->drag_range.col0 - sheet->range.col0);
3572 old_range = sheet->range;
3573 sheet->range = sheet->drag_range;
3574 sheet->drag_range = old_range;
3575 g_signal_emit (sheet, sheet_signals[MOVE_RANGE], 0,
3576 &sheet->drag_range, &sheet->range);
3577 gtk_sheet_select_range (sheet, &sheet->range);
3580 if (GTK_SHEET_IN_RESIZE (sheet))
3582 GtkSheetRange old_range;
3583 draw_xor_rectangle (sheet, sheet->drag_range);
3584 GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_RESIZE);
3585 gdk_display_pointer_ungrab (display, event->time);
3587 gtk_sheet_real_unselect_range (sheet, NULL);
3589 sheet->active_cell.row = sheet->active_cell.row +
3590 (sheet->drag_range.row0 - sheet->range.row0);
3591 sheet->active_cell.col = sheet->active_cell.col +
3592 (sheet->drag_range.col0 - sheet->range.col0);
3593 if (sheet->drag_range.row0 < sheet->range.row0)
3594 sheet->selection_cell.row = sheet->drag_range.row0;
3595 if (sheet->drag_range.rowi >= sheet->range.rowi)
3596 sheet->selection_cell.row = sheet->drag_range.rowi;
3597 if (sheet->drag_range.col0 < sheet->range.col0)
3598 sheet->selection_cell.col = sheet->drag_range.col0;
3599 if (sheet->drag_range.coli >= sheet->range.coli)
3600 sheet->selection_cell.col = sheet->drag_range.coli;
3601 old_range = sheet->range;
3602 sheet->range = sheet->drag_range;
3603 sheet->drag_range = old_range;
3605 if (sheet->state == GTK_STATE_NORMAL) sheet->state = GTK_SHEET_RANGE_SELECTED;
3606 g_signal_emit (sheet, sheet_signals[RESIZE_RANGE], 0,
3607 &sheet->drag_range, &sheet->range);
3608 gtk_sheet_select_range (sheet, &sheet->range);
3611 if (sheet->state == GTK_SHEET_NORMAL && GTK_SHEET_IN_SELECTION (sheet))
3613 GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
3614 gdk_display_pointer_ungrab (display, event->time);
3615 change_active_cell (sheet, sheet->active_cell.row,
3616 sheet->active_cell.col);
3619 if (GTK_SHEET_IN_SELECTION)
3620 gdk_display_pointer_ungrab (display, event->time);
3621 gtk_grab_remove (GTK_WIDGET (sheet));
3623 GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
3632 /* Shamelessly lifted from gtktooltips */
3634 gtk_sheet_subtitle_paint_window (GtkWidget *tip_window)
3638 gtk_widget_size_request (tip_window, &req);
3639 gtk_paint_flat_box (tip_window->style, tip_window->window,
3640 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
3641 NULL, GTK_WIDGET(tip_window), "tooltip",
3642 0, 0, req.width, req.height);
3648 destroy_hover_window (GtkSheetHoverTitle *h)
3650 gtk_widget_destroy (h->window);
3654 static GtkSheetHoverTitle *
3655 create_hover_window (void)
3657 GtkSheetHoverTitle *hw = g_malloc (sizeof (*hw));
3659 hw->window = gtk_window_new (GTK_WINDOW_POPUP);
3661 #if GTK_CHECK_VERSION (2, 9, 0)
3662 gtk_window_set_type_hint (GTK_WINDOW (hw->window),
3663 GDK_WINDOW_TYPE_HINT_TOOLTIP);
3666 gtk_widget_set_app_paintable (hw->window, TRUE);
3667 gtk_window_set_resizable (GTK_WINDOW (hw->window), FALSE);
3668 gtk_widget_set_name (hw->window, "gtk-tooltips");
3669 gtk_container_set_border_width (GTK_CONTAINER (hw->window), 4);
3671 g_signal_connect (hw->window,
3673 G_CALLBACK (gtk_sheet_subtitle_paint_window),
3676 hw->label = gtk_label_new (NULL);
3679 gtk_label_set_line_wrap (GTK_LABEL (hw->label), TRUE);
3680 gtk_misc_set_alignment (GTK_MISC (hw->label), 0.5, 0.5);
3682 gtk_container_add (GTK_CONTAINER (hw->window), hw->label);
3684 gtk_widget_show (hw->label);
3686 g_signal_connect (hw->window,
3688 G_CALLBACK (gtk_widget_destroyed),
3694 #define HOVER_WINDOW_Y_OFFSET 2
3697 show_subtitle (GtkSheet *sheet, gint row, gint column, const gchar *subtitle)
3706 gtk_label_set_text (GTK_LABEL (sheet->hover_window->label),
3710 sheet->hover_window->row = row;
3711 sheet->hover_window->column = column;
3713 gdk_window_get_origin (GTK_WIDGET (sheet)->window, &x, &y);
3715 gtk_widget_get_pointer (GTK_WIDGET (sheet), &px, &py);
3717 gtk_widget_show (sheet->hover_window->window);
3719 width = GTK_WIDGET (sheet->hover_window->label)->allocation.width;
3725 y += sheet->column_title_area.y;
3726 y += sheet->column_title_area.height;
3727 y += HOVER_WINDOW_Y_OFFSET;
3733 x += sheet->row_title_area.x;
3734 x += sheet->row_title_area.width * 2 / 3.0;
3737 gtk_window_move (GTK_WINDOW (sheet->hover_window->window),
3742 motion_timeout_callback (gpointer data)
3744 GtkSheet *sheet = GTK_SHEET (data);
3747 gtk_widget_get_pointer (GTK_WIDGET (sheet), &x, &y);
3749 if ( gtk_sheet_get_pixel_info (sheet, x, y, &row, &column) )
3751 if (sheet->row_title_under)
3753 GSheetRow *row_geo = sheet->row_geometry;
3756 text = g_sheet_row_get_subtitle (row_geo, row);
3758 show_subtitle (sheet, row, -1, text);
3762 if (sheet->column_title_under)
3764 GSheetColumn *col_geo = sheet->column_geometry;
3767 text = g_sheet_column_get_subtitle (col_geo, column);
3769 show_subtitle (sheet, -1, column, text);
3779 gtk_sheet_motion (GtkWidget *widget, GdkEventMotion *event)
3782 GdkModifierType mods;
3783 GdkCursorType new_cursor;
3786 GdkDisplay *display;
3788 g_return_val_if_fail (widget != NULL, FALSE);
3789 g_return_val_if_fail (GTK_IS_SHEET (widget), FALSE);
3790 g_return_val_if_fail (event != NULL, FALSE);
3792 sheet = GTK_SHEET (widget);
3794 display = gtk_widget_get_display (widget);
3796 /* selections on the sheet */
3800 if (!GTK_WIDGET_VISIBLE (sheet->hover_window->window))
3802 if ( sheet->motion_timer > 0 )
3803 g_source_remove (sheet->motion_timer);
3804 sheet->motion_timer =
3805 g_timeout_add (TIMEOUT_HOVER, motion_timeout_callback, sheet);
3811 gtk_widget_get_pointer (widget, &wx, &wy);
3813 if ( gtk_sheet_get_pixel_info (sheet, wx, wy, &row, &column) )
3815 if ( row != sheet->hover_window->row ||
3816 column != sheet->hover_window->column)
3818 gtk_widget_hide (sheet->hover_window->window);
3823 if (event->window == sheet->column_title_window &&
3824 gtk_sheet_columns_resizable (sheet))
3826 if (!GTK_SHEET_IN_SELECTION (sheet) &&
3827 on_column_boundary (sheet, x, &column))
3829 new_cursor = GDK_SB_H_DOUBLE_ARROW;
3830 if (new_cursor != sheet->cursor_drag->type)
3832 gdk_cursor_unref (sheet->cursor_drag);
3833 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_SB_H_DOUBLE_ARROW);
3834 gdk_window_set_cursor (sheet->column_title_window,
3835 sheet->cursor_drag);
3840 new_cursor = GDK_TOP_LEFT_ARROW;
3841 if (!GTK_SHEET_IN_XDRAG (sheet) &&
3842 new_cursor != sheet->cursor_drag->type)
3844 gdk_cursor_unref (sheet->cursor_drag);
3845 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
3846 gdk_window_set_cursor (sheet->column_title_window,
3847 sheet->cursor_drag);
3852 if (event->window == sheet->row_title_window &&
3853 gtk_sheet_rows_resizable (sheet))
3855 if (!GTK_SHEET_IN_SELECTION (sheet) && POSSIBLE_YDRAG (sheet, y, &column))
3857 new_cursor = GDK_SB_V_DOUBLE_ARROW;
3858 if (new_cursor != sheet->cursor_drag->type)
3860 gdk_cursor_unref (sheet->cursor_drag);
3861 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_SB_V_DOUBLE_ARROW);
3862 gdk_window_set_cursor (sheet->row_title_window, sheet->cursor_drag);
3867 new_cursor = GDK_TOP_LEFT_ARROW;
3868 if (!GTK_SHEET_IN_YDRAG (sheet) &&
3869 new_cursor != sheet->cursor_drag->type)
3871 gdk_cursor_unref (sheet->cursor_drag);
3872 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
3873 gdk_window_set_cursor (sheet->row_title_window, sheet->cursor_drag);
3878 new_cursor = GDK_PLUS;
3879 if ( event->window == sheet->sheet_window &&
3880 !POSSIBLE_DRAG (sheet, x, y, &row, &column) &&
3881 !GTK_SHEET_IN_DRAG (sheet) &&
3882 !POSSIBLE_RESIZE (sheet, x, y, &row, &column) &&
3883 !GTK_SHEET_IN_RESIZE (sheet) &&
3884 new_cursor != sheet->cursor_drag->type)
3886 gdk_cursor_unref (sheet->cursor_drag);
3887 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
3888 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3891 new_cursor = GDK_TOP_LEFT_ARROW;
3892 if ( event->window == sheet->sheet_window &&
3893 ! (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
3894 GTK_SHEET_IN_RESIZE (sheet)) &&
3895 (POSSIBLE_DRAG (sheet, x, y, &row, &column) ||
3896 GTK_SHEET_IN_DRAG (sheet)) &&
3897 new_cursor != sheet->cursor_drag->type)
3899 gdk_cursor_unref (sheet->cursor_drag);
3900 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
3901 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3904 new_cursor = GDK_SIZING;
3905 if ( event->window == sheet->sheet_window &&
3906 sheet->selection_mode != GTK_SELECTION_NONE &&
3907 !GTK_SHEET_IN_DRAG (sheet) &&
3908 (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
3909 GTK_SHEET_IN_RESIZE (sheet)) &&
3910 new_cursor != sheet->cursor_drag->type)
3912 gdk_cursor_unref (sheet->cursor_drag);
3913 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_SIZING);
3914 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3918 gdk_window_get_pointer (widget->window, &x, &y, &mods);
3919 if (! (mods & GDK_BUTTON1_MASK)) return FALSE;
3921 if (GTK_SHEET_IN_XDRAG (sheet))
3925 new_column_width (sheet, sheet->drag_cell.col, &x);
3927 if (x != sheet->x_drag)
3929 draw_xor_vline (sheet);
3931 draw_xor_vline (sheet);
3937 if (GTK_SHEET_IN_YDRAG (sheet))
3939 if (event->is_hint || event->window != widget->window)
3940 gtk_widget_get_pointer (widget, NULL, &y);
3944 new_row_height (sheet, sheet->drag_cell.row, &y);
3945 if (y != sheet->y_drag)
3947 draw_xor_hline (sheet);
3949 draw_xor_hline (sheet);
3954 if (GTK_SHEET_IN_DRAG (sheet))
3957 column = column_from_xpixel (sheet, x)- sheet->drag_cell.col;
3958 row = yyy_row_ypixel_to_row (sheet, y) - sheet->drag_cell.row;
3959 if (sheet->state == GTK_SHEET_COLUMN_SELECTED) row = 0;
3960 if (sheet->state == GTK_SHEET_ROW_SELECTED) column = 0;
3964 if (aux.row0 + row >= 0 && aux.rowi + row < g_sheet_row_get_row_count (sheet->row_geometry) &&
3965 aux.col0 + column >= 0 && aux.coli + column < g_sheet_column_get_column_count (sheet->column_geometry))
3967 aux = sheet->drag_range;
3968 sheet->drag_range.row0 = sheet->range.row0 + row;
3969 sheet->drag_range.col0 = sheet->range.col0 + column;
3970 sheet->drag_range.rowi = sheet->range.rowi + row;
3971 sheet->drag_range.coli = sheet->range.coli + column;
3972 if (aux.row0 != sheet->drag_range.row0 ||
3973 aux.col0 != sheet->drag_range.col0)
3975 draw_xor_rectangle (sheet, aux);
3976 draw_xor_rectangle (sheet, sheet->drag_range);
3982 if (GTK_SHEET_IN_RESIZE (sheet))
3985 gint v_h, current_col, current_row, col_threshold, row_threshold;
3987 if (abs (x - g_sheet_column_start_pixel (sheet->column_geometry, sheet->drag_cell.col)) >
3988 abs (y - g_sheet_row_start_pixel (sheet->row_geometry, sheet->drag_cell.row))) v_h = 2;
3990 current_col = column_from_xpixel (sheet, x);
3991 current_row = yyy_row_ypixel_to_row (sheet, y);
3992 column = current_col - sheet->drag_cell.col;
3993 row = current_row - sheet->drag_cell.row;
3995 /*use half of column width resp. row height as threshold to
3997 col_threshold = g_sheet_column_start_pixel (sheet->column_geometry, current_col) +
3998 g_sheet_column_get_width (sheet->column_geometry, current_col) / 2;
4001 if (x < col_threshold)
4004 else if (column < 0)
4006 if (x > col_threshold)
4009 row_threshold = g_sheet_row_start_pixel (sheet->row_geometry, current_row) +
4010 g_sheet_row_get_height (sheet->row_geometry, current_row)/2;
4013 if (y < row_threshold)
4018 if (y > row_threshold)
4022 if (sheet->state == GTK_SHEET_COLUMN_SELECTED) row = 0;
4023 if (sheet->state == GTK_SHEET_ROW_SELECTED) column = 0;
4033 if (aux.row0 + row >= 0 && aux.rowi + row < g_sheet_row_get_row_count (sheet->row_geometry) &&
4034 aux.col0 + column >= 0 && aux.coli + column < g_sheet_column_get_column_count (sheet->column_geometry))
4036 aux = sheet->drag_range;
4037 sheet->drag_range = sheet->range;
4039 if (row < 0) sheet->drag_range.row0 = sheet->range.row0 + row;
4040 if (row > 0) sheet->drag_range.rowi = sheet->range.rowi + row;
4041 if (column < 0) sheet->drag_range.col0 = sheet->range.col0 + column;
4042 if (column > 0) sheet->drag_range.coli = sheet->range.coli + column;
4044 if (aux.row0 != sheet->drag_range.row0 ||
4045 aux.rowi != sheet->drag_range.rowi ||
4046 aux.col0 != sheet->drag_range.col0 ||
4047 aux.coli != sheet->drag_range.coli)
4049 draw_xor_rectangle (sheet, aux);
4050 draw_xor_rectangle (sheet, sheet->drag_range);
4056 gtk_sheet_get_pixel_info (sheet, x, y, &row, &column);
4058 if (sheet->state == GTK_SHEET_NORMAL && row == sheet->active_cell.row &&
4059 column == sheet->active_cell.col) return TRUE;
4061 if (GTK_SHEET_IN_SELECTION (sheet) && mods&GDK_BUTTON1_MASK)
4062 gtk_sheet_extend_selection (sheet, row, column);
4068 gtk_sheet_crossing_notify (GtkWidget *widget,
4069 GdkEventCrossing *event)
4071 GtkSheet *sheet = GTK_SHEET (widget);
4073 if (event->window == sheet->column_title_window)
4074 sheet->column_title_under = event->type == GDK_ENTER_NOTIFY;
4075 else if (event->window == sheet->row_title_window)
4076 sheet->row_title_under = event->type == GDK_ENTER_NOTIFY;
4082 gtk_sheet_extend_selection (GtkSheet *sheet, gint row, gint column)
4084 GtkSheetRange range;
4088 if (row == sheet->selection_cell.row && column == sheet->selection_cell.col)
4091 if (sheet->selection_mode == GTK_SELECTION_SINGLE) return;
4093 gtk_widget_grab_focus (GTK_WIDGET (sheet));
4095 if (GTK_SHEET_IN_DRAG (sheet)) return;
4097 state = sheet->state;
4099 switch (sheet->state)
4101 case GTK_SHEET_ROW_SELECTED:
4102 column = g_sheet_column_get_column_count (sheet->column_geometry) - 1;
4104 case GTK_SHEET_COLUMN_SELECTED:
4105 row = g_sheet_row_get_row_count (sheet->row_geometry) - 1;
4107 case GTK_SHEET_NORMAL:
4108 sheet->state = GTK_SHEET_RANGE_SELECTED;
4109 r = sheet->active_cell.row;
4110 c = sheet->active_cell.col;
4111 sheet->range.col0 = c;
4112 sheet->range.row0 = r;
4113 sheet->range.coli = c;
4114 sheet->range.rowi = r;
4115 gtk_sheet_range_draw_selection (sheet, sheet->range);
4116 case GTK_SHEET_RANGE_SELECTED:
4117 sheet->state = GTK_SHEET_RANGE_SELECTED;
4120 sheet->selection_cell.row = row;
4121 sheet->selection_cell.col = column;
4123 range.col0 = MIN (column, sheet->active_cell.col);
4124 range.coli = MAX (column, sheet->active_cell.col);
4125 range.row0 = MIN (row, sheet->active_cell.row);
4126 range.rowi = MAX (row, sheet->active_cell.row);
4128 if (range.row0 != sheet->range.row0 || range.rowi != sheet->range.rowi ||
4129 range.col0 != sheet->range.col0 || range.coli != sheet->range.coli ||
4130 state == GTK_SHEET_NORMAL)
4131 gtk_sheet_real_select_range (sheet, &range);
4136 gtk_sheet_entry_key_press (GtkWidget *widget,
4140 g_signal_emit_by_name (widget, "key_press_event", key, &focus);
4145 /* Number of rows in a step-increment */
4146 #define ROWS_PER_STEP 1
4150 page_vertical (GtkSheet *sheet, GtkScrollType dir)
4152 gint old_row = sheet->active_cell.row ;
4153 glong vpixel = g_sheet_row_start_pixel (sheet->row_geometry, old_row);
4157 vpixel -= g_sheet_row_start_pixel (sheet->row_geometry,
4158 min_visible_row (sheet));
4162 case GTK_SCROLL_PAGE_DOWN:
4163 gtk_adjustment_set_value (sheet->vadjustment,
4164 sheet->vadjustment->value +
4165 sheet->vadjustment->page_increment);
4167 case GTK_SCROLL_PAGE_UP:
4168 gtk_adjustment_set_value (sheet->vadjustment,
4169 sheet->vadjustment->value -
4170 sheet->vadjustment->page_increment);
4174 g_assert_not_reached ();
4179 vpixel += g_sheet_row_start_pixel (sheet->row_geometry,
4180 min_visible_row (sheet));
4182 new_row = yyy_row_ypixel_to_row (sheet, vpixel);
4184 change_active_cell (sheet, new_row,
4185 sheet->active_cell.col);
4190 step_sheet (GtkSheet *sheet, GtkScrollType dir)
4192 gint current_row = sheet->active_cell.row;
4193 gint current_col = sheet->active_cell.col;
4194 GtkSheetCell new_cell ;
4195 gboolean forbidden = FALSE;
4197 new_cell.row = current_row;
4198 new_cell.col = current_col;
4202 case GTK_SCROLL_STEP_DOWN:
4205 case GTK_SCROLL_STEP_UP:
4208 case GTK_SCROLL_STEP_RIGHT:
4211 case GTK_SCROLL_STEP_LEFT:
4215 g_assert_not_reached ();
4220 maximize_int (&new_cell.row, 0);
4221 maximize_int (&new_cell.col, 0);
4223 minimize_int (&new_cell.row,
4224 g_sheet_row_get_row_count (sheet->row_geometry) - 1);
4226 minimize_int (&new_cell.col,
4227 g_sheet_column_get_column_count (sheet->column_geometry) - 1);
4229 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
4230 &sheet->active_cell,
4237 change_active_cell (sheet, new_cell.row, new_cell.col);
4239 if ( new_cell.col > max_fully_visible_column (sheet))
4242 g_sheet_column_start_pixel (sheet->column_geometry,
4244 hpos -= sheet->hadjustment->page_size;
4246 gtk_adjustment_set_value (sheet->hadjustment,
4249 else if ( new_cell.col < min_fully_visible_column (sheet))
4252 g_sheet_column_start_pixel (sheet->column_geometry,
4255 gtk_adjustment_set_value (sheet->hadjustment,
4260 if ( new_cell.row > max_fully_visible_row (sheet))
4263 g_sheet_row_start_pixel (sheet->row_geometry,
4265 vpos -= sheet->vadjustment->page_size;
4267 gtk_adjustment_set_value (sheet->vadjustment,
4270 else if ( new_cell.row < min_fully_visible_row (sheet))
4273 g_sheet_row_start_pixel (sheet->row_geometry,
4276 gtk_adjustment_set_value (sheet->vadjustment,
4283 gtk_sheet_key_press (GtkWidget *widget,
4286 GtkSheet *sheet = GTK_SHEET (widget);
4288 GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
4290 switch (key->keyval)
4294 step_sheet (sheet, GTK_SCROLL_STEP_RIGHT);
4296 case GDK_ISO_Left_Tab:
4298 step_sheet (sheet, GTK_SCROLL_STEP_LEFT);
4302 step_sheet (sheet, GTK_SCROLL_STEP_DOWN);
4305 step_sheet (sheet, GTK_SCROLL_STEP_UP);
4309 page_vertical (sheet, GTK_SCROLL_PAGE_DOWN);
4312 page_vertical (sheet, GTK_SCROLL_PAGE_UP);
4316 gtk_adjustment_set_value (sheet->vadjustment,
4317 sheet->vadjustment->lower);
4319 change_active_cell (sheet, 0,
4320 sheet->active_cell.col);
4325 gtk_adjustment_set_value (sheet->vadjustment,
4326 sheet->vadjustment->upper -
4327 sheet->vadjustment->page_size -
4328 sheet->vadjustment->page_increment);
4331 change_active_cellx (sheet,
4332 g_sheet_row_get_row_count (sheet->row_geometry) - 1,
4333 sheet->active_cell.col);
4337 gtk_sheet_real_cell_clear (sheet, sheet->active_cell.row, sheet->active_cell.col);
4348 gtk_sheet_size_request (GtkWidget *widget,
4349 GtkRequisition *requisition)
4353 g_return_if_fail (widget != NULL);
4354 g_return_if_fail (GTK_IS_SHEET (widget));
4355 g_return_if_fail (requisition != NULL);
4357 sheet = GTK_SHEET (widget);
4359 requisition->width = 3 * DEFAULT_COLUMN_WIDTH;
4360 requisition->height = 3 * DEFAULT_ROW_HEIGHT;
4362 /* compute the size of the column title area */
4363 if (sheet->column_titles_visible)
4364 requisition->height += sheet->column_title_area.height;
4366 /* compute the size of the row title area */
4367 if (sheet->row_titles_visible)
4368 requisition->width += sheet->row_title_area.width;
4373 gtk_sheet_size_allocate (GtkWidget *widget,
4374 GtkAllocation *allocation)
4377 GtkAllocation sheet_allocation;
4380 g_return_if_fail (widget != NULL);
4381 g_return_if_fail (GTK_IS_SHEET (widget));
4382 g_return_if_fail (allocation != NULL);
4384 sheet = GTK_SHEET (widget);
4385 widget->allocation = *allocation;
4386 border_width = GTK_CONTAINER (widget)->border_width;
4388 if (GTK_WIDGET_REALIZED (widget))
4389 gdk_window_move_resize (widget->window,
4390 allocation->x + border_width,
4391 allocation->y + border_width,
4392 allocation->width - 2 * border_width,
4393 allocation->height - 2 * border_width);
4395 /* use internal allocation structure for all the math
4396 * because it's easier than always subtracting the container
4398 sheet->internal_allocation.x = 0;
4399 sheet->internal_allocation.y = 0;
4400 sheet->internal_allocation.width = allocation->width - 2 * border_width;
4401 sheet->internal_allocation.height = allocation->height - 2 * border_width;
4403 sheet_allocation.x = 0;
4404 sheet_allocation.y = 0;
4405 sheet_allocation.width = allocation->width - 2 * border_width;
4406 sheet_allocation.height = allocation->height - 2 * border_width;
4408 if (GTK_WIDGET_REALIZED (widget))
4409 gdk_window_move_resize (sheet->sheet_window,
4412 sheet_allocation.width,
4413 sheet_allocation.height);
4415 /* position the window which holds the column title buttons */
4416 sheet->column_title_area.x = 0;
4417 sheet->column_title_area.y = 0;
4419 if (sheet->row_titles_visible)
4421 sheet->column_title_area.x = sheet->row_title_area.width;
4424 sheet->column_title_area.width = sheet_allocation.width ;
4427 if (GTK_WIDGET_REALIZED (widget) && sheet->column_titles_visible)
4428 gdk_window_move_resize (sheet->column_title_window,
4429 sheet->column_title_area.x,
4430 sheet->column_title_area.y,
4431 sheet->column_title_area.width,
4432 sheet->column_title_area.height);
4435 /* column button allocation */
4436 draw_column_title_buttons (sheet);
4438 /* position the window which holds the row title buttons */
4439 sheet->row_title_area.x = 0;
4440 sheet->row_title_area.y = 0;
4441 if (sheet->column_titles_visible)
4443 sheet->row_title_area.y = sheet->column_title_area.height;
4446 sheet->row_title_area.height = sheet_allocation.height -
4447 sheet->row_title_area.y;
4449 if (GTK_WIDGET_REALIZED (widget) && sheet->row_titles_visible)
4450 gdk_window_move_resize (sheet->row_title_window,
4451 sheet->row_title_area.x,
4452 sheet->row_title_area.y,
4453 sheet->row_title_area.width,
4454 sheet->row_title_area.height);
4457 /* row button allocation */
4458 draw_row_title_buttons (sheet);
4459 draw_column_title_buttons (sheet);
4461 /* set the scrollbars adjustments */
4462 adjust_scrollbars (sheet);
4466 draw_column_title_buttons (GtkSheet *sheet)
4470 if (!sheet->column_titles_visible) return;
4471 if (!GTK_WIDGET_REALIZED (sheet))
4474 gdk_drawable_get_size (sheet->sheet_window, &width, NULL);
4477 if (sheet->row_titles_visible)
4479 x = sheet->row_title_area.width;
4482 if (sheet->column_title_area.width != width || sheet->column_title_area.x != x)
4484 sheet->column_title_area.width = width;
4485 sheet->column_title_area.x = x;
4486 gdk_window_move_resize (sheet->column_title_window,
4487 sheet->column_title_area.x,
4488 sheet->column_title_area.y,
4489 sheet->column_title_area.width,
4490 sheet->column_title_area.height);
4493 if (max_visible_column (sheet) ==
4494 g_sheet_column_get_column_count (sheet->column_geometry) - 1)
4495 gdk_window_clear_area (sheet->column_title_window,
4497 sheet->column_title_area.width,
4498 sheet->column_title_area.height);
4500 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
4502 size_allocate_global_button (sheet);
4504 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
4505 max_visible_column (sheet));
4509 draw_row_title_buttons (GtkSheet *sheet)
4514 if (!sheet->row_titles_visible) return;
4515 if (!GTK_WIDGET_REALIZED (sheet))
4518 gdk_drawable_get_size (sheet->sheet_window, NULL, &height);
4520 if (sheet->column_titles_visible)
4522 y = sheet->column_title_area.height;
4525 if (sheet->row_title_area.height != height || sheet->row_title_area.y != y)
4527 sheet->row_title_area.y = y;
4528 sheet->row_title_area.height = height;
4529 gdk_window_move_resize (sheet->row_title_window,
4530 sheet->row_title_area.x,
4531 sheet->row_title_area.y,
4532 sheet->row_title_area.width,
4533 sheet->row_title_area.height);
4536 if (max_visible_row (sheet) == g_sheet_row_get_row_count (sheet->row_geometry) - 1)
4537 gdk_window_clear_area (sheet->row_title_window,
4539 sheet->row_title_area.width,
4540 sheet->row_title_area.height);
4542 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
4544 size_allocate_global_button (sheet);
4547 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
4548 max_visible_row (sheet));
4553 gtk_sheet_size_allocate_entry (GtkSheet *sheet)
4555 GtkAllocation entry_alloc;
4556 GtkSheetCellAttr attributes = { 0 };
4557 GtkEntry *sheet_entry;
4560 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4561 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
4563 sheet_entry = GTK_ENTRY (gtk_sheet_get_entry (sheet));
4565 if ( ! gtk_sheet_get_attributes (sheet, sheet->active_cell.row,
4566 sheet->active_cell.col,
4570 gtk_widget_ensure_style (GTK_WIDGET (sheet_entry));
4572 if ( GTK_WIDGET_REALIZED (sheet->entry_widget) )
4574 GtkStyle *style = GTK_WIDGET (sheet_entry)->style;
4576 style->bg[GTK_STATE_NORMAL] = attributes.background;
4577 style->fg[GTK_STATE_NORMAL] = attributes.foreground;
4578 style->text[GTK_STATE_NORMAL] = attributes.foreground;
4579 style->bg[GTK_STATE_ACTIVE] = attributes.background;
4580 style->fg[GTK_STATE_ACTIVE] = attributes.foreground;
4581 style->text[GTK_STATE_ACTIVE] = attributes.foreground;
4583 pango_font_description_free (style->font_desc);
4584 g_assert (attributes.font_desc);
4585 style->font_desc = pango_font_description_copy (attributes.font_desc);
4588 rectangle_from_cell (sheet, sheet->active_cell.row,
4589 sheet->active_cell.col, &entry_alloc);
4591 gtk_widget_set_size_request (sheet->entry_widget, entry_alloc.width,
4592 entry_alloc.height);
4593 gtk_widget_size_allocate (sheet->entry_widget, &entry_alloc);
4598 create_sheet_entry (GtkSheet *sheet)
4600 if (sheet->entry_widget)
4602 gtk_widget_unparent (sheet->entry_widget);
4605 if (sheet->entry_type)
4607 sheet->entry_container = g_object_new (sheet->entry_type, NULL);
4608 g_object_ref_sink (sheet->entry_container);
4609 sheet->entry_widget = gtk_sheet_get_entry (sheet);
4611 if ( NULL == sheet->entry_widget)
4613 g_warning ("Entry type is %s. It must be GtkEntry subclass, or a widget containing one. "
4614 "Using default", g_type_name (sheet->entry_type));
4615 g_object_unref (sheet->entry_container);
4616 sheet->entry_widget = sheet->entry_container = gtk_entry_new ();
4620 sheet->entry_widget = sheet->entry_container ;
4625 sheet->entry_widget = sheet->entry_container = gtk_entry_new ();
4626 g_object_ref_sink (sheet->entry_container);
4629 gtk_widget_size_request (sheet->entry_widget, NULL);
4631 if (GTK_WIDGET_REALIZED (sheet))
4633 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
4634 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
4635 gtk_widget_realize (sheet->entry_widget);
4638 g_signal_connect_swapped (sheet->entry_widget, "key_press_event",
4639 G_CALLBACK (gtk_sheet_entry_key_press),
4642 sheet->entry_handler_id =
4643 g_signal_connect (sheet->entry_widget,
4645 G_CALLBACK (gtk_sheet_entry_changed),
4648 gtk_widget_show (sheet->entry_widget);
4652 /* Finds the last child widget that happens to be of type GtkEntry */
4654 find_entry (GtkWidget *w, gpointer user_data)
4656 GtkWidget **entry = user_data;
4657 if ( GTK_IS_ENTRY (w))
4664 gtk_sheet_get_entry (GtkSheet *sheet)
4667 GtkWidget *entry = NULL;
4669 g_return_val_if_fail (sheet != NULL, NULL);
4670 g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);
4671 g_return_val_if_fail (sheet->entry_widget != NULL, NULL);
4673 if (GTK_IS_ENTRY (sheet->entry_container))
4674 return (sheet->entry_container);
4676 parent = sheet->entry_container;
4678 if (GTK_IS_CONTAINER (parent))
4680 gtk_container_forall (GTK_CONTAINER (parent), find_entry, &entry);
4682 if (GTK_IS_ENTRY (entry))
4686 if (!GTK_IS_ENTRY (entry)) return NULL;
4693 gtk_sheet_get_entry_widget (GtkSheet *sheet)
4695 g_return_val_if_fail (sheet != NULL, NULL);
4696 g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);
4697 g_return_val_if_fail (sheet->entry_widget != NULL, NULL);
4699 return (sheet->entry_widget);
4704 draw_button (GtkSheet *sheet, GdkWindow *window,
4705 GtkSheetButton *button, gboolean is_sensitive,
4706 GdkRectangle allocation)
4708 GtkShadowType shadow_type;
4709 gint text_width = 0, text_height = 0;
4710 PangoAlignment align = PANGO_ALIGN_LEFT;
4716 g_return_if_fail (sheet != NULL);
4717 g_return_if_fail (button != NULL);
4720 rtl = gtk_widget_get_direction (GTK_WIDGET (sheet)) == GTK_TEXT_DIR_RTL;
4722 gdk_window_clear_area (window,
4723 allocation.x, allocation.y,
4724 allocation.width, allocation.height);
4726 gtk_paint_box (sheet->button->style, window,
4727 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
4728 &allocation, GTK_WIDGET (sheet->button),
4730 allocation.x, allocation.y,
4731 allocation.width, allocation.height);
4733 state = button->state;
4734 if (!is_sensitive) state = GTK_STATE_INSENSITIVE;
4736 if (state == GTK_STATE_ACTIVE)
4737 shadow_type = GTK_SHADOW_IN;
4739 shadow_type = GTK_SHADOW_OUT;
4741 if (state != GTK_STATE_NORMAL && state != GTK_STATE_INSENSITIVE)
4742 gtk_paint_box (sheet->button->style, window,
4743 button->state, shadow_type,
4744 &allocation, GTK_WIDGET (sheet->button),
4746 allocation.x, allocation.y,
4747 allocation.width, allocation.height);
4749 if (button->label_visible)
4751 text_height = DEFAULT_ROW_HEIGHT -
4752 2 * COLUMN_TITLES_HEIGHT;
4754 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4756 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc,
4759 allocation.y += 2 * sheet->button->style->ythickness;
4761 if (button->label && strlen (button->label)>0)
4763 PangoRectangle rect;
4764 gchar *line = button->label;
4766 PangoLayout *layout = NULL;
4767 gint real_x = allocation.x;
4768 gint real_y = allocation.y;
4770 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), line);
4771 pango_layout_get_extents (layout, NULL, &rect);
4773 text_width = PANGO_PIXELS (rect.width);
4774 switch (button->justification)
4776 case GTK_JUSTIFY_LEFT:
4777 real_x = allocation.x + COLUMN_TITLES_HEIGHT;
4778 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4780 case GTK_JUSTIFY_RIGHT:
4781 real_x = allocation.x + allocation.width - text_width - COLUMN_TITLES_HEIGHT;
4782 align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
4784 case GTK_JUSTIFY_CENTER:
4786 real_x = allocation.x + (allocation.width - text_width)/2;
4787 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4788 pango_layout_set_justify (layout, TRUE);
4790 pango_layout_set_alignment (layout, align);
4791 gtk_paint_layout (GTK_WIDGET (sheet)->style,
4800 g_object_unref (layout);
4803 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4805 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc, NULL);
4809 gtk_sheet_button_free (button);
4813 /* Draw the column title buttons FIRST through to LAST */
4815 draw_column_title_buttons_range (GtkSheet *sheet, gint first, gint last)
4819 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4821 if (!sheet->column_titles_visible) return;
4823 g_return_if_fail (first >= min_visible_column (sheet));
4824 g_return_if_fail (last <= max_visible_column (sheet));
4827 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->column_title_window));
4829 gdk_window_begin_paint_region (sheet->column_title_window, region);
4831 for (col = first ; col <= last ; ++col)
4833 GdkRectangle allocation;
4834 gboolean is_sensitive = FALSE;
4837 button = g_sheet_column_get_button (sheet->column_geometry, col);
4840 g_sheet_column_start_pixel (sheet->column_geometry, col)
4842 allocation.x -= sheet->hadjustment->value;
4844 allocation.height = sheet->column_title_area.height;
4846 g_sheet_column_get_width (sheet->column_geometry, col);
4848 g_sheet_column_get_sensitivity (sheet->column_geometry, col);
4850 draw_button (sheet, sheet->column_title_window,
4851 button, is_sensitive, allocation);
4854 gdk_window_end_paint (sheet->column_title_window);
4859 draw_row_title_buttons_range (GtkSheet *sheet, gint first, gint last)
4863 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4865 if (!sheet->row_titles_visible) return;
4867 g_return_if_fail (first >= min_visible_row (sheet));
4868 g_return_if_fail (last <= max_visible_row (sheet));
4872 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->row_title_window));
4874 gdk_window_begin_paint_region (sheet->row_title_window, region);
4877 for (row = first; row <= last; ++row)
4879 GdkRectangle allocation;
4881 gboolean is_sensitive = FALSE;
4883 GtkSheetButton *button =
4884 g_sheet_row_get_button (sheet->row_geometry, row);
4886 allocation.y = g_sheet_row_start_pixel (sheet->row_geometry, row)
4888 allocation.y -= sheet->vadjustment->value;
4890 allocation.width = sheet->row_title_area.width;
4891 allocation.height = g_sheet_row_get_height (sheet->row_geometry, row);
4892 is_sensitive = g_sheet_row_get_sensitivity (sheet->row_geometry, row);
4894 draw_button (sheet, sheet->row_title_window,
4895 button, is_sensitive, allocation);
4898 gdk_window_end_paint (sheet->row_title_window);
4905 * vadjustment_value_changed
4906 * hadjustment_value_changed */
4909 adjust_scrollbars (GtkSheet *sheet)
4913 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4916 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
4918 if ( sheet->row_titles_visible)
4919 width -= sheet->row_title_area.width;
4921 if (sheet->column_titles_visible)
4922 height -= sheet->column_title_area.height;
4924 if (sheet->vadjustment)
4926 glong last_row = g_sheet_row_get_row_count (sheet->row_geometry) - 1;
4928 sheet->vadjustment->step_increment =
4930 g_sheet_row_get_height (sheet->row_geometry, last_row);
4932 sheet->vadjustment->page_increment =
4934 sheet->column_title_area.height -
4935 g_sheet_row_get_height (sheet->row_geometry, last_row);
4939 sheet->vadjustment->upper =
4940 g_sheet_row_start_pixel (sheet->row_geometry, last_row)
4942 g_sheet_row_get_height (sheet->row_geometry, last_row)
4945 sheet->vadjustment->lower = 0;
4946 sheet->vadjustment->page_size = height;
4948 g_signal_emit_by_name (sheet->vadjustment, "changed");
4951 if (sheet->hadjustment)
4954 sheet->hadjustment->step_increment = 1;
4956 sheet->hadjustment->page_increment = width;
4958 last_col = g_sheet_column_get_column_count (sheet->column_geometry) - 1;
4960 sheet->hadjustment->upper =
4961 g_sheet_column_start_pixel (sheet->column_geometry, last_col)
4963 g_sheet_column_get_width (sheet->column_geometry, last_col)
4966 sheet->hadjustment->lower = 0;
4967 sheet->hadjustment->page_size = width;
4969 g_signal_emit_by_name (sheet->hadjustment, "changed");
4974 vadjustment_value_changed (GtkAdjustment *adjustment,
4978 GtkSheet *sheet = GTK_SHEET (data);
4980 g_return_if_fail (adjustment != NULL);
4982 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
4985 gtk_widget_hide (sheet->entry_widget);
4988 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
4990 gdk_window_begin_paint_region (sheet->sheet_window, region);
4993 gtk_sheet_range_draw (sheet, NULL);
4994 draw_row_title_buttons (sheet);
4995 // size_allocate_global_button (sheet);
4997 gdk_window_end_paint (sheet->sheet_window);
5002 hadjustment_value_changed (GtkAdjustment *adjustment,
5006 GtkSheet *sheet = GTK_SHEET (data);
5008 g_return_if_fail (adjustment != NULL);
5010 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
5012 gtk_widget_hide (sheet->entry_widget);
5016 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
5018 gdk_window_begin_paint_region (sheet->sheet_window, region);
5020 gtk_sheet_range_draw (sheet, NULL);
5021 draw_column_title_buttons (sheet);
5022 // size_allocate_global_button (sheet);
5024 gdk_window_end_paint (sheet->sheet_window);
5028 /* COLUMN RESIZING */
5030 draw_xor_vline (GtkSheet *sheet)
5033 gint xpos = sheet->x_drag;
5034 gdk_drawable_get_size (sheet->sheet_window,
5038 if (sheet->row_titles_visible)
5039 xpos += sheet->row_title_area.width;
5041 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
5043 sheet->column_title_area.height,
5045 height + CELL_SPACING);
5050 draw_xor_hline (GtkSheet *sheet)
5054 gint ypos = sheet->y_drag;
5056 gdk_drawable_get_size (sheet->sheet_window,
5060 if (sheet->column_titles_visible)
5061 ypos += sheet->column_title_area.height;
5063 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
5064 sheet->row_title_area.width,
5066 width + CELL_SPACING,
5070 /* SELECTED RANGE */
5072 draw_xor_rectangle (GtkSheet *sheet, GtkSheetRange range)
5075 GdkRectangle clip_area, area;
5078 area.x = g_sheet_column_start_pixel (sheet->column_geometry, range.col0);
5079 area.y = g_sheet_row_start_pixel (sheet->row_geometry, range.row0);
5080 area.width = g_sheet_column_start_pixel (sheet->column_geometry, range.coli)- area.x+
5081 g_sheet_column_get_width (sheet->column_geometry, range.coli);
5082 area.height = g_sheet_row_start_pixel (sheet->row_geometry, range.rowi)- area.y +
5083 g_sheet_row_get_height (sheet->row_geometry, range.rowi);
5085 clip_area.x = sheet->row_title_area.width;
5086 clip_area.y = sheet->column_title_area.height;
5088 gdk_drawable_get_size (sheet->sheet_window,
5089 &clip_area.width, &clip_area.height);
5091 if (!sheet->row_titles_visible) clip_area.x = 0;
5092 if (!sheet->column_titles_visible) clip_area.y = 0;
5096 area.width = area.width + area.x;
5099 if (area.width > clip_area.width) area.width = clip_area.width + 10;
5102 area.height = area.height + area.y;
5105 if (area.height > clip_area.height) area.height = clip_area.height + 10;
5109 clip_area.width += 3;
5110 clip_area.height += 3;
5112 gdk_gc_get_values (sheet->xor_gc, &values);
5114 gdk_gc_set_clip_rectangle (sheet->xor_gc, &clip_area);
5116 gdk_draw_rectangle (sheet->sheet_window,
5119 area.x + i, area.y + i,
5120 area.width - 2 * i, area.height - 2 * i);
5123 gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
5125 gdk_gc_set_foreground (sheet->xor_gc, &values.foreground);
5129 /* this function returns the new width of the column being resized given
5130 * the COLUMN and X position of the cursor; the x cursor position is passed
5131 * in as a pointer and automaticaly corrected if it's outside the acceptable
5134 new_column_width (GtkSheet *sheet, gint column, gint *x)
5136 gint left_pos = g_sheet_column_start_pixel (sheet->column_geometry, column)
5137 - sheet->hadjustment->value;
5139 gint width = *x - left_pos;
5141 if ( width < sheet->column_requisition)
5143 width = sheet->column_requisition;
5144 *x = left_pos + width;
5147 g_sheet_column_set_width (sheet->column_geometry, column, width);
5149 draw_column_title_buttons (sheet);
5154 /* this function returns the new height of the row being resized given
5155 * the row and y position of the cursor; the y cursor position is passed
5156 * in as a pointer and automaticaly corrected if it's beyond min / max limits */
5158 new_row_height (GtkSheet *sheet, gint row, gint *y)
5164 min_height = sheet->row_requisition;
5166 /* you can't shrink a row to less than its minimum height */
5167 if (cy < g_sheet_row_start_pixel (sheet->row_geometry, row) + min_height)
5170 *y = cy = g_sheet_row_start_pixel (sheet->row_geometry, row) + min_height;
5173 /* calculate new row height making sure it doesn't end up
5174 * less than the minimum height */
5175 height = (cy - g_sheet_row_start_pixel (sheet->row_geometry, row));
5176 if (height < min_height)
5177 height = min_height;
5179 g_sheet_row_set_height (sheet->row_geometry, row, height);
5180 draw_row_title_buttons (sheet);
5186 gtk_sheet_set_column_width (GtkSheet *sheet,
5192 g_return_if_fail (sheet != NULL);
5193 g_return_if_fail (GTK_IS_SHEET (sheet));
5195 if (column < 0 || column >= g_sheet_column_get_column_count (sheet->column_geometry))
5198 gtk_sheet_column_size_request (sheet, column, &min_width);
5199 if (width < min_width) return;
5201 g_sheet_column_set_width (sheet->column_geometry, column, width);
5203 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
5205 draw_column_title_buttons (sheet);
5206 adjust_scrollbars (sheet);
5207 gtk_sheet_size_allocate_entry (sheet);
5208 gtk_sheet_range_draw (sheet, NULL);
5215 gtk_sheet_set_row_height (GtkSheet *sheet,
5221 g_return_if_fail (sheet != NULL);
5222 g_return_if_fail (GTK_IS_SHEET (sheet));
5224 if (row < 0 || row >= g_sheet_row_get_row_count (sheet->row_geometry))
5227 gtk_sheet_row_size_request (sheet, row, &min_height);
5228 if (height < min_height) return;
5230 g_sheet_row_set_height (sheet->row_geometry, row, height);
5232 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) )
5234 draw_row_title_buttons (sheet);
5235 adjust_scrollbars (sheet);
5236 gtk_sheet_size_allocate_entry (sheet);
5237 gtk_sheet_range_draw (sheet, NULL);
5242 gtk_sheet_get_attributes (const GtkSheet *sheet, gint row, gint col,
5243 GtkSheetCellAttr *attr)
5246 const GtkJustification *j ;
5247 const PangoFontDescription *font_desc ;
5248 const GtkSheetCellBorder *border ;
5249 GdkColormap *colormap;
5251 g_return_val_if_fail (sheet != NULL, FALSE);
5252 g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE);
5254 if (row < 0 || col < 0) return FALSE;
5257 attr->foreground = GTK_WIDGET (sheet)->style->black;
5258 attr->background = sheet->color[BG_COLOR];
5260 attr->border.width = 0;
5261 attr->border.line_style = GDK_LINE_SOLID;
5262 attr->border.cap_style = GDK_CAP_NOT_LAST;
5263 attr->border.join_style = GDK_JOIN_MITER;
5264 attr->border.mask = 0;
5265 attr->border.color = GTK_WIDGET (sheet)->style->black;
5266 attr->font_desc = GTK_WIDGET (sheet)->style->font_desc;
5268 attr->is_editable = g_sheet_model_is_editable (sheet->model, row, col);
5269 attr->is_visible = g_sheet_model_is_visible (sheet->model, row, col);
5272 colormap = gtk_widget_get_colormap (GTK_WIDGET (sheet));
5273 fg = g_sheet_model_get_foreground (sheet->model, row, col);
5276 gdk_colormap_alloc_color (colormap, fg, TRUE, TRUE);
5277 attr->foreground = *fg;
5280 bg = g_sheet_model_get_background (sheet->model, row, col);
5283 gdk_colormap_alloc_color (colormap, bg, TRUE, TRUE);
5284 attr->background = *bg;
5287 attr->justification =
5288 g_sheet_column_get_justification (sheet->column_geometry, col);
5290 j = g_sheet_model_get_justification (sheet->model, row, col);
5292 attr->justification = *j;
5294 font_desc = g_sheet_model_get_font_desc (sheet->model, row, col);
5295 if ( font_desc ) attr->font_desc = font_desc;
5297 border = g_sheet_model_get_cell_border (sheet->model, row, col);
5299 if ( border ) attr->border = *border;
5305 gtk_sheet_button_size_request (GtkSheet *sheet,
5306 const GtkSheetButton *button,
5307 GtkRequisition *button_requisition)
5309 GtkRequisition requisition;
5310 GtkRequisition label_requisition;
5312 label_requisition.height = DEFAULT_ROW_HEIGHT;
5313 label_requisition.width = COLUMN_MIN_WIDTH;
5315 requisition.height = DEFAULT_ROW_HEIGHT;
5316 requisition.width = COLUMN_MIN_WIDTH;
5319 *button_requisition = requisition;
5320 button_requisition->width = MAX (requisition.width, label_requisition.width);
5321 button_requisition->height = MAX (requisition.height, label_requisition.height);
5326 gtk_sheet_row_size_request (GtkSheet *sheet,
5330 GtkRequisition button_requisition;
5332 gtk_sheet_button_size_request (sheet,
5333 g_sheet_row_get_button (sheet->row_geometry, row),
5334 &button_requisition);
5336 *requisition = button_requisition.height;
5338 sheet->row_requisition = *requisition;
5342 gtk_sheet_column_size_request (GtkSheet *sheet,
5346 GtkRequisition button_requisition;
5348 GtkSheetButton *button = g_sheet_column_get_button (sheet->column_geometry, col);
5350 gtk_sheet_button_size_request (sheet,
5352 &button_requisition);
5354 gtk_sheet_button_free (button);
5356 *requisition = button_requisition.width;
5358 sheet->column_requisition = *requisition;
5363 gtk_sheet_forall (GtkContainer *container,
5364 gboolean include_internals,
5365 GtkCallback callback,
5366 gpointer callback_data)
5368 GtkSheet *sheet = GTK_SHEET (container);
5370 g_return_if_fail (callback != NULL);
5372 if (sheet->button && sheet->button->parent)
5373 (* callback) (sheet->button, callback_data);
5375 if (sheet->entry_container && GTK_IS_CONTAINER (sheet->entry_container))
5376 (* callback) (sheet->entry_container, callback_data);
5381 gtk_sheet_get_model (const GtkSheet *sheet)
5383 g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);
5385 return sheet->model;
5390 gtk_sheet_button_new (void)
5392 GtkSheetButton *button = g_malloc (sizeof (GtkSheetButton));
5394 button->state = GTK_STATE_NORMAL;
5395 button->label = NULL;
5396 button->label_visible = TRUE;
5397 button->justification = GTK_JUSTIFY_FILL;
5404 gtk_sheet_button_free (GtkSheetButton *button)
5406 if (!button) return ;
5408 g_free (button->label);
5414 append_cell_text (GString *string, const GtkSheet *sheet, gint r, gint c)
5416 gchar *celltext = gtk_sheet_cell_get_text (sheet, r, c);
5418 if ( NULL == celltext)
5421 g_string_append (string, celltext);
5427 range_to_text (const GtkSheet *sheet)
5432 if ( !gtk_sheet_range_isvisible (sheet, sheet->range))
5435 string = g_string_sized_new (80);
5437 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
5439 for (c = sheet->range.col0; c < sheet->range.coli; ++c)
5441 append_cell_text (string, sheet, r, c);
5442 g_string_append (string, "\t");
5444 append_cell_text (string, sheet, r, c);
5445 if ( r < sheet->range.rowi)
5446 g_string_append (string, "\n");
5453 range_to_html (const GtkSheet *sheet)
5458 if ( !gtk_sheet_range_isvisible (sheet, sheet->range))
5461 string = g_string_sized_new (480);
5463 g_string_append (string, "<html>\n");
5464 g_string_append (string, "<body>\n");
5465 g_string_append (string, "<table>\n");
5466 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
5468 g_string_append (string, "<tr>\n");
5469 for (c = sheet->range.col0; c <= sheet->range.coli; ++c)
5471 g_string_append (string, "<td>");
5472 append_cell_text (string, sheet, r, c);
5473 g_string_append (string, "</td>\n");
5475 g_string_append (string, "</tr>\n");
5477 g_string_append (string, "</table>\n");
5478 g_string_append (string, "</body>\n");
5479 g_string_append (string, "</html>\n");
5491 primary_get_cb (GtkClipboard *clipboard,
5492 GtkSelectionData *selection_data,
5496 GtkSheet *sheet = GTK_SHEET (data);
5497 GString *string = NULL;
5501 case SELECT_FMT_TEXT:
5502 string = range_to_text (sheet);
5504 case SELECT_FMT_HTML:
5505 string = range_to_html (sheet);
5508 g_assert_not_reached ();
5511 gtk_selection_data_set (selection_data, selection_data->target,
5513 (const guchar *) string->str, string->len);
5514 g_string_free (string, TRUE);
5518 primary_clear_cb (GtkClipboard *clipboard,
5521 GtkSheet *sheet = GTK_SHEET (data);
5522 if ( ! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
5525 gtk_sheet_real_unselect_range (sheet, NULL);
5529 gtk_sheet_update_primary_selection (GtkSheet *sheet)
5531 static const GtkTargetEntry targets[] = {
5532 { "UTF8_STRING", 0, SELECT_FMT_TEXT },
5533 { "STRING", 0, SELECT_FMT_TEXT },
5534 { "TEXT", 0, SELECT_FMT_TEXT },
5535 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
5536 { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
5537 { "text/plain", 0, SELECT_FMT_TEXT },
5538 { "text/html", 0, SELECT_FMT_HTML }
5541 GtkClipboard *clipboard;
5543 if (!GTK_WIDGET_REALIZED (sheet))
5546 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (sheet),
5547 GDK_SELECTION_PRIMARY);
5549 if (gtk_sheet_range_isvisible (sheet, sheet->range))
5551 if (!gtk_clipboard_set_with_owner (clipboard, targets,
5552 G_N_ELEMENTS (targets),
5553 primary_get_cb, primary_clear_cb,
5555 primary_clear_cb (clipboard, sheet);
5559 if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (sheet))
5560 gtk_clipboard_clear (clipboard);