2 Copyright (C) 2006, 2008, 2009 Free Software Foundation
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 This file is derived from the gtksheet.c and extensively modified for the
19 requirements of PSPPIRE. The changes are copyright by the
20 Free Software Foundation. The copyright notice for the original work is
24 /* GtkSheet widget for Gtk+.
25 * Copyright (C) 1999-2001 Adrian E. Feiguin <adrian@ifir.ifir.edu.ar>
27 * Based on GtkClist widget by Jay Painter, but major changes.
28 * Memory allocation routines inspired on SC (Spreadsheet Calculator)
30 * This library is free software; you can redistribute it and/or
31 * modify it under the terms of the GNU Lesser General Public
32 * License as published by the Free Software Foundation; either
33 * version 2.1 of the License, or (at your option) any later version.
35 * This library is distributed in the hope that it will be useful,
36 * but WITHOUT ANY WARRANTY; without even the implied warranty of
37 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
38 * Lesser General Public License for more details.
40 * You should have received a copy of the GNU Lesser General Public
41 * License along with this library; if not, write to the Free Software
42 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
46 * SECTION:psppiresheet
47 * @short_description: spreadsheet widget for gtk2
49 * PsppireSheet is a matrix widget for GTK+. It consists of an scrollable grid of
50 * cells where you can allocate text. Cell contents can be edited interactively
51 * through a specially designed entry, GtkItemEntry.
61 #include <gdk/gdkkeysyms.h>
62 #include <gtk/gtksignal.h>
63 #include <gtk/gtkbutton.h>
64 #include <gtk/gtkadjustment.h>
65 #include <gtk/gtktypeutils.h>
66 #include <gtk/gtkentry.h>
67 #include <gtk/gtkcontainer.h>
68 #include <pango/pango.h>
69 #include "psppire-sheet.h"
70 #include <ui/gui/psppire-marshal.h>
71 #include <ui/gui/sheet/psppire-sheetmodel.h>
72 #include <ui/gui/sheet/psppire-axis.h>
73 #include <libpspp/misc.h>
80 PSPPIRE_SHEET_IN_XDRAG = 1 << 1,
81 PSPPIRE_SHEET_IN_YDRAG = 1 << 2,
82 PSPPIRE_SHEET_IN_DRAG = 1 << 3,
84 /* This flag is set when the user is actually in the process
85 of making a selection - ie while the mouse button is
88 PSPPIRE_SHEET_IN_SELECTION = 1 << 4,
90 PSPPIRE_SHEET_IN_RESIZE = 1 << 5
93 #define PSPPIRE_SHEET_FLAGS(sheet) (PSPPIRE_SHEET (sheet)->flags)
94 #define PSPPIRE_SHEET_SET_FLAGS(sheet,flag) (PSPPIRE_SHEET_FLAGS (sheet) |= (flag))
95 #define PSPPIRE_SHEET_UNSET_FLAGS(sheet,flag) (PSPPIRE_SHEET_FLAGS (sheet) &= ~ (flag))
97 #define PSPPIRE_SHEET_IN_XDRAG(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_XDRAG)
98 #define PSPPIRE_SHEET_IN_YDRAG(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_YDRAG)
99 #define PSPPIRE_SHEET_IN_DRAG(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_DRAG)
100 #define PSPPIRE_SHEET_IN_SELECTION(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_SELECTION)
101 #define PSPPIRE_SHEET_IN_RESIZE(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_RESIZE)
103 #define CELL_SPACING 1
105 #define TIMEOUT_HOVER 300
106 #define COLUMN_MIN_WIDTH 10
107 #define COLUMN_TITLES_HEIGHT 4
108 #define DEFAULT_COLUMN_WIDTH 80
109 #define DEFAULT_ROW_HEIGHT 25
111 static void set_entry_widget_font (PsppireSheet *sheet);
113 static void psppire_sheet_update_primary_selection (PsppireSheet *sheet);
114 static void draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint n);
115 static void draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint n);
116 static void redraw_range (PsppireSheet *sheet, PsppireSheetRange *range);
119 static void set_row_height (PsppireSheet *sheet,
123 static void destroy_hover_window (PsppireSheetHoverTitle *);
124 static PsppireSheetHoverTitle *create_hover_window (void);
127 dispose_string (const PsppireSheet *sheet, gchar *text)
129 PsppireSheetModel *model = psppire_sheet_get_model (sheet);
134 if (psppire_sheet_model_free_strings (model))
139 /* FIXME: Why bother with these two ? */
141 /* returns the column index from a pixel location */
143 column_from_xpixel (const PsppireSheet *sheet, gint pixel)
145 return psppire_axis_unit_at_pixel (sheet->haxis, pixel);
149 row_from_ypixel (const PsppireSheet *sheet, gint pixel)
151 return psppire_axis_unit_at_pixel (sheet->vaxis, pixel);
155 /* Return the lowest row number which is wholly or partially on
156 the visible range of the sheet */
158 min_visible_row (const PsppireSheet *sheet)
160 return row_from_ypixel (sheet, sheet->vadjustment->value);
164 min_fully_visible_row (const PsppireSheet *sheet)
166 glong row = min_visible_row (sheet);
168 if ( psppire_axis_start_pixel (sheet->vaxis, row) < sheet->vadjustment->value)
175 max_visible_row (const PsppireSheet *sheet)
177 return row_from_ypixel (sheet, sheet->vadjustment->value + sheet->vadjustment->page_size);
182 max_fully_visible_row (const PsppireSheet *sheet)
184 glong row = max_visible_row (sheet);
186 if ( psppire_axis_start_pixel (sheet->vaxis, row)
188 psppire_axis_unit_size (sheet->vaxis, row)
189 > sheet->vadjustment->value)
196 /* Returns the lowest column number which is wholly or partially
199 min_visible_column (const PsppireSheet *sheet)
201 return column_from_xpixel (sheet, sheet->hadjustment->value);
205 min_fully_visible_column (const PsppireSheet *sheet)
207 glong col = min_visible_column (sheet);
209 if ( psppire_axis_start_pixel (sheet->haxis, col) < sheet->hadjustment->value)
216 /* Returns the highest column number which is wholly or partially
219 max_visible_column (const PsppireSheet *sheet)
221 return column_from_xpixel (sheet, sheet->hadjustment->value + sheet->hadjustment->page_size);
225 max_fully_visible_column (const PsppireSheet *sheet)
227 glong col = max_visible_column (sheet);
229 if ( psppire_axis_start_pixel (sheet->haxis, col)
231 psppire_axis_unit_size (sheet->haxis, col)
232 > sheet->hadjustment->value)
240 /* The size of the region (in pixels) around the row/column boundaries
241 where the height/width may be grabbed to change size */
245 on_column_boundary (const PsppireSheet *sheet, gint x, gint *column)
250 x += sheet->hadjustment->value;
255 col = column_from_xpixel (sheet, x);
257 pixel = x - DRAG_WIDTH / 2;
261 if ( column_from_xpixel (sheet, pixel) < col )
267 if ( column_from_xpixel (sheet, x + DRAG_WIDTH / 2) > col )
277 on_row_boundary (const PsppireSheet *sheet, gint y, gint *row)
282 y += sheet->vadjustment->value;
287 r = row_from_ypixel (sheet, y);
289 pixel = y - DRAG_WIDTH / 2;
293 if ( row_from_ypixel (sheet, pixel) < r )
299 if ( row_from_ypixel (sheet, y + DRAG_WIDTH / 2) > r )
309 static inline gboolean
310 POSSIBLE_DRAG (const PsppireSheet *sheet, gint x, gint y,
311 gint *drag_row, gint *drag_column)
315 /* Can't drag if nothing is selected */
316 if ( sheet->range.row0 < 0 || sheet->range.rowi < 0 ||
317 sheet->range.col0 < 0 || sheet->range.coli < 0 )
320 *drag_column = column_from_xpixel (sheet, x);
321 *drag_row = row_from_ypixel (sheet, y);
323 if (x >= psppire_axis_start_pixel (sheet->haxis, sheet->range.col0) - DRAG_WIDTH / 2 &&
324 x <= psppire_axis_start_pixel (sheet->haxis, sheet->range.coli) +
325 psppire_axis_unit_size (sheet->haxis, sheet->range.coli) + DRAG_WIDTH / 2)
327 ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.row0);
328 if (y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2)
330 *drag_row = sheet->range.row0;
333 ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) +
334 psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi);
335 if (y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2)
337 *drag_row = sheet->range.rowi;
342 if (y >= psppire_axis_start_pixel (sheet->vaxis, sheet->range.row0) - DRAG_WIDTH / 2 &&
343 y <= psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) +
344 psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi) + DRAG_WIDTH / 2)
346 xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.col0);
347 if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2)
349 *drag_column = sheet->range.col0;
352 xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.coli) +
353 psppire_axis_unit_size (sheet->haxis, sheet->range.coli);
354 if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2)
356 *drag_column = sheet->range.coli;
364 static inline gboolean
365 POSSIBLE_RESIZE (const PsppireSheet *sheet, gint x, gint y,
366 gint *drag_row, gint *drag_column)
370 /* Can't drag if nothing is selected */
371 if ( sheet->range.row0 < 0 || sheet->range.rowi < 0 ||
372 sheet->range.col0 < 0 || sheet->range.coli < 0 )
375 xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.coli)+
376 psppire_axis_unit_size (sheet->haxis, sheet->range.coli);
378 ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) +
379 psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi);
381 if (sheet->select_status == PSPPIRE_SHEET_COLUMN_SELECTED)
382 ydrag = psppire_axis_start_pixel (sheet->vaxis, min_visible_row (sheet));
384 if (sheet->select_status == PSPPIRE_SHEET_ROW_SELECTED)
385 xdrag = psppire_axis_start_pixel (sheet->haxis, min_visible_column (sheet));
387 *drag_column = column_from_xpixel (sheet, x);
388 *drag_row = row_from_ypixel (sheet, y);
390 if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2 &&
391 y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2) return TRUE;
398 rectangle_from_range (PsppireSheet *sheet, const PsppireSheetRange *range,
401 g_return_val_if_fail (range, FALSE);
403 r->x = psppire_axis_start_pixel (sheet->haxis, range->col0);
404 r->x -= round (sheet->hadjustment->value);
406 r->y = psppire_axis_start_pixel (sheet->vaxis, range->row0);
407 r->y -= round (sheet->vadjustment->value);
409 r->width = psppire_axis_start_pixel (sheet->haxis, range->coli) -
410 psppire_axis_start_pixel (sheet->haxis, range->col0) +
411 psppire_axis_unit_size (sheet->haxis, range->coli);
413 r->height = psppire_axis_start_pixel (sheet->vaxis, range->rowi) -
414 psppire_axis_start_pixel (sheet->vaxis, range->row0) +
415 psppire_axis_unit_size (sheet->vaxis, range->rowi);
417 if ( sheet->column_titles_visible)
419 r->y += sheet->column_title_area.height;
422 if ( sheet->row_titles_visible)
424 r->x += sheet->row_title_area.width;
431 rectangle_from_cell (PsppireSheet *sheet, gint row, gint col,
434 PsppireSheetRange range;
435 g_return_val_if_fail (row >= 0, FALSE);
436 g_return_val_if_fail (col >= 0, FALSE);
438 range.row0 = range.rowi = row;
439 range.col0 = range.coli = col;
441 return rectangle_from_range (sheet, &range, r);
445 static void psppire_sheet_class_init (PsppireSheetClass *klass);
446 static void psppire_sheet_init (PsppireSheet *sheet);
447 static void psppire_sheet_dispose (GObject *object);
448 static void psppire_sheet_finalize (GObject *object);
449 static void psppire_sheet_style_set (GtkWidget *widget,
450 GtkStyle *previous_style);
451 static void psppire_sheet_realize (GtkWidget *widget);
452 static void psppire_sheet_unrealize (GtkWidget *widget);
453 static void psppire_sheet_map (GtkWidget *widget);
454 static void psppire_sheet_unmap (GtkWidget *widget);
455 static gint psppire_sheet_expose (GtkWidget *widget,
456 GdkEventExpose *event);
458 static void psppire_sheet_forall (GtkContainer *container,
459 gboolean include_internals,
460 GtkCallback callback,
461 gpointer callback_data);
463 static gboolean psppire_sheet_set_scroll_adjustments (PsppireSheet *sheet,
464 GtkAdjustment *hadjustment,
465 GtkAdjustment *vadjustment);
467 static gint psppire_sheet_button_press (GtkWidget *widget,
468 GdkEventButton *event);
469 static gint psppire_sheet_button_release (GtkWidget *widget,
470 GdkEventButton *event);
471 static gint psppire_sheet_motion (GtkWidget *widget,
472 GdkEventMotion *event);
473 static gboolean psppire_sheet_crossing_notify (GtkWidget *widget,
474 GdkEventCrossing *event);
475 static gint psppire_sheet_entry_key_press (GtkWidget *widget,
477 static gboolean psppire_sheet_key_press (GtkWidget *widget,
479 static void psppire_sheet_size_request (GtkWidget *widget,
480 GtkRequisition *requisition);
481 static void psppire_sheet_size_allocate (GtkWidget *widget,
482 GtkAllocation *allocation);
484 static gboolean psppire_sheet_focus_in (GtkWidget *widget,
485 GdkEventFocus *event);
489 static gboolean psppire_sheet_range_isvisible (const PsppireSheet *sheet,
490 const PsppireSheetRange *range);
491 static gboolean psppire_sheet_cell_isvisible (PsppireSheet *sheet,
492 gint row, gint column);
493 /* Drawing Routines */
496 static void psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint column);
499 /* draw visible part of range. */
500 static void draw_sheet_region (PsppireSheet *sheet, GdkRegion *region);
505 static void psppire_sheet_real_select_range (PsppireSheet *sheet,
506 const PsppireSheetRange *range);
507 static void psppire_sheet_real_unselect_range (PsppireSheet *sheet,
508 const PsppireSheetRange *range);
509 static void psppire_sheet_draw_border (PsppireSheet *sheet,
510 PsppireSheetRange range);
512 /* Active Cell handling */
514 static void psppire_sheet_hide_entry_widget (PsppireSheet *sheet);
515 static void change_active_cell (PsppireSheet *sheet, gint row, gint col);
516 static gboolean psppire_sheet_draw_active_cell (PsppireSheet *sheet);
517 static void psppire_sheet_show_entry_widget (PsppireSheet *sheet);
518 static gboolean psppire_sheet_click_cell (PsppireSheet *sheet,
525 static void adjust_scrollbars (PsppireSheet *sheet);
526 static void vadjustment_value_changed (GtkAdjustment *adjustment,
528 static void hadjustment_value_changed (GtkAdjustment *adjustment,
532 static void draw_xor_vline (PsppireSheet *sheet);
533 static void draw_xor_hline (PsppireSheet *sheet);
534 static void draw_xor_rectangle (PsppireSheet *sheet,
535 PsppireSheetRange range);
539 static void create_global_button (PsppireSheet *sheet);
540 static void global_button_clicked (GtkWidget *widget,
544 static void create_sheet_entry (PsppireSheet *sheet);
545 static void psppire_sheet_size_allocate_entry (PsppireSheet *sheet);
547 /* Sheet button gadgets */
549 static void draw_column_title_buttons (PsppireSheet *sheet);
550 static void draw_row_title_buttons (PsppireSheet *sheet);
553 static void size_allocate_global_button (PsppireSheet *sheet);
554 static void psppire_sheet_button_size_request (PsppireSheet *sheet,
555 const PsppireSheetButton *button,
556 GtkRequisition *requisition);
558 static void psppire_sheet_real_cell_clear (PsppireSheet *sheet,
580 static GtkContainerClass *parent_class = NULL;
581 static guint sheet_signals[LAST_SIGNAL] = { 0 };
585 psppire_sheet_get_type ()
587 static GType sheet_type = 0;
591 static const GTypeInfo sheet_info =
593 sizeof (PsppireSheetClass),
596 (GClassInitFunc) psppire_sheet_class_init,
599 sizeof (PsppireSheet),
601 (GInstanceInitFunc) psppire_sheet_init,
606 g_type_register_static (GTK_TYPE_BIN, "PsppireSheet",
614 static PsppireSheetRange*
615 psppire_sheet_range_copy (const PsppireSheetRange *range)
617 PsppireSheetRange *new_range;
619 g_return_val_if_fail (range != NULL, NULL);
621 new_range = g_new (PsppireSheetRange, 1);
629 psppire_sheet_range_free (PsppireSheetRange *range)
631 g_return_if_fail (range != NULL);
637 psppire_sheet_range_get_type (void)
639 static GType sheet_range_type = 0;
641 if (!sheet_range_type)
644 g_boxed_type_register_static ("PsppireSheetRange",
645 (GBoxedCopyFunc) psppire_sheet_range_copy,
646 (GBoxedFreeFunc) psppire_sheet_range_free);
649 return sheet_range_type;
652 static PsppireSheetCell*
653 psppire_sheet_cell_copy (const PsppireSheetCell *cell)
655 PsppireSheetCell *new_cell;
657 g_return_val_if_fail (cell != NULL, NULL);
659 new_cell = g_new (PsppireSheetCell, 1);
667 psppire_sheet_cell_free (PsppireSheetCell *cell)
669 g_return_if_fail (cell != NULL);
675 psppire_sheet_cell_get_type (void)
677 static GType sheet_cell_type = 0;
679 if (!sheet_cell_type)
682 g_boxed_type_register_static ("PsppireSheetCell",
683 (GBoxedCopyFunc) psppire_sheet_cell_copy,
684 (GBoxedFreeFunc) psppire_sheet_cell_free);
687 return sheet_cell_type;
702 resize_column (PsppireSheet *sheet, gint unit, glong size)
704 PsppireSheetRange range;
706 range.coli = max_visible_column (sheet);
707 range.row0 = min_visible_row (sheet);
708 range.rowi = max_visible_row (sheet);
710 redraw_range (sheet, &range);
712 draw_column_title_buttons_range (sheet, range.col0, range.coli);
717 psppire_sheet_set_horizontal_axis (PsppireSheet *sheet, PsppireAxis *a)
720 g_object_unref (sheet->haxis);
723 g_signal_connect_swapped (a, "resize-unit", G_CALLBACK (resize_column), sheet);
726 g_object_ref (sheet->haxis);
730 resize_row (PsppireSheet *sheet, gint unit, glong size)
732 PsppireSheetRange range;
733 range.col0 = min_visible_column (sheet);
734 range.coli = max_visible_column (sheet);
736 range.rowi = max_visible_row (sheet);
738 redraw_range (sheet, &range);
740 draw_row_title_buttons_range (sheet, range.row0, range.rowi);
744 psppire_sheet_set_vertical_axis (PsppireSheet *sheet, PsppireAxis *a)
747 g_object_unref (sheet->vaxis);
751 g_signal_connect_swapped (a, "resize-unit", G_CALLBACK (resize_row), sheet);
754 g_object_ref (sheet->vaxis);
757 static const GtkBorder default_cell_padding = {3, 3, 3, 3};
760 psppire_sheet_set_property (GObject *object,
766 PsppireSheet *sheet = PSPPIRE_SHEET (object);
770 case PROP_CELL_PADDING:
771 if ( sheet->cell_padding)
772 g_boxed_free (GTK_TYPE_BORDER, sheet->cell_padding);
774 sheet->cell_padding = g_value_dup_boxed (value);
776 if (NULL == sheet->cell_padding)
777 sheet->cell_padding = g_boxed_copy (GTK_TYPE_BORDER,
778 &default_cell_padding);
781 g_object_set (sheet->vaxis, "padding",
782 sheet->cell_padding->top + sheet->cell_padding->bottom,
786 g_object_set (sheet->haxis, "padding",
787 sheet->cell_padding->left + sheet->cell_padding->right,
791 psppire_sheet_set_vertical_axis (sheet, g_value_get_pointer (value));
792 g_object_set (sheet->vaxis, "padding",
793 sheet->cell_padding->top + sheet->cell_padding->bottom,
797 psppire_sheet_set_horizontal_axis (sheet, g_value_get_pointer (value));
798 g_object_set (sheet->haxis, "padding",
799 sheet->cell_padding->left + sheet->cell_padding->right,
803 psppire_sheet_set_model (sheet, g_value_get_pointer (value));
806 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
812 psppire_sheet_get_property (GObject *object,
817 PsppireSheet *sheet = PSPPIRE_SHEET (object);
821 case PROP_CELL_PADDING:
822 g_value_set_boxed (value, sheet->cell_padding);
825 g_value_set_pointer (value, sheet->vaxis);
828 g_value_set_pointer (value, sheet->haxis);
831 g_value_set_pointer (value, sheet->model);
834 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
841 psppire_sheet_class_init (PsppireSheetClass *klass)
843 GObjectClass *object_class = G_OBJECT_CLASS (klass);
845 GParamSpec *haxis_spec ;
846 GParamSpec *vaxis_spec ;
847 GParamSpec *model_spec ;
848 GParamSpec *cell_padding_spec ;
850 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
851 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
853 parent_class = g_type_class_peek_parent (klass);
856 * PsppireSheet::select-row
857 * @sheet: the sheet widget that emitted the signal
858 * @row: the newly selected row index
860 * A row has been selected.
862 sheet_signals[SELECT_ROW] =
863 g_signal_new ("select-row",
864 G_TYPE_FROM_CLASS (object_class),
866 offsetof (PsppireSheetClass, select_row),
868 g_cclosure_marshal_VOID__INT,
875 * PsppireSheet::select - column
876 * @sheet: the sheet widget that emitted the signal
877 * @column: the newly selected column index
879 * A column has been selected.
881 sheet_signals[SELECT_COLUMN] =
882 g_signal_new ("select-column",
883 G_TYPE_FROM_CLASS (object_class),
885 offsetof (PsppireSheetClass, select_column),
887 g_cclosure_marshal_VOID__INT,
894 * PsppireSheet::double-click-row
895 * @sheet: the sheet widget that emitted the signal
896 * @row: the row that was double clicked.
898 * A row's title button has been double clicked
900 sheet_signals[DOUBLE_CLICK_ROW] =
901 g_signal_new ("double-click-row",
902 G_TYPE_FROM_CLASS (object_class),
906 g_cclosure_marshal_VOID__INT,
913 * PsppireSheet::double-click-column
914 * @sheet: the sheet widget that emitted the signal
915 * @column: the column that was double clicked.
917 * A column's title button has been double clicked
919 sheet_signals[DOUBLE_CLICK_COLUMN] =
920 g_signal_new ("double-click-column",
921 G_TYPE_FROM_CLASS (object_class),
925 g_cclosure_marshal_VOID__INT,
932 * PsppireSheet::button-event-column
933 * @sheet: the sheet widget that emitted the signal
934 * @column: the column on which the event occured.
936 * A button event occured on a column title button
938 sheet_signals[BUTTON_EVENT_COLUMN] =
939 g_signal_new ("button-event-column",
940 G_TYPE_FROM_CLASS (object_class),
944 psppire_marshal_VOID__INT_POINTER,
953 * PsppireSheet::button-event-row
954 * @sheet: the sheet widget that emitted the signal
955 * @column: the column on which the event occured.
957 * A button event occured on a row title button
959 sheet_signals[BUTTON_EVENT_ROW] =
960 g_signal_new ("button-event-row",
961 G_TYPE_FROM_CLASS (object_class),
965 psppire_marshal_VOID__INT_POINTER,
973 sheet_signals[SELECT_RANGE] =
974 g_signal_new ("select-range",
975 G_TYPE_FROM_CLASS (object_class),
977 offsetof (PsppireSheetClass, select_range),
979 g_cclosure_marshal_VOID__BOXED,
982 PSPPIRE_TYPE_SHEET_RANGE);
985 sheet_signals[RESIZE_RANGE] =
986 g_signal_new ("resize-range",
987 G_TYPE_FROM_CLASS (object_class),
989 offsetof (PsppireSheetClass, resize_range),
991 psppire_marshal_VOID__BOXED_BOXED,
994 PSPPIRE_TYPE_SHEET_RANGE, PSPPIRE_TYPE_SHEET_RANGE
997 sheet_signals[MOVE_RANGE] =
998 g_signal_new ("move-range",
999 G_TYPE_FROM_CLASS (object_class),
1001 offsetof (PsppireSheetClass, move_range),
1003 psppire_marshal_VOID__BOXED_BOXED,
1006 PSPPIRE_TYPE_SHEET_RANGE, PSPPIRE_TYPE_SHEET_RANGE
1009 sheet_signals[TRAVERSE] =
1010 g_signal_new ("traverse",
1011 G_TYPE_FROM_CLASS (object_class),
1013 offsetof (PsppireSheetClass, traverse),
1015 psppire_marshal_BOOLEAN__BOXED_POINTER,
1017 PSPPIRE_TYPE_SHEET_CELL,
1021 sheet_signals[ACTIVATE] =
1022 g_signal_new ("activate",
1023 G_TYPE_FROM_CLASS (object_class),
1025 offsetof (PsppireSheetClass, activate),
1027 psppire_marshal_VOID__INT_INT_INT_INT,
1029 G_TYPE_INT, G_TYPE_INT,
1030 G_TYPE_INT, G_TYPE_INT);
1032 widget_class->set_scroll_adjustments_signal =
1033 g_signal_new ("set-scroll-adjustments",
1034 G_TYPE_FROM_CLASS (object_class),
1036 offsetof (PsppireSheetClass, set_scroll_adjustments),
1038 psppire_marshal_VOID__OBJECT_OBJECT,
1039 G_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
1042 container_class->add = NULL;
1043 container_class->remove = NULL;
1044 container_class->forall = psppire_sheet_forall;
1045 container_class->set_focus_child = NULL;
1047 object_class->dispose = psppire_sheet_dispose;
1048 object_class->finalize = psppire_sheet_finalize;
1051 g_param_spec_boxed ("cell-padding",
1053 "The space between a cell's contents and its border",
1055 G_PARAM_CONSTRUCT | G_PARAM_READABLE | G_PARAM_WRITABLE);
1058 g_param_spec_pointer ("vertical-axis",
1060 "A pointer to the PsppireAxis object for the rows",
1061 G_PARAM_READABLE | G_PARAM_WRITABLE );
1064 g_param_spec_pointer ("horizontal-axis",
1066 "A pointer to the PsppireAxis object for the columns",
1067 G_PARAM_READABLE | G_PARAM_WRITABLE );
1070 g_param_spec_pointer ("model",
1072 "A pointer to the data model",
1073 G_PARAM_READABLE | G_PARAM_WRITABLE );
1076 object_class->set_property = psppire_sheet_set_property;
1077 object_class->get_property = psppire_sheet_get_property;
1079 g_object_class_install_property (object_class,
1083 g_object_class_install_property (object_class,
1087 g_object_class_install_property (object_class,
1091 g_object_class_install_property (object_class,
1096 widget_class->realize = psppire_sheet_realize;
1097 widget_class->unrealize = psppire_sheet_unrealize;
1098 widget_class->map = psppire_sheet_map;
1099 widget_class->unmap = psppire_sheet_unmap;
1100 widget_class->style_set = psppire_sheet_style_set;
1101 widget_class->button_press_event = psppire_sheet_button_press;
1102 widget_class->button_release_event = psppire_sheet_button_release;
1103 widget_class->motion_notify_event = psppire_sheet_motion;
1104 widget_class->enter_notify_event = psppire_sheet_crossing_notify;
1105 widget_class->leave_notify_event = psppire_sheet_crossing_notify;
1106 widget_class->key_press_event = psppire_sheet_key_press;
1107 widget_class->expose_event = psppire_sheet_expose;
1108 widget_class->size_request = psppire_sheet_size_request;
1109 widget_class->size_allocate = psppire_sheet_size_allocate;
1110 widget_class->focus_in_event = psppire_sheet_focus_in;
1111 widget_class->focus_out_event = NULL;
1113 klass->set_scroll_adjustments = psppire_sheet_set_scroll_adjustments;
1114 klass->select_row = NULL;
1115 klass->select_column = NULL;
1116 klass->select_range = NULL;
1117 klass->resize_range = NULL;
1118 klass->move_range = NULL;
1119 klass->traverse = NULL;
1120 klass->activate = NULL;
1121 klass->changed = NULL;
1125 psppire_sheet_init (PsppireSheet *sheet)
1127 sheet->model = NULL;
1128 sheet->haxis = NULL;
1129 sheet->vaxis = NULL;
1132 sheet->selection_mode = GTK_SELECTION_NONE;
1133 sheet->select_status = PSPPIRE_SHEET_NORMAL;
1135 GTK_WIDGET_UNSET_FLAGS (sheet, GTK_NO_WINDOW);
1136 GTK_WIDGET_SET_FLAGS (sheet, GTK_CAN_FOCUS);
1138 sheet->column_title_window = NULL;
1139 sheet->column_title_area.x = 0;
1140 sheet->column_title_area.y = 0;
1141 sheet->column_title_area.width = 0;
1142 sheet->column_title_area.height = DEFAULT_ROW_HEIGHT;
1144 sheet->row_title_window = NULL;
1145 sheet->row_title_area.x = 0;
1146 sheet->row_title_area.y = 0;
1147 sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
1148 sheet->row_title_area.height = 0;
1151 sheet->active_cell.row = 0;
1152 sheet->active_cell.col = 0;
1154 sheet->range.row0 = 0;
1155 sheet->range.rowi = 0;
1156 sheet->range.col0 = 0;
1157 sheet->range.coli = 0;
1159 sheet->sheet_window = NULL;
1160 sheet->entry_widget = NULL;
1161 sheet->button = NULL;
1163 sheet->hadjustment = NULL;
1164 sheet->vadjustment = NULL;
1166 sheet->cursor_drag = NULL;
1168 sheet->xor_gc = NULL;
1169 sheet->fg_gc = NULL;
1170 sheet->bg_gc = NULL;
1173 sheet->show_grid = TRUE;
1175 sheet->motion_timer = 0;
1177 sheet->row_titles_visible = TRUE;
1178 sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
1180 sheet->column_titles_visible = TRUE;
1183 /* create sheet entry */
1184 sheet->entry_type = GTK_TYPE_ENTRY;
1185 create_sheet_entry (sheet);
1187 /* create global selection button */
1188 create_global_button (sheet);
1192 /* Cause RANGE to be redrawn. If RANGE is null, then the
1193 entire visible range will be redrawn.
1196 redraw_range (PsppireSheet *sheet, PsppireSheetRange *range)
1200 if ( ! GTK_WIDGET_REALIZED (sheet))
1203 if ( NULL != range )
1204 rectangle_from_range (sheet, range, &rect);
1207 GdkRegion *r = gdk_drawable_get_visible_region (sheet->sheet_window);
1208 gdk_region_get_clipbox (r, &rect);
1210 if ( sheet->column_titles_visible)
1212 rect.y += sheet->column_title_area.height;
1213 rect.height -= sheet->column_title_area.height;
1216 if ( sheet->row_titles_visible)
1218 rect.x += sheet->row_title_area.width;
1219 rect.width -= sheet->row_title_area.width;
1223 gdk_window_invalidate_rect (sheet->sheet_window, &rect, FALSE);
1227 /* Callback which occurs whenever columns are inserted / deleted in the model */
1229 columns_inserted_deleted_callback (PsppireSheetModel *model, gint first_column,
1233 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1235 PsppireSheetRange range;
1236 gint model_columns = psppire_sheet_model_get_column_count (model);
1239 /* Need to update all the columns starting from the first column and onwards.
1240 * Previous column are unchanged, so don't need to be updated.
1242 range.col0 = first_column;
1244 range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1245 range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1247 adjust_scrollbars (sheet);
1249 if (sheet->active_cell.col >= model_columns)
1250 change_active_cell (sheet, sheet->active_cell.row, model_columns - 1);
1252 draw_column_title_buttons_range (sheet,
1253 first_column, max_visible_column (sheet));
1256 redraw_range (sheet, &range);
1262 /* Callback which occurs whenever rows are inserted / deleted in the model */
1264 rows_inserted_deleted_callback (PsppireSheetModel *model, gint first_row,
1265 gint n_rows, gpointer data)
1267 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1269 PsppireSheetRange range;
1271 gint model_rows = psppire_sheet_model_get_row_count (model);
1273 /* Need to update all the rows starting from the first row and onwards.
1274 * Previous rows are unchanged, so don't need to be updated.
1276 range.row0 = first_row;
1278 range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1279 range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1281 adjust_scrollbars (sheet);
1283 if (sheet->active_cell.row >= model_rows)
1284 change_active_cell (sheet, model_rows - 1, sheet->active_cell.col);
1286 draw_row_title_buttons_range (sheet, first_row, max_visible_row (sheet));
1288 redraw_range (sheet, &range);
1292 If row0 or rowi are negative, then all rows will be updated.
1293 If col0 or coli are negative, then all columns will be updated.
1296 range_update_callback (PsppireSheetModel *m, gint row0, gint col0,
1297 gint rowi, gint coli, gpointer data)
1299 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1301 PsppireSheetRange range;
1308 if ( !GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1311 if ( ( row0 < 0 && col0 < 0 ) || ( rowi < 0 && coli < 0 ) )
1313 redraw_range (sheet, NULL);
1314 adjust_scrollbars (sheet);
1316 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
1317 max_visible_row (sheet));
1319 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
1320 max_visible_column (sheet));
1324 else if ( row0 < 0 || rowi < 0 )
1326 range.row0 = min_visible_row (sheet);
1327 range.rowi = max_visible_row (sheet);
1329 else if ( col0 < 0 || coli < 0 )
1331 range.col0 = min_visible_column (sheet);
1332 range.coli = max_visible_column (sheet);
1335 redraw_range (sheet, &range);
1340 * psppire_sheet_new:
1341 * @rows: initial number of rows
1342 * @columns: initial number of columns
1343 * @title: sheet title
1344 * @model: the model to use for the sheet data
1346 * Creates a new sheet widget with the given number of rows and columns.
1348 * Returns: the new sheet widget
1351 psppire_sheet_new (PsppireSheetModel *model)
1353 GtkWidget *widget = g_object_new (PSPPIRE_TYPE_SHEET,
1361 * psppire_sheet_set_model
1362 * @sheet: the sheet to set the model for
1363 * @model: the model to use for the sheet data
1365 * Sets the model for a PsppireSheet
1369 psppire_sheet_set_model (PsppireSheet *sheet, PsppireSheetModel *model)
1371 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1373 if (sheet->model ) g_object_unref (sheet->model);
1375 sheet->model = model;
1379 g_object_ref (model);
1381 sheet->update_handler_id = g_signal_connect (model, "range_changed",
1382 G_CALLBACK (range_update_callback),
1385 g_signal_connect (model, "rows_inserted",
1386 G_CALLBACK (rows_inserted_deleted_callback), sheet);
1388 g_signal_connect (model, "rows_deleted",
1389 G_CALLBACK (rows_inserted_deleted_callback), sheet);
1391 g_signal_connect (model, "columns_inserted",
1392 G_CALLBACK (columns_inserted_deleted_callback), sheet);
1394 g_signal_connect (model, "columns_deleted",
1395 G_CALLBACK (columns_inserted_deleted_callback), sheet);
1401 psppire_sheet_change_entry (PsppireSheet *sheet, GtkType entry_type)
1403 g_return_if_fail (sheet != NULL);
1404 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1406 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
1407 psppire_sheet_hide_entry_widget (sheet);
1409 sheet->entry_type = entry_type;
1411 create_sheet_entry (sheet);
1413 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
1414 psppire_sheet_show_entry_widget (sheet);
1418 psppire_sheet_show_grid (PsppireSheet *sheet, gboolean show)
1420 g_return_if_fail (sheet != NULL);
1421 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1423 if (show == sheet->show_grid) return;
1425 sheet->show_grid = show;
1427 redraw_range (sheet, NULL);
1431 psppire_sheet_grid_visible (PsppireSheet *sheet)
1433 g_return_val_if_fail (sheet != NULL, 0);
1434 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
1436 return sheet->show_grid;
1440 psppire_sheet_get_columns_count (PsppireSheet *sheet)
1442 g_return_val_if_fail (sheet != NULL, 0);
1443 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
1445 return psppire_axis_unit_count (sheet->haxis);
1448 static void set_column_width (PsppireSheet *sheet,
1454 psppire_sheet_show_column_titles (PsppireSheet *sheet)
1456 if (sheet->column_titles_visible) return;
1458 sheet->column_titles_visible = TRUE;
1460 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1463 gdk_window_show (sheet->column_title_window);
1464 gdk_window_move_resize (sheet->column_title_window,
1465 sheet->column_title_area.x,
1466 sheet->column_title_area.y,
1467 sheet->column_title_area.width,
1468 sheet->column_title_area.height);
1470 adjust_scrollbars (sheet);
1472 if (sheet->vadjustment)
1473 g_signal_emit_by_name (sheet->vadjustment,
1476 size_allocate_global_button (sheet);
1478 if ( sheet->row_titles_visible)
1479 gtk_widget_show (sheet->button);
1484 psppire_sheet_show_row_titles (PsppireSheet *sheet)
1486 if (sheet->row_titles_visible) return;
1488 sheet->row_titles_visible = TRUE;
1491 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1493 gdk_window_show (sheet->row_title_window);
1494 gdk_window_move_resize (sheet->row_title_window,
1495 sheet->row_title_area.x,
1496 sheet->row_title_area.y,
1497 sheet->row_title_area.width,
1498 sheet->row_title_area.height);
1500 adjust_scrollbars (sheet);
1503 if (sheet->hadjustment)
1504 g_signal_emit_by_name (sheet->hadjustment,
1507 size_allocate_global_button (sheet);
1509 if ( sheet->column_titles_visible)
1510 gtk_widget_show (sheet->button);
1514 psppire_sheet_hide_column_titles (PsppireSheet *sheet)
1516 if (!sheet->column_titles_visible) return;
1518 sheet->column_titles_visible = FALSE;
1520 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1522 if (sheet->column_title_window)
1523 gdk_window_hide (sheet->column_title_window);
1525 gtk_widget_hide (sheet->button);
1527 adjust_scrollbars (sheet);
1530 if (sheet->vadjustment)
1531 g_signal_emit_by_name (sheet->vadjustment,
1536 psppire_sheet_hide_row_titles (PsppireSheet *sheet)
1538 if (!sheet->row_titles_visible) return;
1540 sheet->row_titles_visible = FALSE;
1542 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1544 if (sheet->row_title_window)
1545 gdk_window_hide (sheet->row_title_window);
1547 gtk_widget_hide (sheet->button);
1549 adjust_scrollbars (sheet);
1552 if (sheet->hadjustment)
1553 g_signal_emit_by_name (sheet->hadjustment,
1558 /* Scroll the sheet so that the cell ROW, COLUMN is visible.
1559 If {ROW,COL}_ALIGN is zero, then the cell will be placed
1560 at the {top,left} of the sheet. If it's 1, then it'll
1561 be placed at the {bottom,right}.
1562 ROW or COL may be -1, in which case scrolling in that dimension
1566 psppire_sheet_moveto (PsppireSheet *sheet,
1574 g_return_if_fail (row_align >= 0);
1575 g_return_if_fail (col_align >= 0);
1577 g_return_if_fail (row_align <= 1);
1578 g_return_if_fail (col_align <= 1);
1580 g_return_if_fail (col <
1581 psppire_axis_unit_count (sheet->haxis));
1582 g_return_if_fail (row <
1583 psppire_axis_unit_count (sheet->vaxis));
1585 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
1590 gint y = psppire_axis_start_pixel (sheet->vaxis, row);
1592 gtk_adjustment_set_value (sheet->vadjustment, y - height * row_align);
1598 gint x = psppire_axis_start_pixel (sheet->haxis, col);
1600 gtk_adjustment_set_value (sheet->hadjustment, x - width * col_align);
1606 psppire_sheet_select_row (PsppireSheet *sheet, gint row)
1608 g_return_if_fail (sheet != NULL);
1609 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1611 if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis))
1614 if (sheet->select_status != PSPPIRE_SHEET_NORMAL)
1615 psppire_sheet_real_unselect_range (sheet, NULL);
1617 sheet->select_status = PSPPIRE_SHEET_ROW_SELECTED;
1618 sheet->range.row0 = row;
1619 sheet->range.col0 = 0;
1620 sheet->range.rowi = row;
1621 sheet->range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1623 g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, row);
1624 psppire_sheet_real_select_range (sheet, NULL);
1629 psppire_sheet_select_column (PsppireSheet *sheet, gint column)
1631 g_return_if_fail (sheet != NULL);
1632 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1634 if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis))
1637 if (sheet->select_status != PSPPIRE_SHEET_NORMAL)
1638 psppire_sheet_real_unselect_range (sheet, NULL);
1640 sheet->select_status = PSPPIRE_SHEET_COLUMN_SELECTED;
1641 sheet->range.row0 = 0;
1642 sheet->range.col0 = column;
1643 sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1644 sheet->range.coli = column;
1646 g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, column);
1647 psppire_sheet_real_select_range (sheet, NULL);
1654 psppire_sheet_range_isvisible (const PsppireSheet *sheet,
1655 const PsppireSheetRange *range)
1657 g_return_val_if_fail (sheet != NULL, FALSE);
1659 if (range->row0 < 0 || range->row0 >= psppire_axis_unit_count (sheet->vaxis))
1662 if (range->rowi < 0 || range->rowi >= psppire_axis_unit_count (sheet->vaxis))
1665 if (range->col0 < 0 || range->col0 >= psppire_axis_unit_count (sheet->haxis))
1668 if (range->coli < 0 || range->coli >= psppire_axis_unit_count (sheet->haxis))
1671 if (range->rowi < min_visible_row (sheet))
1674 if (range->row0 > max_visible_row (sheet))
1677 if (range->coli < min_visible_column (sheet))
1680 if (range->col0 > max_visible_column (sheet))
1687 psppire_sheet_cell_isvisible (PsppireSheet *sheet,
1688 gint row, gint column)
1690 PsppireSheetRange range;
1693 range.col0 = column;
1695 range.coli = column;
1697 return psppire_sheet_range_isvisible (sheet, &range);
1701 psppire_sheet_get_visible_range (PsppireSheet *sheet, PsppireSheetRange *range)
1703 g_return_if_fail (sheet != NULL);
1704 g_return_if_fail (PSPPIRE_IS_SHEET (sheet)) ;
1705 g_return_if_fail (range != NULL);
1707 range->row0 = min_visible_row (sheet);
1708 range->col0 = min_visible_column (sheet);
1709 range->rowi = max_visible_row (sheet);
1710 range->coli = max_visible_column (sheet);
1715 psppire_sheet_set_scroll_adjustments (PsppireSheet *sheet,
1716 GtkAdjustment *hadjustment,
1717 GtkAdjustment *vadjustment)
1719 if ( sheet->vadjustment != vadjustment )
1721 if (sheet->vadjustment)
1722 g_object_unref (sheet->vadjustment);
1723 sheet->vadjustment = vadjustment;
1727 g_object_ref (vadjustment);
1729 g_signal_connect (sheet->vadjustment, "value_changed",
1730 G_CALLBACK (vadjustment_value_changed),
1735 if ( sheet->hadjustment != hadjustment )
1737 if (sheet->hadjustment)
1738 g_object_unref (sheet->hadjustment);
1740 sheet->hadjustment = hadjustment;
1744 g_object_ref (hadjustment);
1746 g_signal_connect (sheet->hadjustment, "value_changed",
1747 G_CALLBACK (hadjustment_value_changed),
1755 psppire_sheet_finalize (GObject *object)
1757 PsppireSheet *sheet;
1759 g_return_if_fail (object != NULL);
1760 g_return_if_fail (PSPPIRE_IS_SHEET (object));
1762 sheet = PSPPIRE_SHEET (object);
1764 if (G_OBJECT_CLASS (parent_class)->finalize)
1765 (*G_OBJECT_CLASS (parent_class)->finalize) (object);
1769 psppire_sheet_dispose (GObject *object)
1771 PsppireSheet *sheet = PSPPIRE_SHEET (object);
1773 g_return_if_fail (object != NULL);
1774 g_return_if_fail (PSPPIRE_IS_SHEET (object));
1776 if ( sheet->dispose_has_run )
1779 sheet->dispose_has_run = TRUE;
1781 if ( sheet->cell_padding)
1782 g_boxed_free (GTK_TYPE_BORDER, sheet->cell_padding);
1784 if (sheet->model) g_object_unref (sheet->model);
1785 if (sheet->vaxis) g_object_unref (sheet->vaxis);
1786 if (sheet->haxis) g_object_unref (sheet->haxis);
1788 g_object_unref (sheet->button);
1789 sheet->button = NULL;
1791 /* unref adjustments */
1792 if (sheet->hadjustment)
1794 g_signal_handlers_disconnect_matched (sheet->hadjustment,
1795 G_SIGNAL_MATCH_DATA,
1799 g_object_unref (sheet->hadjustment);
1800 sheet->hadjustment = NULL;
1803 if (sheet->vadjustment)
1805 g_signal_handlers_disconnect_matched (sheet->vadjustment,
1806 G_SIGNAL_MATCH_DATA,
1810 g_object_unref (sheet->vadjustment);
1812 sheet->vadjustment = NULL;
1815 if (G_OBJECT_CLASS (parent_class)->dispose)
1816 (*G_OBJECT_CLASS (parent_class)->dispose) (object);
1820 psppire_sheet_style_set (GtkWidget *widget,
1821 GtkStyle *previous_style)
1823 PsppireSheet *sheet;
1825 g_return_if_fail (widget != NULL);
1826 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1828 if (GTK_WIDGET_CLASS (parent_class)->style_set)
1829 (*GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous_style);
1831 sheet = PSPPIRE_SHEET (widget);
1833 if (GTK_WIDGET_REALIZED (widget))
1835 gtk_style_set_background (widget->style, widget->window, widget->state);
1838 set_entry_widget_font (sheet);
1843 psppire_sheet_realize (GtkWidget *widget)
1845 PsppireSheet *sheet;
1846 GdkWindowAttr attributes;
1847 const gint attributes_mask =
1848 GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_CURSOR;
1851 GdkColormap *colormap;
1852 GdkDisplay *display;
1854 g_return_if_fail (widget != NULL);
1855 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1857 sheet = PSPPIRE_SHEET (widget);
1859 colormap = gtk_widget_get_colormap (widget);
1860 display = gtk_widget_get_display (widget);
1862 attributes.window_type = GDK_WINDOW_CHILD;
1863 attributes.x = widget->allocation.x;
1864 attributes.y = widget->allocation.y;
1865 attributes.width = widget->allocation.width;
1866 attributes.height = widget->allocation.height;
1867 attributes.wclass = GDK_INPUT_OUTPUT;
1869 attributes.visual = gtk_widget_get_visual (widget);
1870 attributes.colormap = colormap;
1872 attributes.event_mask = gtk_widget_get_events (widget);
1873 attributes.event_mask |= (GDK_EXPOSURE_MASK |
1874 GDK_BUTTON_PRESS_MASK |
1875 GDK_BUTTON_RELEASE_MASK |
1876 GDK_KEY_PRESS_MASK |
1877 GDK_ENTER_NOTIFY_MASK |
1878 GDK_LEAVE_NOTIFY_MASK |
1879 GDK_POINTER_MOTION_MASK |
1880 GDK_POINTER_MOTION_HINT_MASK);
1882 attributes.cursor = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
1885 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1887 gdk_window_set_user_data (widget->window, sheet);
1889 widget->style = gtk_style_attach (widget->style, widget->window);
1891 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1893 gdk_color_parse ("white", &sheet->color[BG_COLOR]);
1894 gdk_colormap_alloc_color (colormap, &sheet->color[BG_COLOR], FALSE,
1896 gdk_color_parse ("gray", &sheet->color[GRID_COLOR]);
1897 gdk_colormap_alloc_color (colormap, &sheet->color[GRID_COLOR], FALSE,
1902 attributes.width = sheet->column_title_area.width;
1903 attributes.height = sheet->column_title_area.height;
1906 /* column - title window */
1907 sheet->column_title_window =
1908 gdk_window_new (widget->window, &attributes, attributes_mask);
1909 gdk_window_set_user_data (sheet->column_title_window, sheet);
1910 gtk_style_set_background (widget->style, sheet->column_title_window,
1916 attributes.width = sheet->row_title_area.width;
1917 attributes.height = sheet->row_title_area.height;
1919 /* row - title window */
1920 sheet->row_title_window = gdk_window_new (widget->window,
1921 &attributes, attributes_mask);
1922 gdk_window_set_user_data (sheet->row_title_window, sheet);
1923 gtk_style_set_background (widget->style, sheet->row_title_window,
1926 /* sheet - window */
1927 attributes.cursor = gdk_cursor_new_for_display (display, GDK_PLUS);
1932 sheet->sheet_window = gdk_window_new (widget->window,
1933 &attributes, attributes_mask);
1934 gdk_window_set_user_data (sheet->sheet_window, sheet);
1936 gdk_cursor_unref (attributes.cursor);
1938 gdk_window_set_background (sheet->sheet_window, &widget->style->white);
1939 gdk_window_show (sheet->sheet_window);
1942 sheet->fg_gc = gdk_gc_new (widget->window);
1943 sheet->bg_gc = gdk_gc_new (widget->window);
1945 values.foreground = widget->style->white;
1946 values.function = GDK_INVERT;
1947 values.subwindow_mode = GDK_INCLUDE_INFERIORS;
1948 values.line_width = MAX (sheet->cell_padding->left,
1949 MAX (sheet->cell_padding->right,
1950 MAX (sheet->cell_padding->top,
1951 sheet->cell_padding->bottom)));
1953 sheet->xor_gc = gdk_gc_new_with_values (widget->window,
1961 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
1962 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
1964 gtk_widget_set_parent_window (sheet->button, sheet->sheet_window);
1965 gtk_widget_set_parent (sheet->button, GTK_WIDGET (sheet));
1967 sheet->button->style = gtk_style_attach (sheet->button->style,
1968 sheet->sheet_window);
1971 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
1973 if (sheet->column_titles_visible)
1974 gdk_window_show (sheet->column_title_window);
1975 if (sheet->row_titles_visible)
1976 gdk_window_show (sheet->row_title_window);
1978 sheet->hover_window = create_hover_window ();
1980 draw_row_title_buttons (sheet);
1981 draw_column_title_buttons (sheet);
1983 psppire_sheet_update_primary_selection (sheet);
1986 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1990 create_global_button (PsppireSheet *sheet)
1992 sheet->button = gtk_button_new_with_label (" ");
1994 GTK_WIDGET_UNSET_FLAGS(sheet->button, GTK_CAN_FOCUS);
1996 g_object_ref_sink (sheet->button);
1998 g_signal_connect (sheet->button,
2000 G_CALLBACK (global_button_clicked),
2005 size_allocate_global_button (PsppireSheet *sheet)
2007 GtkAllocation allocation;
2009 if (!sheet->column_titles_visible) return;
2010 if (!sheet->row_titles_visible) return;
2012 gtk_widget_size_request (sheet->button, NULL);
2016 allocation.width = sheet->row_title_area.width;
2017 allocation.height = sheet->column_title_area.height;
2019 gtk_widget_size_allocate (sheet->button, &allocation);
2023 global_button_clicked (GtkWidget *widget, gpointer data)
2025 psppire_sheet_click_cell (PSPPIRE_SHEET (data), -1, -1);
2030 psppire_sheet_unrealize (GtkWidget *widget)
2032 PsppireSheet *sheet;
2034 g_return_if_fail (widget != NULL);
2035 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
2037 sheet = PSPPIRE_SHEET (widget);
2039 gdk_cursor_unref (sheet->cursor_drag);
2040 sheet->cursor_drag = NULL;
2042 gdk_colormap_free_colors (gtk_widget_get_colormap (widget),
2043 sheet->color, n_COLORS);
2045 g_object_unref (sheet->xor_gc);
2046 g_object_unref (sheet->fg_gc);
2047 g_object_unref (sheet->bg_gc);
2049 destroy_hover_window (sheet->hover_window);
2051 gdk_window_destroy (sheet->sheet_window);
2052 gdk_window_destroy (sheet->column_title_window);
2053 gdk_window_destroy (sheet->row_title_window);
2055 gtk_widget_unparent (sheet->entry_widget);
2056 if (sheet->button != NULL)
2057 gtk_widget_unparent (sheet->button);
2059 if (GTK_WIDGET_CLASS (parent_class)->unrealize)
2060 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
2064 psppire_sheet_map (GtkWidget *widget)
2066 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2068 g_return_if_fail (widget != NULL);
2069 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
2071 if (!GTK_WIDGET_MAPPED (widget))
2073 GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
2075 gdk_window_show (widget->window);
2076 gdk_window_show (sheet->sheet_window);
2078 if (sheet->column_titles_visible)
2080 draw_column_title_buttons (sheet);
2081 gdk_window_show (sheet->column_title_window);
2083 if (sheet->row_titles_visible)
2085 draw_row_title_buttons (sheet);
2086 gdk_window_show (sheet->row_title_window);
2089 if (!GTK_WIDGET_MAPPED (sheet->entry_widget)
2090 && sheet->active_cell.row >= 0
2091 && sheet->active_cell.col >= 0 )
2093 gtk_widget_show (sheet->entry_widget);
2094 gtk_widget_map (sheet->entry_widget);
2097 if (!GTK_WIDGET_MAPPED (sheet->button))
2099 gtk_widget_show (sheet->button);
2100 gtk_widget_map (sheet->button);
2103 redraw_range (sheet, NULL);
2104 change_active_cell (sheet,
2105 sheet->active_cell.row,
2106 sheet->active_cell.col);
2111 psppire_sheet_unmap (GtkWidget *widget)
2113 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2115 if (!GTK_WIDGET_MAPPED (widget))
2118 GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
2120 gdk_window_hide (sheet->sheet_window);
2121 if (sheet->column_titles_visible)
2122 gdk_window_hide (sheet->column_title_window);
2123 if (sheet->row_titles_visible)
2124 gdk_window_hide (sheet->row_title_window);
2125 gdk_window_hide (widget->window);
2127 gtk_widget_unmap (sheet->entry_widget);
2128 gtk_widget_unmap (sheet->button);
2129 gtk_widget_unmap (sheet->hover_window->window);
2132 /* get cell attributes of the given cell */
2133 /* TRUE means that the cell is currently allocated */
2134 static gboolean psppire_sheet_get_attributes (const PsppireSheet *sheet,
2136 PsppireSheetCellAttr *attributes);
2141 psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint col)
2143 PangoLayout *layout;
2144 PangoRectangle text;
2145 PangoFontDescription *font_desc = GTK_WIDGET (sheet)->style->font_desc;
2150 PsppireSheetCellAttr attributes;
2153 g_return_if_fail (sheet != NULL);
2155 /* bail now if we aren't yet drawable */
2156 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
2159 row >= psppire_axis_unit_count (sheet->vaxis))
2163 col >= psppire_axis_unit_count (sheet->haxis))
2166 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2168 /* select GC for background rectangle */
2169 gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground);
2170 gdk_gc_set_foreground (sheet->bg_gc, &attributes.background);
2172 rectangle_from_cell (sheet, row, col, &area);
2174 gdk_gc_set_line_attributes (sheet->fg_gc, 1, 0, 0, 0);
2176 if (sheet->show_grid)
2178 gdk_gc_set_foreground (sheet->bg_gc, &sheet->color[GRID_COLOR]);
2180 gdk_draw_rectangle (sheet->sheet_window,
2184 area.width, area.height);
2188 label = psppire_sheet_cell_get_text (sheet, row, col);
2193 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), label);
2194 dispose_string (sheet, label);
2197 pango_layout_set_font_description (layout, font_desc);
2199 pango_layout_get_pixel_extents (layout, NULL, &text);
2201 gdk_gc_set_clip_rectangle (sheet->fg_gc, &area);
2203 font_height = pango_font_description_get_size (font_desc);
2204 if ( !pango_font_description_get_size_is_absolute (font_desc))
2205 font_height /= PANGO_SCALE;
2208 if ( sheet->cell_padding )
2210 area.x += sheet->cell_padding->left;
2211 area.width -= sheet->cell_padding->right
2212 + sheet->cell_padding->left;
2214 area.y += sheet->cell_padding->top;
2215 area.height -= sheet->cell_padding->bottom
2217 sheet->cell_padding->top;
2220 /* Centre the text vertically */
2221 area.y += (area.height - font_height) / 2.0;
2223 switch (attributes.justification)
2225 case GTK_JUSTIFY_RIGHT:
2226 area.x += area.width - text.width;
2228 case GTK_JUSTIFY_CENTER:
2229 area.x += (area.width - text.width) / 2.0;
2231 case GTK_JUSTIFY_LEFT:
2235 g_critical ("Unhandled justification %d in column %d\n",
2236 attributes.justification, col);
2240 gdk_draw_layout (sheet->sheet_window, sheet->fg_gc,
2245 gdk_gc_set_clip_rectangle (sheet->fg_gc, NULL);
2246 g_object_unref (layout);
2251 draw_sheet_region (PsppireSheet *sheet, GdkRegion *region)
2253 PsppireSheetRange range;
2258 PsppireSheetRange drawing_range;
2260 gdk_region_get_clipbox (region, &area);
2262 y = area.y + sheet->vadjustment->value;
2263 x = area.x + sheet->hadjustment->value;
2265 if ( sheet->column_titles_visible)
2266 y -= sheet->column_title_area.height;
2268 if ( sheet->row_titles_visible)
2269 x -= sheet->row_title_area.width;
2271 maximize_int (&x, 0);
2272 maximize_int (&y, 0);
2274 range.row0 = row_from_ypixel (sheet, y);
2275 range.rowi = row_from_ypixel (sheet, y + area.height);
2277 range.col0 = column_from_xpixel (sheet, x);
2278 range.coli = column_from_xpixel (sheet, x + area.width);
2280 g_return_if_fail (sheet != NULL);
2281 g_return_if_fail (PSPPIRE_SHEET (sheet));
2283 if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (sheet))) return;
2284 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2285 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
2288 drawing_range.row0 = MAX (range.row0, min_visible_row (sheet));
2289 drawing_range.col0 = MAX (range.col0, min_visible_column (sheet));
2290 drawing_range.rowi = MIN (range.rowi, max_visible_row (sheet));
2291 drawing_range.coli = MIN (range.coli, max_visible_column (sheet));
2293 g_return_if_fail (drawing_range.rowi >= drawing_range.row0);
2294 g_return_if_fail (drawing_range.coli >= drawing_range.col0);
2296 for (i = drawing_range.row0; i <= drawing_range.rowi; i++)
2298 for (j = drawing_range.col0; j <= drawing_range.coli; j++)
2299 psppire_sheet_cell_draw (sheet, i, j);
2302 if (sheet->select_status == PSPPIRE_SHEET_NORMAL &&
2303 sheet->active_cell.row >= drawing_range.row0 &&
2304 sheet->active_cell.row <= drawing_range.rowi &&
2305 sheet->active_cell.col >= drawing_range.col0 &&
2306 sheet->active_cell.col <= drawing_range.coli)
2307 psppire_sheet_show_entry_widget (sheet);
2313 safe_strcmp (const gchar *s1, const gchar *s2)
2315 if ( !s1 && !s2) return 0;
2316 if ( !s1) return -1;
2317 if ( !s2) return +1;
2318 return strcmp (s1, s2);
2322 psppire_sheet_set_cell (PsppireSheet *sheet, gint row, gint col,
2323 GtkJustification justification,
2326 PsppireSheetModel *model ;
2329 g_return_if_fail (sheet != NULL);
2330 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2332 if (col >= psppire_axis_unit_count (sheet->haxis)
2333 || row >= psppire_axis_unit_count (sheet->vaxis))
2336 if (col < 0 || row < 0) return;
2338 model = psppire_sheet_get_model (sheet);
2340 old_text = psppire_sheet_model_get_string (model, row, col);
2342 if (0 != safe_strcmp (old_text, text))
2344 g_signal_handler_block (sheet->model, sheet->update_handler_id);
2345 psppire_sheet_model_set_string (model, text, row, col);
2346 g_signal_handler_unblock (sheet->model, sheet->update_handler_id);
2349 if ( psppire_sheet_model_free_strings (model))
2355 psppire_sheet_cell_clear (PsppireSheet *sheet, gint row, gint column)
2357 PsppireSheetRange range;
2359 g_return_if_fail (sheet != NULL);
2360 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2361 if (column >= psppire_axis_unit_count (sheet->haxis) ||
2362 row >= psppire_axis_unit_count (sheet->vaxis)) return;
2364 if (column < 0 || row < 0) return;
2368 range.col0 = min_visible_column (sheet);
2369 range.coli = max_visible_column (sheet);
2371 psppire_sheet_real_cell_clear (sheet, row, column);
2373 redraw_range (sheet, &range);
2377 psppire_sheet_real_cell_clear (PsppireSheet *sheet, gint row, gint column)
2379 PsppireSheetModel *model = psppire_sheet_get_model (sheet);
2381 gchar *old_text = psppire_sheet_cell_get_text (sheet, row, column);
2383 if (old_text && strlen (old_text) > 0 )
2385 psppire_sheet_model_datum_clear (model, row, column);
2388 dispose_string (sheet, old_text);
2392 psppire_sheet_cell_get_text (const PsppireSheet *sheet, gint row, gint col)
2394 PsppireSheetModel *model;
2395 g_return_val_if_fail (sheet != NULL, NULL);
2396 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
2398 if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis))
2400 if (col < 0 || row < 0) return NULL;
2402 model = psppire_sheet_get_model (sheet);
2407 return psppire_sheet_model_get_string (model, row, col);
2411 /* Convert X, Y (in pixels) to *ROW, *COLUMN
2412 If the function returns FALSE, then the results will be unreliable.
2415 psppire_sheet_get_pixel_info (PsppireSheet *sheet,
2423 *column = -G_MAXINT;
2425 g_return_val_if_fail (sheet != NULL, 0);
2426 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2428 /* bounds checking, return false if the user clicked
2436 if ( sheet->column_titles_visible)
2437 y -= sheet->column_title_area.height;
2439 y += sheet->vadjustment->value;
2441 if ( y < 0 && sheet->column_titles_visible)
2447 trow = row_from_ypixel (sheet, y);
2448 if (trow > psppire_axis_unit_count (sheet->vaxis))
2454 if ( sheet->row_titles_visible)
2455 x -= sheet->row_title_area.width;
2457 x += sheet->hadjustment->value;
2459 if ( x < 0 && sheet->row_titles_visible)
2465 tcol = column_from_xpixel (sheet, x);
2466 if (tcol > psppire_axis_unit_count (sheet->haxis))
2476 psppire_sheet_get_cell_area (PsppireSheet *sheet,
2481 g_return_val_if_fail (sheet != NULL, 0);
2482 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2484 if (row >= psppire_axis_unit_count (sheet->vaxis) || column >= psppire_axis_unit_count (sheet->haxis))
2487 area->x = (column == -1) ? 0 : psppire_axis_start_pixel (sheet->haxis, column);
2488 area->y = (row == -1) ? 0 : psppire_axis_start_pixel (sheet->vaxis, row);
2490 area->width= (column == -1) ? sheet->row_title_area.width
2491 : psppire_axis_unit_size (sheet->haxis, column);
2493 area->height= (row == -1) ? sheet->column_title_area.height
2494 : psppire_axis_unit_size (sheet->vaxis, row);
2500 psppire_sheet_set_active_cell (PsppireSheet *sheet, gint row, gint col)
2502 g_return_if_fail (sheet != NULL);
2503 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2505 if (row < -1 || col < -1)
2508 if (row >= psppire_axis_unit_count (sheet->vaxis)
2510 col >= psppire_axis_unit_count (sheet->haxis))
2513 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2516 if ( row == -1 || col == -1)
2518 psppire_sheet_hide_entry_widget (sheet);
2522 change_active_cell (sheet, row, col);
2526 psppire_sheet_get_active_cell (PsppireSheet *sheet, gint *row, gint *column)
2528 g_return_if_fail (sheet != NULL);
2529 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2531 if ( row ) *row = sheet->active_cell.row;
2532 if (column) *column = sheet->active_cell.col;
2536 entry_load_text (PsppireSheet *sheet)
2540 GtkJustification justification;
2541 PsppireSheetCellAttr attributes;
2543 if (!GTK_WIDGET_VISIBLE (sheet->entry_widget)) return;
2544 if (sheet->select_status != PSPPIRE_SHEET_NORMAL) return;
2546 row = sheet->active_cell.row;
2547 col = sheet->active_cell.col;
2549 if (row < 0 || col < 0) return;
2551 text = gtk_entry_get_text (psppire_sheet_get_entry (sheet));
2553 if (text && strlen (text) > 0)
2555 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2556 justification = attributes.justification;
2557 psppire_sheet_set_cell (sheet, row, col, justification, text);
2563 psppire_sheet_hide_entry_widget (PsppireSheet *sheet)
2565 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2568 if (sheet->active_cell.row < 0 ||
2569 sheet->active_cell.col < 0) return;
2571 gtk_widget_hide (sheet->entry_widget);
2572 gtk_widget_unmap (sheet->entry_widget);
2574 GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2578 change_active_cell (PsppireSheet *sheet, gint row, gint col)
2580 gint old_row, old_col;
2582 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2584 if (row < 0 || col < 0)
2587 if ( row > psppire_axis_unit_count (sheet->vaxis)
2588 || col > psppire_axis_unit_count (sheet->haxis))
2591 old_row = sheet->active_cell.row;
2592 old_col = sheet->active_cell.col;
2594 entry_load_text (sheet);
2596 /* Erase the old cell border */
2597 psppire_sheet_draw_active_cell (sheet);
2599 sheet->active_cell.row = row;
2600 sheet->active_cell.col = col;
2602 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
2604 GTK_WIDGET_UNSET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2606 psppire_sheet_draw_active_cell (sheet);
2607 psppire_sheet_show_entry_widget (sheet);
2609 GTK_WIDGET_SET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2611 g_signal_emit (sheet, sheet_signals [ACTIVATE], 0,
2612 row, col, old_row, old_col);
2617 psppire_sheet_show_entry_widget (PsppireSheet *sheet)
2619 GtkEntry *sheet_entry;
2620 PsppireSheetCellAttr attributes;
2624 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2626 row = sheet->active_cell.row;
2627 col = sheet->active_cell.col;
2629 /* Don't show the active cell, if there is no active cell: */
2630 if (! (row >= 0 && col >= 0)) /* e.g row or coll == -1. */
2633 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2634 if (sheet->select_status != PSPPIRE_SHEET_NORMAL) return;
2635 if (PSPPIRE_SHEET_IN_SELECTION (sheet)) return;
2637 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2639 sheet_entry = psppire_sheet_get_entry (sheet);
2641 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2643 if (GTK_IS_ENTRY (sheet_entry))
2645 gchar *text = psppire_sheet_cell_get_text (sheet, row, col);
2646 const gchar *old_text = gtk_entry_get_text (GTK_ENTRY (sheet_entry));
2649 text = g_strdup ("");
2651 if (strcmp (old_text, text) != 0)
2652 gtk_entry_set_text (sheet_entry, text);
2654 dispose_string (sheet, text);
2657 switch (attributes.justification)
2659 case GTK_JUSTIFY_RIGHT:
2660 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 1.0);
2662 case GTK_JUSTIFY_CENTER:
2663 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.5);
2665 case GTK_JUSTIFY_LEFT:
2667 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.0);
2673 psppire_sheet_size_allocate_entry (sheet);
2675 gtk_widget_set_sensitive (GTK_WIDGET (sheet_entry),
2676 psppire_sheet_model_is_editable (sheet->model,
2678 gtk_widget_map (sheet->entry_widget);
2682 psppire_sheet_draw_active_cell (PsppireSheet *sheet)
2685 PsppireSheetRange range;
2687 row = sheet->active_cell.row;
2688 col = sheet->active_cell.col;
2690 if (row < 0 || col < 0) return FALSE;
2692 if (!psppire_sheet_cell_isvisible (sheet, row, col))
2695 range.col0 = range.coli = col;
2696 range.row0 = range.rowi = row;
2698 psppire_sheet_draw_border (sheet, range);
2706 psppire_sheet_draw_border (PsppireSheet *sheet, PsppireSheetRange new_range)
2710 rectangle_from_range (sheet, &new_range, &area);
2715 gdk_gc_set_clip_rectangle (sheet->xor_gc, &area);
2717 area.x += sheet->cell_padding->left / 2;
2718 area.y += sheet->cell_padding->top / 2;
2719 area.width -= (sheet->cell_padding->left + sheet->cell_padding->right ) / 2;
2720 area.height -= (sheet->cell_padding->top + sheet->cell_padding->bottom ) / 2;
2722 gdk_draw_rectangle (sheet->sheet_window, sheet->xor_gc,
2729 gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
2734 psppire_sheet_real_select_range (PsppireSheet *sheet,
2735 const PsppireSheetRange *range)
2737 g_return_if_fail (sheet != NULL);
2739 if (range == NULL) range = &sheet->range;
2741 memcpy (&sheet->range, range, sizeof (*range));
2743 if (range->row0 < 0 || range->rowi < 0) return;
2744 if (range->col0 < 0 || range->coli < 0) return;
2746 psppire_sheet_update_primary_selection (sheet);
2748 g_signal_emit (sheet, sheet_signals[SELECT_RANGE], 0, &sheet->range);
2753 psppire_sheet_get_selected_range (PsppireSheet *sheet, PsppireSheetRange *range)
2755 g_return_if_fail (sheet != NULL);
2756 *range = sheet->range;
2761 psppire_sheet_select_range (PsppireSheet *sheet, const PsppireSheetRange *range)
2763 g_return_if_fail (sheet != NULL);
2765 if (range == NULL) range=&sheet->range;
2767 if (range->row0 < 0 || range->rowi < 0) return;
2768 if (range->col0 < 0 || range->coli < 0) return;
2771 if (sheet->select_status != PSPPIRE_SHEET_NORMAL)
2772 psppire_sheet_real_unselect_range (sheet, NULL);
2774 sheet->range.row0 = range->row0;
2775 sheet->range.rowi = range->rowi;
2776 sheet->range.col0 = range->col0;
2777 sheet->range.coli = range->coli;
2779 sheet->select_status = PSPPIRE_SHEET_RANGE_SELECTED;
2780 psppire_sheet_real_select_range (sheet, NULL);
2784 psppire_sheet_unselect_range (PsppireSheet *sheet)
2786 if (! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2789 psppire_sheet_real_unselect_range (sheet, NULL);
2790 sheet->select_status = PSPPIRE_SHEET_NORMAL;
2795 psppire_sheet_real_unselect_range (PsppireSheet *sheet,
2796 const PsppireSheetRange *range)
2798 g_return_if_fail (sheet != NULL);
2799 g_return_if_fail (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)));
2802 range = &sheet->range;
2804 if (range->row0 < 0 || range->rowi < 0) return;
2805 if (range->col0 < 0 || range->coli < 0) return;
2807 g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, -1);
2808 g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, -1);
2810 sheet->range.row0 = -1;
2811 sheet->range.rowi = -1;
2812 sheet->range.col0 = -1;
2813 sheet->range.coli = -1;
2818 psppire_sheet_expose (GtkWidget *widget, GdkEventExpose *event)
2820 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2822 g_return_val_if_fail (event != NULL, FALSE);
2824 if (!GTK_WIDGET_DRAWABLE (widget))
2827 /* exposure events on the sheet */
2828 if (event->window == sheet->row_title_window &&
2829 sheet->row_titles_visible)
2831 draw_row_title_buttons_range (sheet,
2832 min_visible_row (sheet),
2833 max_visible_row (sheet));
2836 if (event->window == sheet->column_title_window &&
2837 sheet->column_titles_visible)
2839 draw_column_title_buttons_range (sheet,
2840 min_visible_column (sheet),
2841 max_visible_column (sheet));
2844 if (event->window == sheet->sheet_window)
2846 draw_sheet_region (sheet, event->region);
2848 if (sheet->select_status != PSPPIRE_SHEET_NORMAL)
2851 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
2852 psppire_sheet_range_draw (sheet, &sheet->range);
2854 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
2855 psppire_sheet_range_draw (sheet, &sheet->drag_range);
2858 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
2862 rectangle_from_range (sheet, &sheet->range, &area);
2864 gdk_draw_rectangle (sheet->sheet_window,
2867 area.x + 1, area.y + 1,
2868 area.width, area.height);
2872 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
2873 draw_xor_rectangle (sheet, sheet->drag_range);
2878 if ((!PSPPIRE_SHEET_IN_XDRAG (sheet)) && (!PSPPIRE_SHEET_IN_YDRAG (sheet)))
2881 PsppireSheetRange range;
2882 range.row0 = range.rowi = sheet->active_cell.row;
2883 range.col0 = range.coli = sheet->active_cell.col;
2885 rectangle_from_range (sheet, &range, &rect);
2887 if (GDK_OVERLAP_RECTANGLE_OUT !=
2888 gdk_region_rect_in (event->region, &rect))
2890 psppire_sheet_draw_active_cell (sheet);
2896 (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
2903 psppire_sheet_button_press (GtkWidget *widget, GdkEventButton *event)
2905 PsppireSheet *sheet;
2906 GdkModifierType mods;
2911 g_return_val_if_fail (widget != NULL, FALSE);
2912 g_return_val_if_fail (PSPPIRE_IS_SHEET (widget), FALSE);
2913 g_return_val_if_fail (event != NULL, FALSE);
2915 sheet = PSPPIRE_SHEET (widget);
2917 /* Cancel any pending tooltips */
2918 if (sheet->motion_timer)
2920 g_source_remove (sheet->motion_timer);
2921 sheet->motion_timer = 0;
2924 gtk_widget_get_pointer (widget, &x, &y);
2925 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
2928 if (event->window == sheet->column_title_window)
2930 sheet->x_drag = event->x;
2931 g_signal_emit (sheet,
2932 sheet_signals[BUTTON_EVENT_COLUMN], 0,
2935 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
2937 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
2938 g_signal_emit (sheet,
2939 sheet_signals[DOUBLE_CLICK_COLUMN], 0, column);
2942 else if (event->window == sheet->row_title_window)
2944 g_signal_emit (sheet,
2945 sheet_signals[BUTTON_EVENT_ROW], 0,
2948 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
2950 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
2951 g_signal_emit (sheet,
2952 sheet_signals[DOUBLE_CLICK_ROW], 0, row);
2956 gdk_window_get_pointer (widget->window, NULL, NULL, &mods);
2958 if (! (mods & GDK_BUTTON1_MASK)) return TRUE;
2961 /* press on resize windows */
2962 if (event->window == sheet->column_title_window)
2964 sheet->x_drag = event->x;
2966 if (on_column_boundary (sheet, sheet->x_drag, &sheet->drag_cell.col))
2968 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
2969 gdk_pointer_grab (sheet->column_title_window, FALSE,
2970 GDK_POINTER_MOTION_HINT_MASK |
2971 GDK_BUTTON1_MOTION_MASK |
2972 GDK_BUTTON_RELEASE_MASK,
2973 NULL, NULL, event->time);
2975 draw_xor_vline (sheet);
2980 if (event->window == sheet->row_title_window)
2982 sheet->y_drag = event->y;
2984 if (on_row_boundary (sheet, sheet->y_drag, &sheet->drag_cell.row))
2986 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
2987 gdk_pointer_grab (sheet->row_title_window, FALSE,
2988 GDK_POINTER_MOTION_HINT_MASK |
2989 GDK_BUTTON1_MOTION_MASK |
2990 GDK_BUTTON_RELEASE_MASK,
2991 NULL, NULL, event->time);
2993 draw_xor_hline (sheet);
2998 /* the sheet itself does not handle other than single click events */
2999 if (event->type != GDK_BUTTON_PRESS) return FALSE;
3001 /* selections on the sheet */
3002 if (event->window == sheet->sheet_window)
3004 gtk_widget_get_pointer (widget, &x, &y);
3005 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
3006 gdk_pointer_grab (sheet->sheet_window, FALSE,
3007 GDK_POINTER_MOTION_HINT_MASK |
3008 GDK_BUTTON1_MOTION_MASK |
3009 GDK_BUTTON_RELEASE_MASK,
3010 NULL, NULL, event->time);
3011 gtk_grab_add (GTK_WIDGET (sheet));
3013 if (psppire_sheet_click_cell (sheet, row, column))
3015 if ( sheet->select_status == PSPPIRE_SHEET_NORMAL)
3017 sheet->range.row0 = row;
3018 sheet->range.col0 = column;
3023 sheet->select_status = PSPPIRE_SHEET_NORMAL;
3025 rectangle_from_range (sheet, &sheet->range, &area);
3028 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
3033 if (event->window == sheet->column_title_window)
3035 gtk_widget_get_pointer (widget, &x, &y);
3036 if ( sheet->row_titles_visible)
3037 x -= sheet->row_title_area.width;
3039 x += sheet->hadjustment->value;
3041 column = column_from_xpixel (sheet, x);
3043 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
3045 gtk_grab_add (GTK_WIDGET (sheet));
3046 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3050 if (event->window == sheet->row_title_window)
3052 gtk_widget_get_pointer (widget, &x, &y);
3053 if ( sheet->column_titles_visible)
3054 y -= sheet->column_title_area.height;
3056 y += sheet->vadjustment->value;
3058 row = row_from_ypixel (sheet, y);
3059 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
3061 gtk_grab_add (GTK_WIDGET (sheet));
3062 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3070 psppire_sheet_click_cell (PsppireSheet *sheet, gint row, gint column)
3072 PsppireSheetCell cell;
3073 gboolean forbid_move;
3078 if (row >= psppire_axis_unit_count (sheet->vaxis)
3079 || column >= psppire_axis_unit_count (sheet->haxis))
3084 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
3085 &sheet->active_cell,
3091 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
3094 row = sheet->active_cell.row;
3095 column = sheet->active_cell.col;
3097 change_active_cell (sheet, row, column);
3101 if (row == -1 && column >= 0)
3103 psppire_sheet_select_column (sheet, column);
3107 if (column == -1 && row >= 0)
3109 psppire_sheet_select_row (sheet, row);
3113 if (row == -1 && column == -1)
3115 sheet->range.row0 = 0;
3116 sheet->range.col0 = 0;
3117 sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
3119 psppire_axis_unit_count (sheet->haxis) - 1;
3120 psppire_sheet_select_range (sheet, NULL);
3124 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
3125 change_active_cell (sheet, row, column);
3127 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
3133 psppire_sheet_button_release (GtkWidget *widget,
3134 GdkEventButton *event)
3136 GdkDisplay *display = gtk_widget_get_display (widget);
3138 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3140 /* release on resize windows */
3141 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3144 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
3145 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3147 gdk_display_pointer_ungrab (display, event->time);
3148 draw_xor_vline (sheet);
3151 psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)
3152 + sheet->hadjustment->value;
3154 set_column_width (sheet, sheet->drag_cell.col, width);
3159 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3162 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
3163 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3165 gdk_display_pointer_ungrab (display, event->time);
3166 draw_xor_hline (sheet);
3169 psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row) +
3170 sheet->vadjustment->value;
3172 set_row_height (sheet, sheet->drag_cell.row, height);
3177 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3179 PsppireSheetRange old_range;
3180 draw_xor_rectangle (sheet, sheet->drag_range);
3181 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_DRAG);
3182 gdk_display_pointer_ungrab (display, event->time);
3184 psppire_sheet_real_unselect_range (sheet, NULL);
3186 old_range = sheet->range;
3187 sheet->range = sheet->drag_range;
3188 sheet->drag_range = old_range;
3189 g_signal_emit (sheet, sheet_signals[MOVE_RANGE], 0,
3190 &sheet->drag_range, &sheet->range);
3191 psppire_sheet_select_range (sheet, &sheet->range);
3194 if (PSPPIRE_SHEET_IN_RESIZE (sheet))
3196 PsppireSheetRange old_range;
3197 draw_xor_rectangle (sheet, sheet->drag_range);
3198 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_RESIZE);
3199 gdk_display_pointer_ungrab (display, event->time);
3201 psppire_sheet_real_unselect_range (sheet, NULL);
3203 old_range = sheet->range;
3204 sheet->range = sheet->drag_range;
3205 sheet->drag_range = old_range;
3207 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
3208 sheet->select_status = PSPPIRE_SHEET_RANGE_SELECTED;
3210 g_signal_emit (sheet, sheet_signals[RESIZE_RANGE], 0,
3211 &sheet->drag_range, &sheet->range);
3212 psppire_sheet_select_range (sheet, &sheet->range);
3215 if (PSPPIRE_SHEET_IN_SELECTION (sheet))
3217 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3218 sheet->select_status = PSPPIRE_SHEET_RANGE_SELECTED;
3220 change_active_cell (sheet, sheet->active_cell.row,
3221 sheet->active_cell.col);
3224 gdk_display_pointer_ungrab (display, event->time);
3225 gtk_grab_remove (GTK_WIDGET (sheet));
3227 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3236 /* Shamelessly lifted from gtktooltips */
3238 psppire_sheet_subtitle_paint_window (GtkWidget *tip_window)
3242 gtk_widget_size_request (tip_window, &req);
3243 gtk_paint_flat_box (tip_window->style, tip_window->window,
3244 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
3245 NULL, GTK_WIDGET(tip_window), "tooltip",
3246 0, 0, req.width, req.height);
3252 destroy_hover_window (PsppireSheetHoverTitle *h)
3254 gtk_widget_destroy (h->window);
3258 static PsppireSheetHoverTitle *
3259 create_hover_window (void)
3261 PsppireSheetHoverTitle *hw = g_malloc (sizeof (*hw));
3263 hw->window = gtk_window_new (GTK_WINDOW_POPUP);
3265 #if GTK_CHECK_VERSION (2, 9, 0)
3266 gtk_window_set_type_hint (GTK_WINDOW (hw->window),
3267 GDK_WINDOW_TYPE_HINT_TOOLTIP);
3270 gtk_widget_set_app_paintable (hw->window, TRUE);
3271 gtk_window_set_resizable (GTK_WINDOW (hw->window), FALSE);
3272 gtk_widget_set_name (hw->window, "gtk-tooltips");
3273 gtk_container_set_border_width (GTK_CONTAINER (hw->window), 4);
3275 g_signal_connect (hw->window,
3277 G_CALLBACK (psppire_sheet_subtitle_paint_window),
3280 hw->label = gtk_label_new (NULL);
3283 gtk_label_set_line_wrap (GTK_LABEL (hw->label), TRUE);
3284 gtk_misc_set_alignment (GTK_MISC (hw->label), 0.5, 0.5);
3286 gtk_container_add (GTK_CONTAINER (hw->window), hw->label);
3288 gtk_widget_show (hw->label);
3290 g_signal_connect (hw->window,
3292 G_CALLBACK (gtk_widget_destroyed),
3298 #define HOVER_WINDOW_Y_OFFSET 2
3301 show_subtitle (PsppireSheet *sheet, gint row, gint column,
3302 const gchar *subtitle)
3311 gtk_label_set_text (GTK_LABEL (sheet->hover_window->label),
3315 sheet->hover_window->row = row;
3316 sheet->hover_window->column = column;
3318 gdk_window_get_origin (GTK_WIDGET (sheet)->window, &x, &y);
3320 gtk_widget_get_pointer (GTK_WIDGET (sheet), &px, &py);
3322 gtk_widget_show (sheet->hover_window->window);
3324 width = GTK_WIDGET (sheet->hover_window->label)->allocation.width;
3330 y += sheet->column_title_area.y;
3331 y += sheet->column_title_area.height;
3332 y += HOVER_WINDOW_Y_OFFSET;
3338 x += sheet->row_title_area.x;
3339 x += sheet->row_title_area.width * 2 / 3.0;
3342 gtk_window_move (GTK_WINDOW (sheet->hover_window->window),
3347 motion_timeout_callback (gpointer data)
3349 PsppireSheet *sheet = PSPPIRE_SHEET (data);
3353 gdk_threads_enter ();
3354 gtk_widget_get_pointer (GTK_WIDGET (sheet), &x, &y);
3356 if ( psppire_sheet_get_pixel_info (sheet, x, y, &row, &column) )
3358 if (sheet->row_title_under && row >= 0)
3360 gchar *text = psppire_sheet_model_get_row_subtitle (sheet->model, row);
3362 show_subtitle (sheet, row, -1, text);
3366 if (sheet->column_title_under && column >= 0)
3368 gchar *text = psppire_sheet_model_get_column_subtitle (sheet->model,
3371 show_subtitle (sheet, -1, column, text);
3377 gdk_threads_leave ();
3382 psppire_sheet_motion (GtkWidget *widget, GdkEventMotion *event)
3384 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3385 GdkModifierType mods;
3386 GdkCursorType new_cursor;
3389 GdkDisplay *display;
3391 g_return_val_if_fail (event != NULL, FALSE);
3393 display = gtk_widget_get_display (widget);
3395 /* selections on the sheet */
3399 if (!GTK_WIDGET_VISIBLE (sheet->hover_window->window))
3401 if ( sheet->motion_timer > 0 )
3402 g_source_remove (sheet->motion_timer);
3403 sheet->motion_timer =
3404 g_timeout_add (TIMEOUT_HOVER, motion_timeout_callback, sheet);
3410 gtk_widget_get_pointer (widget, &wx, &wy);
3412 if ( psppire_sheet_get_pixel_info (sheet, wx, wy, &row, &column) )
3414 if ( row != sheet->hover_window->row ||
3415 column != sheet->hover_window->column)
3417 gtk_widget_hide (sheet->hover_window->window);
3422 if (event->window == sheet->column_title_window)
3424 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3425 on_column_boundary (sheet, x, &column))
3427 new_cursor = GDK_SB_H_DOUBLE_ARROW;
3428 if (new_cursor != sheet->cursor_drag->type)
3430 gdk_cursor_unref (sheet->cursor_drag);
3431 sheet->cursor_drag =
3432 gdk_cursor_new_for_display (display, new_cursor);
3434 gdk_window_set_cursor (sheet->column_title_window,
3435 sheet->cursor_drag);
3440 new_cursor = GDK_TOP_LEFT_ARROW;
3441 if (!PSPPIRE_SHEET_IN_XDRAG (sheet) &&
3442 new_cursor != sheet->cursor_drag->type)
3444 gdk_cursor_unref (sheet->cursor_drag);
3445 sheet->cursor_drag =
3446 gdk_cursor_new_for_display (display, new_cursor);
3447 gdk_window_set_cursor (sheet->column_title_window,
3448 sheet->cursor_drag);
3452 else if (event->window == sheet->row_title_window)
3454 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3455 on_row_boundary (sheet, y, &row))
3457 new_cursor = GDK_SB_V_DOUBLE_ARROW;
3458 if (new_cursor != sheet->cursor_drag->type)
3460 gdk_cursor_unref (sheet->cursor_drag);
3461 sheet->cursor_drag =
3462 gdk_cursor_new_for_display (display, new_cursor);
3463 gdk_window_set_cursor (sheet->row_title_window,
3464 sheet->cursor_drag);
3469 new_cursor = GDK_TOP_LEFT_ARROW;
3470 if (!PSPPIRE_SHEET_IN_YDRAG (sheet) &&
3471 new_cursor != sheet->cursor_drag->type)
3473 gdk_cursor_unref (sheet->cursor_drag);
3474 sheet->cursor_drag =
3475 gdk_cursor_new_for_display (display, new_cursor);
3476 gdk_window_set_cursor (sheet->row_title_window,
3477 sheet->cursor_drag);
3482 new_cursor = GDK_PLUS;
3483 if ( event->window == sheet->sheet_window &&
3484 !POSSIBLE_DRAG (sheet, x, y, &row, &column) &&
3485 !PSPPIRE_SHEET_IN_DRAG (sheet) &&
3486 !POSSIBLE_RESIZE (sheet, x, y, &row, &column) &&
3487 !PSPPIRE_SHEET_IN_RESIZE (sheet) &&
3488 new_cursor != sheet->cursor_drag->type)
3490 gdk_cursor_unref (sheet->cursor_drag);
3491 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
3492 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3495 new_cursor = GDK_TOP_LEFT_ARROW;
3496 if ( event->window == sheet->sheet_window &&
3497 ! (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
3498 PSPPIRE_SHEET_IN_RESIZE (sheet)) &&
3499 (POSSIBLE_DRAG (sheet, x, y, &row, &column) ||
3500 PSPPIRE_SHEET_IN_DRAG (sheet)) &&
3501 new_cursor != sheet->cursor_drag->type)
3503 gdk_cursor_unref (sheet->cursor_drag);
3504 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
3505 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3508 new_cursor = GDK_SIZING;
3509 if ( event->window == sheet->sheet_window &&
3510 sheet->selection_mode != GTK_SELECTION_NONE &&
3511 !PSPPIRE_SHEET_IN_DRAG (sheet) &&
3512 (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
3513 PSPPIRE_SHEET_IN_RESIZE (sheet)) &&
3514 new_cursor != sheet->cursor_drag->type)
3516 gdk_cursor_unref (sheet->cursor_drag);
3517 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_SIZING);
3518 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3522 gdk_window_get_pointer (widget->window, &x, &y, &mods);
3523 if (! (mods & GDK_BUTTON1_MASK)) return FALSE;
3525 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3527 if (event->x != sheet->x_drag)
3529 draw_xor_vline (sheet);
3530 sheet->x_drag = event->x;
3531 draw_xor_vline (sheet);
3537 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3539 if (event->y != sheet->y_drag)
3541 draw_xor_hline (sheet);
3542 sheet->y_drag = event->y;
3543 draw_xor_hline (sheet);
3549 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3551 PsppireSheetRange aux;
3552 column = column_from_xpixel (sheet, x)- sheet->drag_cell.col;
3553 row = row_from_ypixel (sheet, y) - sheet->drag_cell.row;
3554 if (sheet->select_status == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
3555 if (sheet->select_status == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
3559 if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
3560 aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
3562 aux = sheet->drag_range;
3563 sheet->drag_range.row0 = sheet->range.row0 + row;
3564 sheet->drag_range.col0 = sheet->range.col0 + column;
3565 sheet->drag_range.rowi = sheet->range.rowi + row;
3566 sheet->drag_range.coli = sheet->range.coli + column;
3567 if (aux.row0 != sheet->drag_range.row0 ||
3568 aux.col0 != sheet->drag_range.col0)
3570 draw_xor_rectangle (sheet, aux);
3571 draw_xor_rectangle (sheet, sheet->drag_range);
3577 if (PSPPIRE_SHEET_IN_RESIZE (sheet))
3579 PsppireSheetRange aux;
3580 gint v_h, current_col, current_row, col_threshold, row_threshold;
3582 if (abs (x - psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)) >
3583 abs (y - psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row))) v_h = 2;
3585 current_col = column_from_xpixel (sheet, x);
3586 current_row = row_from_ypixel (sheet, y);
3587 column = current_col - sheet->drag_cell.col;
3588 row = current_row - sheet->drag_cell.row;
3590 /*use half of column width resp. row height as threshold to
3592 col_threshold = psppire_axis_start_pixel (sheet->haxis, current_col) +
3593 psppire_axis_unit_size (sheet->haxis, current_col) / 2;
3596 if (x < col_threshold)
3599 else if (column < 0)
3601 if (x > col_threshold)
3604 row_threshold = psppire_axis_start_pixel (sheet->vaxis, current_row) +
3605 psppire_axis_unit_size (sheet->vaxis, current_row)/2;
3608 if (y < row_threshold)
3613 if (y > row_threshold)
3617 if (sheet->select_status == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
3618 if (sheet->select_status == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
3628 if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
3629 aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
3631 aux = sheet->drag_range;
3632 sheet->drag_range = sheet->range;
3634 if (row < 0) sheet->drag_range.row0 = sheet->range.row0 + row;
3635 if (row > 0) sheet->drag_range.rowi = sheet->range.rowi + row;
3636 if (column < 0) sheet->drag_range.col0 = sheet->range.col0 + column;
3637 if (column > 0) sheet->drag_range.coli = sheet->range.coli + column;
3639 if (aux.row0 != sheet->drag_range.row0 ||
3640 aux.rowi != sheet->drag_range.rowi ||
3641 aux.col0 != sheet->drag_range.col0 ||
3642 aux.coli != sheet->drag_range.coli)
3644 draw_xor_rectangle (sheet, aux);
3645 draw_xor_rectangle (sheet, sheet->drag_range);
3651 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
3653 if (sheet->select_status == PSPPIRE_SHEET_NORMAL && row == sheet->active_cell.row &&
3654 column == sheet->active_cell.col) return TRUE;
3656 if ( mods & GDK_BUTTON1_MASK)
3658 if (PSPPIRE_SHEET_IN_SELECTION (sheet) )
3662 /* Redraw the old range */
3663 rectangle_from_range (sheet, &sheet->range, &area);
3666 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
3668 sheet->range.rowi = row;
3669 sheet->range.coli = column;
3670 sheet->select_status = PSPPIRE_SHEET_RANGE_SELECTED;
3672 /* Redraw the new range */
3673 rectangle_from_range (sheet, &sheet->range, &area);
3676 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
3680 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3688 psppire_sheet_crossing_notify (GtkWidget *widget,
3689 GdkEventCrossing *event)
3691 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3693 if (event->window == sheet->column_title_window)
3694 sheet->column_title_under = event->type == GDK_ENTER_NOTIFY;
3695 else if (event->window == sheet->row_title_window)
3696 sheet->row_title_under = event->type == GDK_ENTER_NOTIFY;
3698 if (event->type == GDK_LEAVE_NOTIFY)
3699 gtk_widget_hide (sheet->hover_window->window);
3706 psppire_sheet_focus_in (GtkWidget *w,
3707 GdkEventFocus *event)
3709 PsppireSheet *sheet = PSPPIRE_SHEET (w);
3711 gtk_widget_grab_focus (sheet->entry_widget);
3719 psppire_sheet_entry_key_press (GtkWidget *widget,
3723 g_signal_emit_by_name (widget, "key_press_event", key, &focus);
3728 /* Number of rows in a step-increment */
3729 #define ROWS_PER_STEP 1
3733 page_vertical (PsppireSheet *sheet, GtkScrollType dir)
3735 gint old_row = sheet->active_cell.row ;
3736 glong vpixel = psppire_axis_start_pixel (sheet->vaxis, old_row);
3740 vpixel -= psppire_axis_start_pixel (sheet->vaxis,
3741 min_visible_row (sheet));
3745 case GTK_SCROLL_PAGE_DOWN:
3746 gtk_adjustment_set_value (sheet->vadjustment,
3747 sheet->vadjustment->value +
3748 sheet->vadjustment->page_increment);
3750 case GTK_SCROLL_PAGE_UP:
3751 gtk_adjustment_set_value (sheet->vadjustment,
3752 sheet->vadjustment->value -
3753 sheet->vadjustment->page_increment);
3757 g_assert_not_reached ();
3762 vpixel += psppire_axis_start_pixel (sheet->vaxis,
3763 min_visible_row (sheet));
3765 new_row = row_from_ypixel (sheet, vpixel);
3767 change_active_cell (sheet, new_row,
3768 sheet->active_cell.col);
3773 step_sheet (PsppireSheet *sheet, GtkScrollType dir)
3775 gint current_row = sheet->active_cell.row;
3776 gint current_col = sheet->active_cell.col;
3777 PsppireSheetCell new_cell ;
3778 gboolean forbidden = FALSE;
3780 new_cell.row = current_row;
3781 new_cell.col = current_col;
3785 case GTK_SCROLL_STEP_DOWN:
3788 case GTK_SCROLL_STEP_UP:
3791 case GTK_SCROLL_STEP_RIGHT:
3794 case GTK_SCROLL_STEP_LEFT:
3797 case GTK_SCROLL_STEP_FORWARD:
3800 psppire_sheet_model_get_column_count (sheet->model))
3806 case GTK_SCROLL_STEP_BACKWARD:
3808 if (new_cell.col < 0)
3811 psppire_sheet_model_get_column_count (sheet->model) - 1;
3816 g_assert_not_reached ();
3820 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
3821 &sheet->active_cell,
3829 maximize_int (&new_cell.row, 0);
3830 maximize_int (&new_cell.col, 0);
3832 minimize_int (&new_cell.row,
3833 psppire_axis_unit_count (sheet->vaxis) - 1);
3835 minimize_int (&new_cell.col,
3836 psppire_axis_unit_count (sheet->haxis) - 1);
3838 change_active_cell (sheet, new_cell.row, new_cell.col);
3841 if ( new_cell.col > max_fully_visible_column (sheet))
3844 psppire_axis_start_pixel (sheet->haxis,
3846 hpos -= sheet->hadjustment->page_size;
3848 gtk_adjustment_set_value (sheet->hadjustment,
3851 else if ( new_cell.col < min_fully_visible_column (sheet))
3854 psppire_axis_start_pixel (sheet->haxis,
3857 gtk_adjustment_set_value (sheet->hadjustment,
3862 if ( new_cell.row > max_fully_visible_row (sheet))
3865 psppire_axis_start_pixel (sheet->vaxis,
3867 vpos -= sheet->vadjustment->page_size;
3869 gtk_adjustment_set_value (sheet->vadjustment,
3872 else if ( new_cell.row < min_fully_visible_row (sheet))
3875 psppire_axis_start_pixel (sheet->vaxis,
3878 gtk_adjustment_set_value (sheet->vadjustment,
3882 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
3887 psppire_sheet_key_press (GtkWidget *widget,
3890 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3892 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3894 switch (key->keyval)
3897 step_sheet (sheet, GTK_SCROLL_STEP_FORWARD);
3900 step_sheet (sheet, GTK_SCROLL_STEP_RIGHT);
3902 case GDK_ISO_Left_Tab:
3903 step_sheet (sheet, GTK_SCROLL_STEP_BACKWARD);
3906 step_sheet (sheet, GTK_SCROLL_STEP_LEFT);
3910 step_sheet (sheet, GTK_SCROLL_STEP_DOWN);
3913 step_sheet (sheet, GTK_SCROLL_STEP_UP);
3917 page_vertical (sheet, GTK_SCROLL_PAGE_DOWN);
3920 page_vertical (sheet, GTK_SCROLL_PAGE_UP);
3924 gtk_adjustment_set_value (sheet->vadjustment,
3925 sheet->vadjustment->lower);
3927 change_active_cell (sheet, 0,
3928 sheet->active_cell.col);
3933 gtk_adjustment_set_value (sheet->vadjustment,
3934 sheet->vadjustment->upper -
3935 sheet->vadjustment->page_size -
3936 sheet->vadjustment->page_increment);
3939 change_active_cellx (sheet,
3940 psppire_axis_unit_count (sheet->vaxis) - 1,
3941 sheet->active_cell.col);
3945 psppire_sheet_real_cell_clear (sheet, sheet->active_cell.row, sheet->active_cell.col);
3956 psppire_sheet_size_request (GtkWidget *widget,
3957 GtkRequisition *requisition)
3959 PsppireSheet *sheet;
3961 g_return_if_fail (widget != NULL);
3962 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
3963 g_return_if_fail (requisition != NULL);
3965 sheet = PSPPIRE_SHEET (widget);
3967 requisition->width = 3 * DEFAULT_COLUMN_WIDTH;
3968 requisition->height = 3 * DEFAULT_ROW_HEIGHT;
3970 /* compute the size of the column title area */
3971 if (sheet->column_titles_visible)
3972 requisition->height += sheet->column_title_area.height;
3974 /* compute the size of the row title area */
3975 if (sheet->row_titles_visible)
3976 requisition->width += sheet->row_title_area.width;
3981 psppire_sheet_size_allocate (GtkWidget *widget,
3982 GtkAllocation *allocation)
3984 PsppireSheet *sheet;
3985 GtkAllocation sheet_allocation;
3988 g_return_if_fail (widget != NULL);
3989 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
3990 g_return_if_fail (allocation != NULL);
3992 sheet = PSPPIRE_SHEET (widget);
3993 widget->allocation = *allocation;
3994 border_width = GTK_CONTAINER (widget)->border_width;
3996 if (GTK_WIDGET_REALIZED (widget))
3997 gdk_window_move_resize (widget->window,
3998 allocation->x + border_width,
3999 allocation->y + border_width,
4000 allocation->width - 2 * border_width,
4001 allocation->height - 2 * border_width);
4003 sheet_allocation.x = 0;
4004 sheet_allocation.y = 0;
4005 sheet_allocation.width = allocation->width - 2 * border_width;
4006 sheet_allocation.height = allocation->height - 2 * border_width;
4008 if (GTK_WIDGET_REALIZED (widget))
4009 gdk_window_move_resize (sheet->sheet_window,
4012 sheet_allocation.width,
4013 sheet_allocation.height);
4015 /* position the window which holds the column title buttons */
4016 sheet->column_title_area.x = 0;
4017 sheet->column_title_area.y = 0;
4018 sheet->column_title_area.width = sheet_allocation.width ;
4021 /* position the window which holds the row title buttons */
4022 sheet->row_title_area.x = 0;
4023 sheet->row_title_area.y = 0;
4024 sheet->row_title_area.height = sheet_allocation.height;
4026 if (sheet->row_titles_visible)
4027 sheet->column_title_area.x += sheet->row_title_area.width;
4029 if (sheet->column_titles_visible)
4030 sheet->row_title_area.y += sheet->column_title_area.height;
4032 if (GTK_WIDGET_REALIZED (widget) && sheet->column_titles_visible)
4033 gdk_window_move_resize (sheet->column_title_window,
4034 sheet->column_title_area.x,
4035 sheet->column_title_area.y,
4036 sheet->column_title_area.width,
4037 sheet->column_title_area.height);
4040 if (GTK_WIDGET_REALIZED (widget) && sheet->row_titles_visible)
4041 gdk_window_move_resize (sheet->row_title_window,
4042 sheet->row_title_area.x,
4043 sheet->row_title_area.y,
4044 sheet->row_title_area.width,
4045 sheet->row_title_area.height);
4047 size_allocate_global_button (sheet);
4051 gint width = sheet->column_title_area.width;
4053 if ( sheet->row_titles_visible)
4054 width -= sheet->row_title_area.width;
4056 g_object_set (sheet->haxis,
4057 "minimum-extent", width,
4064 gint height = sheet->row_title_area.height;
4066 if ( sheet->column_titles_visible)
4067 height -= sheet->column_title_area.height;
4069 g_object_set (sheet->vaxis,
4070 "minimum-extent", height,
4075 /* set the scrollbars adjustments */
4076 adjust_scrollbars (sheet);
4080 draw_column_title_buttons (PsppireSheet *sheet)
4084 if (!sheet->column_titles_visible) return;
4085 if (!GTK_WIDGET_REALIZED (sheet))
4088 gdk_drawable_get_size (sheet->sheet_window, &width, NULL);
4091 if (sheet->row_titles_visible)
4093 x = sheet->row_title_area.width;
4096 if (sheet->column_title_area.width != width || sheet->column_title_area.x != x)
4098 sheet->column_title_area.width = width;
4099 sheet->column_title_area.x = x;
4100 gdk_window_move_resize (sheet->column_title_window,
4101 sheet->column_title_area.x,
4102 sheet->column_title_area.y,
4103 sheet->column_title_area.width,
4104 sheet->column_title_area.height);
4107 if (max_visible_column (sheet) ==
4108 psppire_axis_unit_count (sheet->haxis) - 1)
4109 gdk_window_clear_area (sheet->column_title_window,
4111 sheet->column_title_area.width,
4112 sheet->column_title_area.height);
4114 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
4116 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
4117 max_visible_column (sheet));
4121 draw_row_title_buttons (PsppireSheet *sheet)
4126 if (!sheet->row_titles_visible) return;
4127 if (!GTK_WIDGET_REALIZED (sheet))
4130 gdk_drawable_get_size (sheet->sheet_window, NULL, &height);
4132 if (sheet->column_titles_visible)
4134 y = sheet->column_title_area.height;
4137 if (sheet->row_title_area.height != height || sheet->row_title_area.y != y)
4139 sheet->row_title_area.y = y;
4140 sheet->row_title_area.height = height;
4141 gdk_window_move_resize (sheet->row_title_window,
4142 sheet->row_title_area.x,
4143 sheet->row_title_area.y,
4144 sheet->row_title_area.width,
4145 sheet->row_title_area.height);
4148 if (max_visible_row (sheet) == psppire_axis_unit_count (sheet->vaxis) - 1)
4149 gdk_window_clear_area (sheet->row_title_window,
4151 sheet->row_title_area.width,
4152 sheet->row_title_area.height);
4154 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
4156 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
4157 max_visible_row (sheet));
4162 psppire_sheet_size_allocate_entry (PsppireSheet *sheet)
4164 GtkAllocation entry_alloc;
4165 PsppireSheetCellAttr attributes = { 0 };
4166 GtkEntry *sheet_entry;
4168 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4169 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
4171 sheet_entry = psppire_sheet_get_entry (sheet);
4173 if ( ! psppire_sheet_get_attributes (sheet, sheet->active_cell.row,
4174 sheet->active_cell.col,
4178 if ( GTK_WIDGET_REALIZED (sheet->entry_widget) )
4180 GtkStyle *style = GTK_WIDGET (sheet_entry)->style;
4182 style->bg[GTK_STATE_NORMAL] = attributes.background;
4183 style->fg[GTK_STATE_NORMAL] = attributes.foreground;
4184 style->text[GTK_STATE_NORMAL] = attributes.foreground;
4185 style->bg[GTK_STATE_ACTIVE] = attributes.background;
4186 style->fg[GTK_STATE_ACTIVE] = attributes.foreground;
4187 style->text[GTK_STATE_ACTIVE] = attributes.foreground;
4190 rectangle_from_cell (sheet, sheet->active_cell.row,
4191 sheet->active_cell.col, &entry_alloc);
4193 entry_alloc.x += sheet->cell_padding->left;
4194 entry_alloc.y += sheet->cell_padding->right;
4195 entry_alloc.width -= sheet->cell_padding->left + sheet->cell_padding->right;
4196 entry_alloc.height -= sheet->cell_padding->top + sheet->cell_padding->bottom;
4199 gtk_widget_set_size_request (sheet->entry_widget, entry_alloc.width,
4200 entry_alloc.height);
4201 gtk_widget_size_allocate (sheet->entry_widget, &entry_alloc);
4205 /* Copy the sheet's font to the entry widget */
4207 set_entry_widget_font (PsppireSheet *sheet)
4209 GtkRcStyle *style = gtk_widget_get_modifier_style (sheet->entry_widget);
4211 pango_font_description_free (style->font_desc);
4212 style->font_desc = pango_font_description_copy (GTK_WIDGET (sheet)->style->font_desc);
4214 gtk_widget_modify_style (sheet->entry_widget, style);
4218 create_sheet_entry (PsppireSheet *sheet)
4220 if (sheet->entry_widget)
4222 gtk_widget_unparent (sheet->entry_widget);
4225 sheet->entry_widget = g_object_new (sheet->entry_type, NULL);
4226 g_object_ref_sink (sheet->entry_widget);
4228 gtk_widget_size_request (sheet->entry_widget, NULL);
4230 if ( GTK_IS_ENTRY (sheet->entry_widget))
4232 g_object_set (sheet->entry_widget,
4237 if (GTK_WIDGET_REALIZED (sheet))
4239 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
4240 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
4241 gtk_widget_realize (sheet->entry_widget);
4244 g_signal_connect_swapped (sheet->entry_widget, "key_press_event",
4245 G_CALLBACK (psppire_sheet_entry_key_press),
4248 set_entry_widget_font (sheet);
4250 gtk_widget_show (sheet->entry_widget);
4254 /* Finds the last child widget that happens to be of type GtkEntry */
4256 find_entry (GtkWidget *w, gpointer user_data)
4258 GtkWidget **entry = user_data;
4259 if ( GTK_IS_ENTRY (w))
4267 psppire_sheet_get_entry (PsppireSheet *sheet)
4269 GtkWidget *w = sheet->entry_widget;
4271 g_return_val_if_fail (sheet != NULL, NULL);
4272 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
4273 g_return_val_if_fail (sheet->entry_widget != NULL, NULL);
4275 while (! GTK_IS_ENTRY (w))
4277 GtkWidget *entry = NULL;
4279 if (GTK_IS_CONTAINER (w))
4281 gtk_container_forall (GTK_CONTAINER (w), find_entry, &entry);
4290 return GTK_ENTRY (w);
4295 draw_button (PsppireSheet *sheet, GdkWindow *window,
4296 PsppireSheetButton *button, gboolean is_sensitive,
4297 GdkRectangle allocation)
4299 GtkShadowType shadow_type;
4300 gint text_width = 0, text_height = 0;
4301 PangoAlignment align = PANGO_ALIGN_LEFT;
4307 g_return_if_fail (sheet != NULL);
4308 g_return_if_fail (button != NULL);
4311 rtl = gtk_widget_get_direction (GTK_WIDGET (sheet)) == GTK_TEXT_DIR_RTL;
4313 gdk_window_clear_area (window,
4314 allocation.x, allocation.y,
4315 allocation.width, allocation.height);
4317 gtk_widget_ensure_style (sheet->button);
4319 gtk_paint_box (sheet->button->style, window,
4320 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
4322 GTK_WIDGET (sheet->button),
4324 allocation.x, allocation.y,
4325 allocation.width, allocation.height);
4327 state = button->state;
4328 if (!is_sensitive) state = GTK_STATE_INSENSITIVE;
4330 if (state == GTK_STATE_ACTIVE)
4331 shadow_type = GTK_SHADOW_IN;
4333 shadow_type = GTK_SHADOW_OUT;
4335 if (state != GTK_STATE_NORMAL && state != GTK_STATE_INSENSITIVE)
4336 gtk_paint_box (sheet->button->style, window,
4337 button->state, shadow_type,
4338 &allocation, GTK_WIDGET (sheet->button),
4340 allocation.x, allocation.y,
4341 allocation.width, allocation.height);
4343 if ( button->overstruck)
4345 GdkPoint points[2] = {
4346 {allocation.x, allocation.y},
4347 {allocation.x + allocation.width,
4348 allocation.y + allocation.height}
4351 gtk_paint_polygon (sheet->button->style,
4363 if (button->label_visible)
4365 text_height = DEFAULT_ROW_HEIGHT -
4366 2 * COLUMN_TITLES_HEIGHT;
4368 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4370 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc,
4373 allocation.y += 2 * sheet->button->style->ythickness;
4375 if (button->label && strlen (button->label) > 0)
4377 PangoRectangle rect;
4378 gchar *line = button->label;
4380 PangoLayout *layout = NULL;
4381 gint real_x = allocation.x;
4382 gint real_y = allocation.y;
4384 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), line);
4385 pango_layout_get_extents (layout, NULL, &rect);
4387 text_width = PANGO_PIXELS (rect.width);
4388 switch (button->justification)
4390 case GTK_JUSTIFY_LEFT:
4391 real_x = allocation.x + COLUMN_TITLES_HEIGHT;
4392 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4394 case GTK_JUSTIFY_RIGHT:
4395 real_x = allocation.x + allocation.width - text_width - COLUMN_TITLES_HEIGHT;
4396 align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
4398 case GTK_JUSTIFY_CENTER:
4400 real_x = allocation.x + (allocation.width - text_width)/2;
4401 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4402 pango_layout_set_justify (layout, TRUE);
4404 pango_layout_set_alignment (layout, align);
4405 gtk_paint_layout (GTK_WIDGET (sheet)->style,
4414 g_object_unref (layout);
4417 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4419 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc, NULL);
4423 psppire_sheet_button_free (button);
4427 /* Draw the column title buttons FIRST through to LAST */
4429 draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4433 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4435 if (!sheet->column_titles_visible) return;
4437 g_return_if_fail (first >= min_visible_column (sheet));
4438 g_return_if_fail (last <= max_visible_column (sheet));
4441 rect.height = sheet->column_title_area.height;
4442 rect.x = psppire_axis_start_pixel (sheet->haxis, first) + CELL_SPACING;
4443 rect.width = psppire_axis_start_pixel (sheet->haxis, last) + CELL_SPACING
4444 + psppire_axis_unit_size (sheet->haxis, last);
4446 rect.x -= sheet->hadjustment->value;
4448 minimize_int (&rect.width, sheet->column_title_area.width);
4449 maximize_int (&rect.x, 0);
4451 gdk_window_begin_paint_rect (sheet->column_title_window, &rect);
4453 for (col = first ; col <= last ; ++col)
4455 GdkRectangle allocation;
4456 gboolean is_sensitive = FALSE;
4458 PsppireSheetButton *
4459 button = psppire_sheet_model_get_column_button (sheet->model, col);
4461 allocation.x = psppire_axis_start_pixel (sheet->haxis, col)
4463 allocation.x -= sheet->hadjustment->value;
4465 allocation.height = sheet->column_title_area.height;
4466 allocation.width = psppire_axis_unit_size (sheet->haxis, col);
4467 is_sensitive = psppire_sheet_model_get_column_sensitivity (sheet->model, col);
4469 draw_button (sheet, sheet->column_title_window,
4470 button, is_sensitive, allocation);
4473 gdk_window_end_paint (sheet->column_title_window);
4478 draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4482 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4484 if (!sheet->row_titles_visible) return;
4486 g_return_if_fail (first >= min_visible_row (sheet));
4487 g_return_if_fail (last <= max_visible_row (sheet));
4490 rect.width = sheet->row_title_area.width;
4491 rect.y = psppire_axis_start_pixel (sheet->vaxis, first) + CELL_SPACING;
4492 rect.height = psppire_axis_start_pixel (sheet->vaxis, last) + CELL_SPACING
4493 + psppire_axis_unit_size (sheet->vaxis, last);
4495 rect.y -= sheet->vadjustment->value;
4497 minimize_int (&rect.height, sheet->row_title_area.height);
4498 maximize_int (&rect.y, 0);
4500 gdk_window_begin_paint_rect (sheet->row_title_window, &rect);
4501 for (row = first; row <= last; ++row)
4503 GdkRectangle allocation;
4505 gboolean is_sensitive = FALSE;
4507 PsppireSheetButton *button =
4508 psppire_sheet_model_get_row_button (sheet->model, row);
4510 allocation.y = psppire_axis_start_pixel (sheet->vaxis, row)
4512 allocation.y -= sheet->vadjustment->value;
4514 allocation.width = sheet->row_title_area.width;
4515 allocation.height = psppire_axis_unit_size (sheet->vaxis, row);
4516 is_sensitive = psppire_sheet_model_get_row_sensitivity (sheet->model, row);
4518 draw_button (sheet, sheet->row_title_window,
4519 button, is_sensitive, allocation);
4522 gdk_window_end_paint (sheet->row_title_window);
4529 * vadjustment_value_changed
4530 * hadjustment_value_changed */
4534 update_adjustment (GtkAdjustment *adj, PsppireAxis *axis, gint page_size)
4537 (adj->value + adj->page_size)
4539 (adj->upper - adj->lower);
4541 const glong last_item = psppire_axis_unit_count (axis) - 1;
4543 if (isnan (position) || position < 0)
4547 psppire_axis_start_pixel (axis, last_item)
4549 psppire_axis_unit_size (axis, last_item)
4553 adj->page_size = page_size;
4556 adj->value = position * (adj->upper - adj->lower) - adj->page_size;
4558 if ( adj->value < adj->lower)
4559 adj->value = adj->lower;
4562 gtk_adjustment_changed (adj);
4567 adjust_scrollbars (PsppireSheet *sheet)
4571 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4574 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
4576 if ( sheet->row_titles_visible)
4577 width -= sheet->row_title_area.width;
4579 if (sheet->column_titles_visible)
4580 height -= sheet->column_title_area.height;
4582 if (sheet->vadjustment)
4584 glong last_row = psppire_axis_unit_count (sheet->vaxis) - 1;
4586 sheet->vadjustment->step_increment =
4588 psppire_axis_unit_size (sheet->vaxis, last_row);
4590 sheet->vadjustment->page_increment =
4592 sheet->column_title_area.height -
4593 psppire_axis_unit_size (sheet->vaxis, last_row);
4595 update_adjustment (sheet->vadjustment, sheet->vaxis, height);
4598 if (sheet->hadjustment)
4600 gint last_col = psppire_axis_unit_count (sheet->haxis) - 1;
4601 sheet->hadjustment->step_increment = 1;
4603 sheet->hadjustment->page_increment = width;
4605 sheet->hadjustment->upper =
4606 psppire_axis_start_pixel (sheet->haxis, last_col)
4608 psppire_axis_unit_size (sheet->haxis, last_col)
4611 update_adjustment (sheet->hadjustment, sheet->haxis, width);
4615 /* Subtracts the region of WIDGET from REGION */
4617 subtract_widget_region (GdkRegion *region, GtkWidget *widget)
4620 GdkRectangle intersect;
4623 gdk_region_get_clipbox (region, &rect);
4624 gtk_widget_intersect (widget,
4628 region2 = gdk_region_rectangle (&intersect);
4629 gdk_region_subtract (region, region2);
4630 gdk_region_destroy (region2);
4634 vadjustment_value_changed (GtkAdjustment *adjustment,
4638 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4640 g_return_if_fail (adjustment != NULL);
4642 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
4644 gtk_widget_hide (sheet->entry_widget);
4647 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
4649 subtract_widget_region (region, sheet->button);
4650 gdk_window_begin_paint_region (sheet->sheet_window, region);
4652 draw_sheet_region (sheet, region);
4654 draw_row_title_buttons (sheet);
4655 psppire_sheet_draw_active_cell (sheet);
4657 gdk_window_end_paint (sheet->sheet_window);
4658 gdk_region_destroy (region);
4663 hadjustment_value_changed (GtkAdjustment *adjustment,
4667 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4669 g_return_if_fail (adjustment != NULL);
4671 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
4673 gtk_widget_hide (sheet->entry_widget);
4677 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
4679 subtract_widget_region (region, sheet->button);
4680 gdk_window_begin_paint_region (sheet->sheet_window, region);
4682 draw_sheet_region (sheet, region);
4684 draw_column_title_buttons (sheet);
4686 psppire_sheet_draw_active_cell (sheet);
4688 gdk_window_end_paint (sheet->sheet_window);
4690 gdk_region_destroy (region);
4694 /* COLUMN RESIZING */
4696 draw_xor_vline (PsppireSheet *sheet)
4699 gint xpos = sheet->x_drag;
4700 gdk_drawable_get_size (sheet->sheet_window,
4703 if (sheet->row_titles_visible)
4704 xpos += sheet->row_title_area.width;
4706 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
4708 sheet->column_title_area.height,
4710 height + CELL_SPACING);
4715 draw_xor_hline (PsppireSheet *sheet)
4719 gint ypos = sheet->y_drag;
4721 gdk_drawable_get_size (sheet->sheet_window,
4725 if (sheet->column_titles_visible)
4726 ypos += sheet->column_title_area.height;
4728 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
4729 sheet->row_title_area.width,
4731 width + CELL_SPACING,
4735 /* SELECTED RANGE */
4737 draw_xor_rectangle (PsppireSheet *sheet, PsppireSheetRange range)
4740 GdkRectangle clip_area, area;
4743 area.x = psppire_axis_start_pixel (sheet->haxis, range.col0);
4744 area.y = psppire_axis_start_pixel (sheet->vaxis, range.row0);
4745 area.width = psppire_axis_start_pixel (sheet->haxis, range.coli)- area.x+
4746 psppire_axis_unit_size (sheet->haxis, range.coli);
4747 area.height = psppire_axis_start_pixel (sheet->vaxis, range.rowi)- area.y +
4748 psppire_axis_unit_size (sheet->vaxis, range.rowi);
4750 clip_area.x = sheet->row_title_area.width;
4751 clip_area.y = sheet->column_title_area.height;
4753 gdk_drawable_get_size (sheet->sheet_window,
4754 &clip_area.width, &clip_area.height);
4756 if (!sheet->row_titles_visible) clip_area.x = 0;
4757 if (!sheet->column_titles_visible) clip_area.y = 0;
4761 area.width = area.width + area.x;
4764 if (area.width > clip_area.width) area.width = clip_area.width + 10;
4767 area.height = area.height + area.y;
4770 if (area.height > clip_area.height) area.height = clip_area.height + 10;
4774 clip_area.width += 3;
4775 clip_area.height += 3;
4777 gdk_gc_get_values (sheet->xor_gc, &values);
4779 gdk_gc_set_clip_rectangle (sheet->xor_gc, &clip_area);
4781 gdk_draw_rectangle (sheet->sheet_window,
4784 area.x + i, area.y + i,
4785 area.width - 2 * i, area.height - 2 * i);
4788 gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
4790 gdk_gc_set_foreground (sheet->xor_gc, &values.foreground);
4795 set_column_width (PsppireSheet *sheet,
4799 g_return_if_fail (sheet != NULL);
4800 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
4802 if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis))
4808 psppire_axis_resize (sheet->haxis, column,
4809 width - sheet->cell_padding->left -
4810 sheet->cell_padding->right);
4812 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4814 draw_column_title_buttons (sheet);
4815 adjust_scrollbars (sheet);
4816 psppire_sheet_size_allocate_entry (sheet);
4817 redraw_range (sheet, NULL);
4822 set_row_height (PsppireSheet *sheet,
4826 g_return_if_fail (sheet != NULL);
4827 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
4829 if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis))
4835 psppire_axis_resize (sheet->vaxis, row,
4836 height - sheet->cell_padding->top -
4837 sheet->cell_padding->bottom);
4839 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) )
4841 draw_row_title_buttons (sheet);
4842 adjust_scrollbars (sheet);
4843 psppire_sheet_size_allocate_entry (sheet);
4844 redraw_range (sheet, NULL);
4849 psppire_sheet_get_attributes (const PsppireSheet *sheet, gint row, gint col,
4850 PsppireSheetCellAttr *attr)
4853 const GtkJustification *j ;
4854 GdkColormap *colormap;
4856 g_return_val_if_fail (sheet != NULL, FALSE);
4857 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), FALSE);
4859 if (row < 0 || col < 0) return FALSE;
4861 attr->foreground = GTK_WIDGET (sheet)->style->black;
4862 attr->background = sheet->color[BG_COLOR];
4864 attr->border.width = 0;
4865 attr->border.line_style = GDK_LINE_SOLID;
4866 attr->border.cap_style = GDK_CAP_NOT_LAST;
4867 attr->border.join_style = GDK_JOIN_MITER;
4868 attr->border.mask = 0;
4869 attr->border.color = GTK_WIDGET (sheet)->style->black;
4871 colormap = gtk_widget_get_colormap (GTK_WIDGET (sheet));
4872 fg = psppire_sheet_model_get_foreground (sheet->model, row, col);
4875 gdk_colormap_alloc_color (colormap, fg, TRUE, TRUE);
4876 attr->foreground = *fg;
4879 bg = psppire_sheet_model_get_background (sheet->model, row, col);
4882 gdk_colormap_alloc_color (colormap, bg, TRUE, TRUE);
4883 attr->background = *bg;
4886 attr->justification =
4887 psppire_sheet_model_get_column_justification (sheet->model, col);
4889 j = psppire_sheet_model_get_justification (sheet->model, row, col);
4891 attr->justification = *j;
4897 psppire_sheet_button_size_request (PsppireSheet *sheet,
4898 const PsppireSheetButton *button,
4899 GtkRequisition *button_requisition)
4901 GtkRequisition requisition;
4902 GtkRequisition label_requisition;
4904 label_requisition.height = DEFAULT_ROW_HEIGHT;
4905 label_requisition.width = COLUMN_MIN_WIDTH;
4907 requisition.height = DEFAULT_ROW_HEIGHT;
4908 requisition.width = COLUMN_MIN_WIDTH;
4911 *button_requisition = requisition;
4912 button_requisition->width = MAX (requisition.width, label_requisition.width);
4913 button_requisition->height = MAX (requisition.height, label_requisition.height);
4918 psppire_sheet_forall (GtkContainer *container,
4919 gboolean include_internals,
4920 GtkCallback callback,
4921 gpointer callback_data)
4923 PsppireSheet *sheet = PSPPIRE_SHEET (container);
4925 g_return_if_fail (callback != NULL);
4927 if (sheet->button && sheet->button->parent)
4928 (* callback) (sheet->button, callback_data);
4930 if (sheet->entry_widget && GTK_IS_CONTAINER (sheet->entry_widget))
4931 (* callback) (sheet->entry_widget, callback_data);
4936 psppire_sheet_get_model (const PsppireSheet *sheet)
4938 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
4940 return sheet->model;
4944 PsppireSheetButton *
4945 psppire_sheet_button_new (void)
4947 PsppireSheetButton *button = g_malloc (sizeof (PsppireSheetButton));
4949 button->state = GTK_STATE_NORMAL;
4950 button->label = NULL;
4951 button->label_visible = TRUE;
4952 button->justification = GTK_JUSTIFY_FILL;
4953 button->overstruck = FALSE;
4960 psppire_sheet_button_free (PsppireSheetButton *button)
4962 if (!button) return ;
4964 g_free (button->label);
4969 append_cell_text (GString *string, const PsppireSheet *sheet, gint r, gint c)
4971 gchar *celltext = psppire_sheet_cell_get_text (sheet, r, c);
4973 if ( NULL == celltext)
4976 g_string_append (string, celltext);
4982 range_to_text (const PsppireSheet *sheet)
4987 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
4990 string = g_string_sized_new (80);
4992 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
4994 for (c = sheet->range.col0; c < sheet->range.coli; ++c)
4996 append_cell_text (string, sheet, r, c);
4997 g_string_append (string, "\t");
4999 append_cell_text (string, sheet, r, c);
5000 if ( r < sheet->range.rowi)
5001 g_string_append (string, "\n");
5008 range_to_html (const PsppireSheet *sheet)
5013 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
5016 string = g_string_sized_new (480);
5018 g_string_append (string, "<html>\n");
5019 g_string_append (string, "<body>\n");
5020 g_string_append (string, "<table>\n");
5021 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
5023 g_string_append (string, "<tr>\n");
5024 for (c = sheet->range.col0; c <= sheet->range.coli; ++c)
5026 g_string_append (string, "<td>");
5027 append_cell_text (string, sheet, r, c);
5028 g_string_append (string, "</td>\n");
5030 g_string_append (string, "</tr>\n");
5032 g_string_append (string, "</table>\n");
5033 g_string_append (string, "</body>\n");
5034 g_string_append (string, "</html>\n");
5046 primary_get_cb (GtkClipboard *clipboard,
5047 GtkSelectionData *selection_data,
5051 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5052 GString *string = NULL;
5056 case SELECT_FMT_TEXT:
5057 string = range_to_text (sheet);
5059 case SELECT_FMT_HTML:
5060 string = range_to_html (sheet);
5063 g_assert_not_reached ();
5066 gtk_selection_data_set (selection_data, selection_data->target,
5068 (const guchar *) string->str, string->len);
5069 g_string_free (string, TRUE);
5073 primary_clear_cb (GtkClipboard *clipboard,
5076 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5077 if ( ! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
5080 psppire_sheet_real_unselect_range (sheet, NULL);
5084 psppire_sheet_update_primary_selection (PsppireSheet *sheet)
5086 static const GtkTargetEntry targets[] = {
5087 { "UTF8_STRING", 0, SELECT_FMT_TEXT },
5088 { "STRING", 0, SELECT_FMT_TEXT },
5089 { "TEXT", 0, SELECT_FMT_TEXT },
5090 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
5091 { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
5092 { "text/plain", 0, SELECT_FMT_TEXT },
5093 { "text/html", 0, SELECT_FMT_HTML }
5096 GtkClipboard *clipboard;
5098 if (!GTK_WIDGET_REALIZED (sheet))
5101 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (sheet),
5102 GDK_SELECTION_PRIMARY);
5104 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
5106 if (!gtk_clipboard_set_with_owner (clipboard, targets,
5107 G_N_ELEMENTS (targets),
5108 primary_get_cb, primary_clear_cb,
5110 primary_clear_cb (clipboard, sheet);
5114 if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (sheet))
5115 gtk_clipboard_clear (clipboard);