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.
59 #include <gdk/gdkkeysyms.h>
60 #include <gtk/gtksignal.h>
61 #include <gtk/gtkbutton.h>
62 #include <gtk/gtkadjustment.h>
63 #include <gtk/gtktypeutils.h>
64 #include <gtk/gtkentry.h>
65 #include <gtk/gtkcontainer.h>
66 #include <pango/pango.h>
67 #include "psppire-sheet.h"
68 #include <ui/gui/psppire-marshal.h>
69 #include <ui/gui/sheet/psppire-sheetmodel.h>
70 #include <ui/gui/sheet/psppire-axis.h>
71 #include <libpspp/misc.h>
76 PSPPIRE_SHEET_IN_XDRAG = 1 << 1,
77 PSPPIRE_SHEET_IN_YDRAG = 1 << 2,
78 PSPPIRE_SHEET_IN_DRAG = 1 << 3,
80 /* This flag is set when the user is actually in the process
81 of making a selection - ie while the mouse button is
84 PSPPIRE_SHEET_IN_SELECTION = 1 << 4
87 #define PSPPIRE_SHEET_FLAGS(sheet) (PSPPIRE_SHEET (sheet)->flags)
88 #define PSPPIRE_SHEET_SET_FLAGS(sheet,flag) (PSPPIRE_SHEET_FLAGS (sheet) |= (flag))
89 #define PSPPIRE_SHEET_UNSET_FLAGS(sheet,flag) (PSPPIRE_SHEET_FLAGS (sheet) &= ~ (flag))
91 #define PSPPIRE_SHEET_IN_XDRAG(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_XDRAG)
92 #define PSPPIRE_SHEET_IN_YDRAG(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_YDRAG)
93 #define PSPPIRE_SHEET_IN_DRAG(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_DRAG)
94 #define PSPPIRE_SHEET_IN_SELECTION(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_SELECTION)
96 #define CELL_SPACING 1
98 #define TIMEOUT_HOVER 300
99 #define COLUMN_MIN_WIDTH 10
100 #define COLUMN_TITLES_HEIGHT 4
101 #define DEFAULT_COLUMN_WIDTH 80
102 #define DEFAULT_ROW_HEIGHT 25
104 static void set_entry_widget_font (PsppireSheet *sheet);
106 static void psppire_sheet_update_primary_selection (PsppireSheet *sheet);
107 static void draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint n);
108 static void draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint n);
109 static void redraw_range (PsppireSheet *sheet, PsppireSheetRange *range);
112 static void set_row_height (PsppireSheet *sheet,
116 static void destroy_hover_window (PsppireSheetHoverTitle *);
117 static PsppireSheetHoverTitle *create_hover_window (void);
120 dispose_string (const PsppireSheet *sheet, gchar *text)
122 PsppireSheetModel *model = psppire_sheet_get_model (sheet);
127 if (psppire_sheet_model_free_strings (model))
132 /* FIXME: Why bother with these two ? */
134 /* returns the column index from a pixel location */
136 column_from_xpixel (const PsppireSheet *sheet, gint pixel)
138 return psppire_axis_unit_at_pixel (sheet->haxis, pixel);
142 row_from_ypixel (const PsppireSheet *sheet, gint pixel)
144 return psppire_axis_unit_at_pixel (sheet->vaxis, pixel);
148 /* Return the lowest row number which is wholly or partially on
149 the visible range of the sheet */
151 min_visible_row (const PsppireSheet *sheet)
153 return row_from_ypixel (sheet, sheet->vadjustment->value);
157 min_fully_visible_row (const PsppireSheet *sheet)
159 glong row = min_visible_row (sheet);
161 if ( psppire_axis_start_pixel (sheet->vaxis, row) < sheet->vadjustment->value)
168 max_visible_row (const PsppireSheet *sheet)
170 return row_from_ypixel (sheet, sheet->vadjustment->value + sheet->vadjustment->page_size);
175 max_fully_visible_row (const PsppireSheet *sheet)
177 glong row = max_visible_row (sheet);
179 if ( psppire_axis_start_pixel (sheet->vaxis, row)
181 psppire_axis_unit_size (sheet->vaxis, row)
182 > sheet->vadjustment->value)
189 /* Returns the lowest column number which is wholly or partially
192 min_visible_column (const PsppireSheet *sheet)
194 return column_from_xpixel (sheet, sheet->hadjustment->value);
198 min_fully_visible_column (const PsppireSheet *sheet)
200 glong col = min_visible_column (sheet);
202 if ( psppire_axis_start_pixel (sheet->haxis, col) < sheet->hadjustment->value)
209 /* Returns the highest column number which is wholly or partially
212 max_visible_column (const PsppireSheet *sheet)
214 return column_from_xpixel (sheet, sheet->hadjustment->value + sheet->hadjustment->page_size);
218 max_fully_visible_column (const PsppireSheet *sheet)
220 glong col = max_visible_column (sheet);
222 if ( psppire_axis_start_pixel (sheet->haxis, col)
224 psppire_axis_unit_size (sheet->haxis, col)
225 > sheet->hadjustment->value)
233 /* The size of the region (in pixels) around the row/column boundaries
234 where the height/width may be grabbed to change size */
238 on_column_boundary (const PsppireSheet *sheet, gint x, gint *column)
243 x += sheet->hadjustment->value;
248 col = column_from_xpixel (sheet, x);
250 pixel = x - DRAG_WIDTH / 2;
254 if ( column_from_xpixel (sheet, pixel) < col )
260 if ( column_from_xpixel (sheet, x + DRAG_WIDTH / 2) > col )
270 on_row_boundary (const PsppireSheet *sheet, gint y, gint *row)
275 y += sheet->vadjustment->value;
280 r = row_from_ypixel (sheet, y);
282 pixel = y - DRAG_WIDTH / 2;
286 if ( row_from_ypixel (sheet, pixel) < r )
292 if ( row_from_ypixel (sheet, y + DRAG_WIDTH / 2) > r )
302 static inline gboolean
303 POSSIBLE_DRAG (const PsppireSheet *sheet, gint x, gint y,
304 gint *drag_row, gint *drag_column)
308 /* Can't drag if nothing is selected */
309 if ( sheet->range.row0 < 0 || sheet->range.rowi < 0 ||
310 sheet->range.col0 < 0 || sheet->range.coli < 0 )
313 *drag_column = column_from_xpixel (sheet, x);
314 *drag_row = row_from_ypixel (sheet, y);
316 if (x >= psppire_axis_start_pixel (sheet->haxis, sheet->range.col0) - DRAG_WIDTH / 2 &&
317 x <= psppire_axis_start_pixel (sheet->haxis, sheet->range.coli) +
318 psppire_axis_unit_size (sheet->haxis, sheet->range.coli) + DRAG_WIDTH / 2)
320 ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.row0);
321 if (y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2)
323 *drag_row = sheet->range.row0;
326 ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) +
327 psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi);
328 if (y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2)
330 *drag_row = sheet->range.rowi;
335 if (y >= psppire_axis_start_pixel (sheet->vaxis, sheet->range.row0) - DRAG_WIDTH / 2 &&
336 y <= psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) +
337 psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi) + DRAG_WIDTH / 2)
339 xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.col0);
340 if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2)
342 *drag_column = sheet->range.col0;
345 xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.coli) +
346 psppire_axis_unit_size (sheet->haxis, sheet->range.coli);
347 if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2)
349 *drag_column = sheet->range.coli;
357 static inline gboolean
358 POSSIBLE_RESIZE (const PsppireSheet *sheet, gint x, gint y,
359 gint *drag_row, gint *drag_column)
363 /* Can't drag if nothing is selected */
364 if ( sheet->range.row0 < 0 || sheet->range.rowi < 0 ||
365 sheet->range.col0 < 0 || sheet->range.coli < 0 )
368 xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.coli)+
369 psppire_axis_unit_size (sheet->haxis, sheet->range.coli);
371 ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) +
372 psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi);
374 if (sheet->select_status == PSPPIRE_SHEET_COLUMN_SELECTED)
375 ydrag = psppire_axis_start_pixel (sheet->vaxis, min_visible_row (sheet));
377 if (sheet->select_status == PSPPIRE_SHEET_ROW_SELECTED)
378 xdrag = psppire_axis_start_pixel (sheet->haxis, min_visible_column (sheet));
380 *drag_column = column_from_xpixel (sheet, x);
381 *drag_row = row_from_ypixel (sheet, y);
383 if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2 &&
384 y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2) return TRUE;
391 rectangle_from_range (PsppireSheet *sheet, const PsppireSheetRange *range,
394 gint col0 = MIN (range->col0, range->coli);
395 gint coli = MAX (range->col0, range->coli);
396 gint row0 = MIN (range->row0, range->rowi);
397 gint rowi = MAX (range->row0, range->rowi);
399 if ( row0 == -1 ) row0 = min_visible_row (sheet);
400 if ( rowi == -1 ) rowi = max_visible_row (sheet);
401 if ( col0 == -1 ) col0 = min_visible_column (sheet);
402 if ( coli == -1 ) coli = max_visible_column (sheet);
404 r->x = psppire_axis_start_pixel (sheet->haxis, col0);
405 r->x -= round (sheet->hadjustment->value);
407 r->y = psppire_axis_start_pixel (sheet->vaxis, row0);
408 r->y -= round (sheet->vadjustment->value);
410 r->width = psppire_axis_start_pixel (sheet->haxis, coli) -
411 psppire_axis_start_pixel (sheet->haxis, col0) +
412 psppire_axis_unit_size (sheet->haxis, coli);
414 r->height = psppire_axis_start_pixel (sheet->vaxis, rowi) -
415 psppire_axis_start_pixel (sheet->vaxis, row0) +
416 psppire_axis_unit_size (sheet->vaxis, rowi);
418 if ( sheet->column_titles_visible)
420 r->y += sheet->column_title_area.height;
423 if ( sheet->row_titles_visible)
425 r->x += sheet->row_title_area.width;
430 rectangle_from_cell (PsppireSheet *sheet, gint row, gint col,
433 PsppireSheetRange range;
434 g_return_if_fail (row >= 0);
435 g_return_if_fail (col >= 0);
437 range.row0 = range.rowi = row;
438 range.col0 = range.coli = col;
440 rectangle_from_range (sheet, &range, r);
444 static void psppire_sheet_class_init (PsppireSheetClass *klass);
445 static void psppire_sheet_init (PsppireSheet *sheet);
446 static void psppire_sheet_dispose (GObject *object);
447 static void psppire_sheet_finalize (GObject *object);
448 static void psppire_sheet_style_set (GtkWidget *widget,
449 GtkStyle *previous_style);
450 static void psppire_sheet_realize (GtkWidget *widget);
451 static void psppire_sheet_unrealize (GtkWidget *widget);
452 static void psppire_sheet_map (GtkWidget *widget);
453 static void psppire_sheet_unmap (GtkWidget *widget);
454 static gint psppire_sheet_expose (GtkWidget *widget,
455 GdkEventExpose *event);
457 static void psppire_sheet_forall (GtkContainer *container,
458 gboolean include_internals,
459 GtkCallback callback,
460 gpointer callback_data);
462 static gboolean psppire_sheet_set_scroll_adjustments (PsppireSheet *sheet,
463 GtkAdjustment *hadjustment,
464 GtkAdjustment *vadjustment);
466 static gint psppire_sheet_button_press (GtkWidget *widget,
467 GdkEventButton *event);
468 static gint psppire_sheet_button_release (GtkWidget *widget,
469 GdkEventButton *event);
470 static gint psppire_sheet_motion (GtkWidget *widget,
471 GdkEventMotion *event);
472 static gboolean psppire_sheet_crossing_notify (GtkWidget *widget,
473 GdkEventCrossing *event);
474 static gint psppire_sheet_entry_key_press (GtkWidget *widget,
476 static gboolean psppire_sheet_key_press (GtkWidget *widget,
478 static void psppire_sheet_size_request (GtkWidget *widget,
479 GtkRequisition *requisition);
480 static void psppire_sheet_size_allocate (GtkWidget *widget,
481 GtkAllocation *allocation);
483 static gboolean psppire_sheet_focus_in (GtkWidget *widget,
484 GdkEventFocus *event);
488 static gboolean psppire_sheet_range_isvisible (const PsppireSheet *sheet,
489 const PsppireSheetRange *range);
490 static gboolean psppire_sheet_cell_isvisible (PsppireSheet *sheet,
491 gint row, gint column);
492 /* Drawing Routines */
495 static void psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint column);
498 /* draw visible part of range. */
499 static void draw_sheet_region (PsppireSheet *sheet, GdkRegion *region);
503 static void psppire_sheet_draw_border (PsppireSheet *sheet,
504 PsppireSheetRange range);
506 /* Active Cell handling */
508 static void psppire_sheet_hide_entry_widget (PsppireSheet *sheet);
509 static void change_active_cell (PsppireSheet *sheet, gint row, gint col);
510 static gboolean psppire_sheet_draw_active_cell (PsppireSheet *sheet);
511 static void psppire_sheet_show_entry_widget (PsppireSheet *sheet);
512 static gboolean psppire_sheet_click_cell (PsppireSheet *sheet,
519 static void adjust_scrollbars (PsppireSheet *sheet);
520 static void vadjustment_value_changed (GtkAdjustment *adjustment,
522 static void hadjustment_value_changed (GtkAdjustment *adjustment,
526 static void draw_xor_vline (PsppireSheet *sheet);
527 static void draw_xor_hline (PsppireSheet *sheet);
528 static void draw_xor_rectangle (PsppireSheet *sheet,
529 PsppireSheetRange range);
533 static void create_global_button (PsppireSheet *sheet);
534 static void global_button_clicked (GtkWidget *widget,
538 static void create_sheet_entry (PsppireSheet *sheet);
539 static void psppire_sheet_size_allocate_entry (PsppireSheet *sheet);
541 /* Sheet button gadgets */
543 static void draw_column_title_buttons (PsppireSheet *sheet);
544 static void draw_row_title_buttons (PsppireSheet *sheet);
547 static void size_allocate_global_button (PsppireSheet *sheet);
548 static void psppire_sheet_button_size_request (PsppireSheet *sheet,
549 const PsppireSheetButton *button,
550 GtkRequisition *requisition);
552 static void psppire_sheet_real_cell_clear (PsppireSheet *sheet,
574 static GtkContainerClass *parent_class = NULL;
575 static guint sheet_signals[LAST_SIGNAL] = { 0 };
579 psppire_sheet_get_type ()
581 static GType sheet_type = 0;
585 static const GTypeInfo sheet_info =
587 sizeof (PsppireSheetClass),
590 (GClassInitFunc) psppire_sheet_class_init,
593 sizeof (PsppireSheet),
595 (GInstanceInitFunc) psppire_sheet_init,
600 g_type_register_static (GTK_TYPE_BIN, "PsppireSheet",
608 static PsppireSheetRange*
609 psppire_sheet_range_copy (const PsppireSheetRange *range)
611 PsppireSheetRange *new_range;
613 g_return_val_if_fail (range != NULL, NULL);
615 new_range = g_new (PsppireSheetRange, 1);
623 psppire_sheet_range_free (PsppireSheetRange *range)
625 g_return_if_fail (range != NULL);
631 psppire_sheet_range_get_type (void)
633 static GType sheet_range_type = 0;
635 if (!sheet_range_type)
638 g_boxed_type_register_static ("PsppireSheetRange",
639 (GBoxedCopyFunc) psppire_sheet_range_copy,
640 (GBoxedFreeFunc) psppire_sheet_range_free);
643 return sheet_range_type;
646 static PsppireSheetCell*
647 psppire_sheet_cell_copy (const PsppireSheetCell *cell)
649 PsppireSheetCell *new_cell;
651 g_return_val_if_fail (cell != NULL, NULL);
653 new_cell = g_new (PsppireSheetCell, 1);
661 psppire_sheet_cell_free (PsppireSheetCell *cell)
663 g_return_if_fail (cell != NULL);
669 psppire_sheet_cell_get_type (void)
671 static GType sheet_cell_type = 0;
673 if (!sheet_cell_type)
676 g_boxed_type_register_static ("PsppireSheetCell",
677 (GBoxedCopyFunc) psppire_sheet_cell_copy,
678 (GBoxedFreeFunc) psppire_sheet_cell_free);
681 return sheet_cell_type;
696 resize_column (PsppireSheet *sheet, gint unit, glong size)
698 PsppireSheetRange range;
700 range.coli = max_visible_column (sheet);
701 range.row0 = min_visible_row (sheet);
702 range.rowi = max_visible_row (sheet);
704 redraw_range (sheet, &range);
706 draw_column_title_buttons_range (sheet, range.col0, range.coli);
711 psppire_sheet_set_horizontal_axis (PsppireSheet *sheet, PsppireAxis *a)
714 g_object_unref (sheet->haxis);
717 g_signal_connect_swapped (a, "resize-unit", G_CALLBACK (resize_column), sheet);
720 g_object_ref (sheet->haxis);
724 resize_row (PsppireSheet *sheet, gint unit, glong size)
726 PsppireSheetRange range;
727 range.col0 = min_visible_column (sheet);
728 range.coli = max_visible_column (sheet);
730 range.rowi = max_visible_row (sheet);
732 redraw_range (sheet, &range);
734 draw_row_title_buttons_range (sheet, range.row0, range.rowi);
738 psppire_sheet_set_vertical_axis (PsppireSheet *sheet, PsppireAxis *a)
741 g_object_unref (sheet->vaxis);
745 g_signal_connect_swapped (a, "resize-unit", G_CALLBACK (resize_row), sheet);
748 g_object_ref (sheet->vaxis);
751 static const GtkBorder default_cell_padding = {3, 3, 3, 3};
754 psppire_sheet_set_property (GObject *object,
760 PsppireSheet *sheet = PSPPIRE_SHEET (object);
764 case PROP_CELL_PADDING:
765 if ( sheet->cell_padding)
766 g_boxed_free (GTK_TYPE_BORDER, sheet->cell_padding);
768 sheet->cell_padding = g_value_dup_boxed (value);
770 if (NULL == sheet->cell_padding)
771 sheet->cell_padding = g_boxed_copy (GTK_TYPE_BORDER,
772 &default_cell_padding);
775 g_object_set (sheet->vaxis, "padding",
776 sheet->cell_padding->top + sheet->cell_padding->bottom,
780 g_object_set (sheet->haxis, "padding",
781 sheet->cell_padding->left + sheet->cell_padding->right,
785 psppire_sheet_set_vertical_axis (sheet, g_value_get_pointer (value));
786 g_object_set (sheet->vaxis, "padding",
787 sheet->cell_padding->top + sheet->cell_padding->bottom,
791 psppire_sheet_set_horizontal_axis (sheet, g_value_get_pointer (value));
792 g_object_set (sheet->haxis, "padding",
793 sheet->cell_padding->left + sheet->cell_padding->right,
797 psppire_sheet_set_model (sheet, g_value_get_pointer (value));
800 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
806 psppire_sheet_get_property (GObject *object,
811 PsppireSheet *sheet = PSPPIRE_SHEET (object);
815 case PROP_CELL_PADDING:
816 g_value_set_boxed (value, sheet->cell_padding);
819 g_value_set_pointer (value, sheet->vaxis);
822 g_value_set_pointer (value, sheet->haxis);
825 g_value_set_pointer (value, sheet->model);
828 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
835 psppire_sheet_class_init (PsppireSheetClass *klass)
837 GObjectClass *object_class = G_OBJECT_CLASS (klass);
839 GParamSpec *haxis_spec ;
840 GParamSpec *vaxis_spec ;
841 GParamSpec *model_spec ;
842 GParamSpec *cell_padding_spec ;
844 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
845 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
847 parent_class = g_type_class_peek_parent (klass);
850 * PsppireSheet::select-row
851 * @sheet: the sheet widget that emitted the signal
852 * @row: the newly selected row index, or -1 if no row is selected.
854 * A row has been selected.
856 sheet_signals[SELECT_ROW] =
857 g_signal_new ("select-row",
858 G_TYPE_FROM_CLASS (object_class),
860 offsetof (PsppireSheetClass, select_row),
862 g_cclosure_marshal_VOID__INT,
869 * PsppireSheet::select - column
870 * @sheet: the sheet widget that emitted the signal
871 * @column: the newly selected column index, or -1 if no column is selected.
873 * A column has been selected.
875 sheet_signals[SELECT_COLUMN] =
876 g_signal_new ("select-column",
877 G_TYPE_FROM_CLASS (object_class),
879 offsetof (PsppireSheetClass, select_column),
881 g_cclosure_marshal_VOID__INT,
888 * PsppireSheet::double-click-row
889 * @sheet: the sheet widget that emitted the signal
890 * @row: the row that was double clicked.
892 * A row's title button has been double clicked
894 sheet_signals[DOUBLE_CLICK_ROW] =
895 g_signal_new ("double-click-row",
896 G_TYPE_FROM_CLASS (object_class),
900 g_cclosure_marshal_VOID__INT,
907 * PsppireSheet::double-click-column
908 * @sheet: the sheet widget that emitted the signal
909 * @column: the column that was double clicked.
911 * A column's title button has been double clicked
913 sheet_signals[DOUBLE_CLICK_COLUMN] =
914 g_signal_new ("double-click-column",
915 G_TYPE_FROM_CLASS (object_class),
919 g_cclosure_marshal_VOID__INT,
926 * PsppireSheet::button-event-column
927 * @sheet: the sheet widget that emitted the signal
928 * @column: the column on which the event occured.
930 * A button event occured on a column title button
932 sheet_signals[BUTTON_EVENT_COLUMN] =
933 g_signal_new ("button-event-column",
934 G_TYPE_FROM_CLASS (object_class),
938 psppire_marshal_VOID__INT_POINTER,
947 * PsppireSheet::button-event-row
948 * @sheet: the sheet widget that emitted the signal
949 * @column: the column on which the event occured.
951 * A button event occured on a row title button
953 sheet_signals[BUTTON_EVENT_ROW] =
954 g_signal_new ("button-event-row",
955 G_TYPE_FROM_CLASS (object_class),
959 psppire_marshal_VOID__INT_POINTER,
967 sheet_signals[SELECT_RANGE] =
968 g_signal_new ("select-range",
969 G_TYPE_FROM_CLASS (object_class),
971 offsetof (PsppireSheetClass, select_range),
973 g_cclosure_marshal_VOID__BOXED,
976 PSPPIRE_TYPE_SHEET_RANGE);
979 sheet_signals[RESIZE_RANGE] =
980 g_signal_new ("resize-range",
981 G_TYPE_FROM_CLASS (object_class),
983 offsetof (PsppireSheetClass, resize_range),
985 psppire_marshal_VOID__BOXED_BOXED,
988 PSPPIRE_TYPE_SHEET_RANGE, PSPPIRE_TYPE_SHEET_RANGE
991 sheet_signals[MOVE_RANGE] =
992 g_signal_new ("move-range",
993 G_TYPE_FROM_CLASS (object_class),
995 offsetof (PsppireSheetClass, move_range),
997 psppire_marshal_VOID__BOXED_BOXED,
1000 PSPPIRE_TYPE_SHEET_RANGE, PSPPIRE_TYPE_SHEET_RANGE
1003 sheet_signals[TRAVERSE] =
1004 g_signal_new ("traverse",
1005 G_TYPE_FROM_CLASS (object_class),
1007 offsetof (PsppireSheetClass, traverse),
1009 psppire_marshal_BOOLEAN__BOXED_POINTER,
1011 PSPPIRE_TYPE_SHEET_CELL,
1015 sheet_signals[ACTIVATE] =
1016 g_signal_new ("activate",
1017 G_TYPE_FROM_CLASS (object_class),
1019 offsetof (PsppireSheetClass, activate),
1021 psppire_marshal_VOID__INT_INT_INT_INT,
1023 G_TYPE_INT, G_TYPE_INT,
1024 G_TYPE_INT, G_TYPE_INT);
1026 widget_class->set_scroll_adjustments_signal =
1027 g_signal_new ("set-scroll-adjustments",
1028 G_TYPE_FROM_CLASS (object_class),
1030 offsetof (PsppireSheetClass, set_scroll_adjustments),
1032 psppire_marshal_VOID__OBJECT_OBJECT,
1033 G_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
1036 container_class->add = NULL;
1037 container_class->remove = NULL;
1038 container_class->forall = psppire_sheet_forall;
1039 container_class->set_focus_child = NULL;
1041 object_class->dispose = psppire_sheet_dispose;
1042 object_class->finalize = psppire_sheet_finalize;
1045 g_param_spec_boxed ("cell-padding",
1047 "The space between a cell's contents and its border",
1049 G_PARAM_CONSTRUCT | G_PARAM_READABLE | G_PARAM_WRITABLE);
1052 g_param_spec_pointer ("vertical-axis",
1054 "A pointer to the PsppireAxis object for the rows",
1055 G_PARAM_READABLE | G_PARAM_WRITABLE );
1058 g_param_spec_pointer ("horizontal-axis",
1060 "A pointer to the PsppireAxis object for the columns",
1061 G_PARAM_READABLE | G_PARAM_WRITABLE );
1064 g_param_spec_pointer ("model",
1066 "A pointer to the data model",
1067 G_PARAM_READABLE | G_PARAM_WRITABLE );
1070 object_class->set_property = psppire_sheet_set_property;
1071 object_class->get_property = psppire_sheet_get_property;
1073 g_object_class_install_property (object_class,
1077 g_object_class_install_property (object_class,
1081 g_object_class_install_property (object_class,
1085 g_object_class_install_property (object_class,
1090 widget_class->realize = psppire_sheet_realize;
1091 widget_class->unrealize = psppire_sheet_unrealize;
1092 widget_class->map = psppire_sheet_map;
1093 widget_class->unmap = psppire_sheet_unmap;
1094 widget_class->style_set = psppire_sheet_style_set;
1095 widget_class->button_press_event = psppire_sheet_button_press;
1096 widget_class->button_release_event = psppire_sheet_button_release;
1097 widget_class->motion_notify_event = psppire_sheet_motion;
1098 widget_class->enter_notify_event = psppire_sheet_crossing_notify;
1099 widget_class->leave_notify_event = psppire_sheet_crossing_notify;
1100 widget_class->key_press_event = psppire_sheet_key_press;
1101 widget_class->expose_event = psppire_sheet_expose;
1102 widget_class->size_request = psppire_sheet_size_request;
1103 widget_class->size_allocate = psppire_sheet_size_allocate;
1104 widget_class->focus_in_event = psppire_sheet_focus_in;
1105 widget_class->focus_out_event = NULL;
1107 klass->set_scroll_adjustments = psppire_sheet_set_scroll_adjustments;
1108 klass->select_row = NULL;
1109 klass->select_column = NULL;
1110 klass->select_range = NULL;
1111 klass->resize_range = NULL;
1112 klass->move_range = NULL;
1113 klass->traverse = NULL;
1114 klass->activate = NULL;
1115 klass->changed = NULL;
1119 psppire_sheet_init (PsppireSheet *sheet)
1121 sheet->model = NULL;
1122 sheet->haxis = NULL;
1123 sheet->vaxis = NULL;
1126 sheet->select_status = PSPPIRE_SHEET_NORMAL;
1128 GTK_WIDGET_UNSET_FLAGS (sheet, GTK_NO_WINDOW);
1129 GTK_WIDGET_SET_FLAGS (sheet, GTK_CAN_FOCUS);
1131 sheet->column_title_window = NULL;
1132 sheet->column_title_area.x = 0;
1133 sheet->column_title_area.y = 0;
1134 sheet->column_title_area.width = 0;
1135 sheet->column_title_area.height = DEFAULT_ROW_HEIGHT;
1137 sheet->row_title_window = NULL;
1138 sheet->row_title_area.x = 0;
1139 sheet->row_title_area.y = 0;
1140 sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
1141 sheet->row_title_area.height = 0;
1144 sheet->active_cell.row = 0;
1145 sheet->active_cell.col = 0;
1147 sheet->range.row0 = 0;
1148 sheet->range.rowi = 0;
1149 sheet->range.col0 = 0;
1150 sheet->range.coli = 0;
1152 sheet->sheet_window = NULL;
1153 sheet->entry_widget = NULL;
1154 sheet->button = NULL;
1156 sheet->hadjustment = NULL;
1157 sheet->vadjustment = NULL;
1159 sheet->cursor_drag = NULL;
1161 sheet->xor_gc = NULL;
1162 sheet->fg_gc = NULL;
1163 sheet->bg_gc = NULL;
1166 sheet->show_grid = TRUE;
1168 sheet->motion_timer = 0;
1170 sheet->row_titles_visible = TRUE;
1171 sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
1173 sheet->column_titles_visible = TRUE;
1176 /* create sheet entry */
1177 sheet->entry_type = GTK_TYPE_ENTRY;
1178 create_sheet_entry (sheet);
1180 /* create global selection button */
1181 create_global_button (sheet);
1185 /* Cause RANGE to be redrawn. If RANGE is null, then the
1186 entire visible range will be redrawn.
1189 redraw_range (PsppireSheet *sheet, PsppireSheetRange *range)
1193 if ( ! GTK_WIDGET_REALIZED (sheet))
1196 if ( NULL != range )
1197 rectangle_from_range (sheet, range, &rect);
1200 GdkRegion *r = gdk_drawable_get_visible_region (sheet->sheet_window);
1201 gdk_region_get_clipbox (r, &rect);
1203 if ( sheet->column_titles_visible)
1205 rect.y += sheet->column_title_area.height;
1206 rect.height -= sheet->column_title_area.height;
1209 if ( sheet->row_titles_visible)
1211 rect.x += sheet->row_title_area.width;
1212 rect.width -= sheet->row_title_area.width;
1216 gdk_window_invalidate_rect (sheet->sheet_window, &rect, FALSE);
1220 /* Callback which occurs whenever columns are inserted / deleted in the model */
1222 columns_inserted_deleted_callback (PsppireSheetModel *model, gint first_column,
1226 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1228 PsppireSheetRange range;
1229 gint model_columns = psppire_sheet_model_get_column_count (model);
1232 /* Need to update all the columns starting from the first column and onwards.
1233 * Previous column are unchanged, so don't need to be updated.
1235 range.col0 = first_column;
1237 range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1238 range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1240 adjust_scrollbars (sheet);
1242 if (sheet->active_cell.col >= model_columns)
1243 change_active_cell (sheet, sheet->active_cell.row, model_columns - 1);
1245 draw_column_title_buttons_range (sheet,
1246 first_column, max_visible_column (sheet));
1249 redraw_range (sheet, &range);
1255 /* Callback which occurs whenever rows are inserted / deleted in the model */
1257 rows_inserted_deleted_callback (PsppireSheetModel *model, gint first_row,
1258 gint n_rows, gpointer data)
1260 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1262 PsppireSheetRange range;
1264 gint model_rows = psppire_sheet_model_get_row_count (model);
1266 /* Need to update all the rows starting from the first row and onwards.
1267 * Previous rows are unchanged, so don't need to be updated.
1269 range.row0 = first_row;
1271 range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1272 range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1274 adjust_scrollbars (sheet);
1276 if (sheet->active_cell.row >= model_rows)
1277 change_active_cell (sheet, model_rows - 1, sheet->active_cell.col);
1279 draw_row_title_buttons_range (sheet, first_row, max_visible_row (sheet));
1281 redraw_range (sheet, &range);
1285 If row0 or rowi are negative, then all rows will be updated.
1286 If col0 or coli are negative, then all columns will be updated.
1289 range_update_callback (PsppireSheetModel *m, gint row0, gint col0,
1290 gint rowi, gint coli, gpointer data)
1292 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1294 PsppireSheetRange range;
1301 if ( !GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1304 if ( ( row0 < 0 && col0 < 0 ) || ( rowi < 0 && coli < 0 ) )
1306 redraw_range (sheet, NULL);
1307 adjust_scrollbars (sheet);
1309 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
1310 max_visible_row (sheet));
1312 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
1313 max_visible_column (sheet));
1317 else if ( row0 < 0 || rowi < 0 )
1319 range.row0 = min_visible_row (sheet);
1320 range.rowi = max_visible_row (sheet);
1322 else if ( col0 < 0 || coli < 0 )
1324 range.col0 = min_visible_column (sheet);
1325 range.coli = max_visible_column (sheet);
1328 redraw_range (sheet, &range);
1333 * psppire_sheet_new:
1334 * @rows: initial number of rows
1335 * @columns: initial number of columns
1336 * @title: sheet title
1337 * @model: the model to use for the sheet data
1339 * Creates a new sheet widget with the given number of rows and columns.
1341 * Returns: the new sheet widget
1344 psppire_sheet_new (PsppireSheetModel *model)
1346 GtkWidget *widget = g_object_new (PSPPIRE_TYPE_SHEET,
1354 * psppire_sheet_set_model
1355 * @sheet: the sheet to set the model for
1356 * @model: the model to use for the sheet data
1358 * Sets the model for a PsppireSheet
1362 psppire_sheet_set_model (PsppireSheet *sheet, PsppireSheetModel *model)
1364 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1366 if (sheet->model ) g_object_unref (sheet->model);
1368 sheet->model = model;
1372 g_object_ref (model);
1374 sheet->update_handler_id = g_signal_connect (model, "range_changed",
1375 G_CALLBACK (range_update_callback),
1378 g_signal_connect (model, "rows_inserted",
1379 G_CALLBACK (rows_inserted_deleted_callback), sheet);
1381 g_signal_connect (model, "rows_deleted",
1382 G_CALLBACK (rows_inserted_deleted_callback), sheet);
1384 g_signal_connect (model, "columns_inserted",
1385 G_CALLBACK (columns_inserted_deleted_callback), sheet);
1387 g_signal_connect (model, "columns_deleted",
1388 G_CALLBACK (columns_inserted_deleted_callback), sheet);
1394 psppire_sheet_change_entry (PsppireSheet *sheet, GtkType entry_type)
1396 g_return_if_fail (sheet != NULL);
1397 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1399 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
1400 psppire_sheet_hide_entry_widget (sheet);
1402 sheet->entry_type = entry_type;
1404 create_sheet_entry (sheet);
1406 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
1407 psppire_sheet_show_entry_widget (sheet);
1411 psppire_sheet_show_grid (PsppireSheet *sheet, gboolean show)
1413 g_return_if_fail (sheet != NULL);
1414 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1416 if (show == sheet->show_grid) return;
1418 sheet->show_grid = show;
1420 redraw_range (sheet, NULL);
1424 psppire_sheet_grid_visible (PsppireSheet *sheet)
1426 g_return_val_if_fail (sheet != NULL, 0);
1427 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
1429 return sheet->show_grid;
1433 psppire_sheet_get_columns_count (PsppireSheet *sheet)
1435 g_return_val_if_fail (sheet != NULL, 0);
1436 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
1438 return psppire_axis_unit_count (sheet->haxis);
1441 static void set_column_width (PsppireSheet *sheet,
1447 psppire_sheet_show_column_titles (PsppireSheet *sheet)
1449 if (sheet->column_titles_visible) return;
1451 sheet->column_titles_visible = TRUE;
1453 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1456 gdk_window_show (sheet->column_title_window);
1457 gdk_window_move_resize (sheet->column_title_window,
1458 sheet->column_title_area.x,
1459 sheet->column_title_area.y,
1460 sheet->column_title_area.width,
1461 sheet->column_title_area.height);
1463 adjust_scrollbars (sheet);
1465 if (sheet->vadjustment)
1466 g_signal_emit_by_name (sheet->vadjustment,
1469 size_allocate_global_button (sheet);
1471 if ( sheet->row_titles_visible)
1472 gtk_widget_show (sheet->button);
1477 psppire_sheet_show_row_titles (PsppireSheet *sheet)
1479 if (sheet->row_titles_visible) return;
1481 sheet->row_titles_visible = TRUE;
1484 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1486 gdk_window_show (sheet->row_title_window);
1487 gdk_window_move_resize (sheet->row_title_window,
1488 sheet->row_title_area.x,
1489 sheet->row_title_area.y,
1490 sheet->row_title_area.width,
1491 sheet->row_title_area.height);
1493 adjust_scrollbars (sheet);
1496 if (sheet->hadjustment)
1497 g_signal_emit_by_name (sheet->hadjustment,
1500 size_allocate_global_button (sheet);
1502 if ( sheet->column_titles_visible)
1503 gtk_widget_show (sheet->button);
1507 psppire_sheet_hide_column_titles (PsppireSheet *sheet)
1509 if (!sheet->column_titles_visible) return;
1511 sheet->column_titles_visible = FALSE;
1513 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1515 if (sheet->column_title_window)
1516 gdk_window_hide (sheet->column_title_window);
1518 gtk_widget_hide (sheet->button);
1520 adjust_scrollbars (sheet);
1523 if (sheet->vadjustment)
1524 g_signal_emit_by_name (sheet->vadjustment,
1529 psppire_sheet_hide_row_titles (PsppireSheet *sheet)
1531 if (!sheet->row_titles_visible) return;
1533 sheet->row_titles_visible = FALSE;
1535 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1537 if (sheet->row_title_window)
1538 gdk_window_hide (sheet->row_title_window);
1540 gtk_widget_hide (sheet->button);
1542 adjust_scrollbars (sheet);
1545 if (sheet->hadjustment)
1546 g_signal_emit_by_name (sheet->hadjustment,
1551 /* Scroll the sheet so that the cell ROW, COLUMN is visible.
1552 If {ROW,COL}_ALIGN is zero, then the cell will be placed
1553 at the {top,left} of the sheet. If it's 1, then it'll
1554 be placed at the {bottom,right}.
1555 ROW or COL may be -1, in which case scrolling in that dimension
1559 psppire_sheet_moveto (PsppireSheet *sheet,
1567 g_return_if_fail (row_align >= 0);
1568 g_return_if_fail (col_align >= 0);
1570 g_return_if_fail (row_align <= 1);
1571 g_return_if_fail (col_align <= 1);
1573 g_return_if_fail (col <
1574 psppire_axis_unit_count (sheet->haxis));
1575 g_return_if_fail (row <
1576 psppire_axis_unit_count (sheet->vaxis));
1578 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
1583 gint y = psppire_axis_start_pixel (sheet->vaxis, row);
1585 gtk_adjustment_set_value (sheet->vadjustment, y - height * row_align);
1591 gint x = psppire_axis_start_pixel (sheet->haxis, col);
1593 gtk_adjustment_set_value (sheet->hadjustment, x - width * col_align);
1601 psppire_sheet_range_isvisible (const PsppireSheet *sheet,
1602 const PsppireSheetRange *range)
1604 g_return_val_if_fail (sheet != NULL, FALSE);
1606 if (range->row0 < 0 || range->row0 >= psppire_axis_unit_count (sheet->vaxis))
1609 if (range->rowi < 0 || range->rowi >= psppire_axis_unit_count (sheet->vaxis))
1612 if (range->col0 < 0 || range->col0 >= psppire_axis_unit_count (sheet->haxis))
1615 if (range->coli < 0 || range->coli >= psppire_axis_unit_count (sheet->haxis))
1618 if (range->rowi < min_visible_row (sheet))
1621 if (range->row0 > max_visible_row (sheet))
1624 if (range->coli < min_visible_column (sheet))
1627 if (range->col0 > max_visible_column (sheet))
1634 psppire_sheet_cell_isvisible (PsppireSheet *sheet,
1635 gint row, gint column)
1637 PsppireSheetRange range;
1640 range.col0 = column;
1642 range.coli = column;
1644 return psppire_sheet_range_isvisible (sheet, &range);
1648 psppire_sheet_get_visible_range (PsppireSheet *sheet, PsppireSheetRange *range)
1650 g_return_if_fail (sheet != NULL);
1651 g_return_if_fail (PSPPIRE_IS_SHEET (sheet)) ;
1652 g_return_if_fail (range != NULL);
1654 range->row0 = min_visible_row (sheet);
1655 range->col0 = min_visible_column (sheet);
1656 range->rowi = max_visible_row (sheet);
1657 range->coli = max_visible_column (sheet);
1662 psppire_sheet_set_scroll_adjustments (PsppireSheet *sheet,
1663 GtkAdjustment *hadjustment,
1664 GtkAdjustment *vadjustment)
1666 if ( sheet->vadjustment != vadjustment )
1668 if (sheet->vadjustment)
1669 g_object_unref (sheet->vadjustment);
1670 sheet->vadjustment = vadjustment;
1674 g_object_ref (vadjustment);
1676 g_signal_connect (sheet->vadjustment, "value_changed",
1677 G_CALLBACK (vadjustment_value_changed),
1682 if ( sheet->hadjustment != hadjustment )
1684 if (sheet->hadjustment)
1685 g_object_unref (sheet->hadjustment);
1687 sheet->hadjustment = hadjustment;
1691 g_object_ref (hadjustment);
1693 g_signal_connect (sheet->hadjustment, "value_changed",
1694 G_CALLBACK (hadjustment_value_changed),
1702 psppire_sheet_finalize (GObject *object)
1704 PsppireSheet *sheet;
1706 g_return_if_fail (object != NULL);
1707 g_return_if_fail (PSPPIRE_IS_SHEET (object));
1709 sheet = PSPPIRE_SHEET (object);
1711 if (G_OBJECT_CLASS (parent_class)->finalize)
1712 (*G_OBJECT_CLASS (parent_class)->finalize) (object);
1716 psppire_sheet_dispose (GObject *object)
1718 PsppireSheet *sheet = PSPPIRE_SHEET (object);
1720 g_return_if_fail (object != NULL);
1721 g_return_if_fail (PSPPIRE_IS_SHEET (object));
1723 if ( sheet->dispose_has_run )
1726 sheet->dispose_has_run = TRUE;
1728 if ( sheet->cell_padding)
1729 g_boxed_free (GTK_TYPE_BORDER, sheet->cell_padding);
1731 if (sheet->model) g_object_unref (sheet->model);
1732 if (sheet->vaxis) g_object_unref (sheet->vaxis);
1733 if (sheet->haxis) g_object_unref (sheet->haxis);
1735 g_object_unref (sheet->button);
1736 sheet->button = NULL;
1738 /* unref adjustments */
1739 if (sheet->hadjustment)
1741 g_signal_handlers_disconnect_matched (sheet->hadjustment,
1742 G_SIGNAL_MATCH_DATA,
1746 g_object_unref (sheet->hadjustment);
1747 sheet->hadjustment = NULL;
1750 if (sheet->vadjustment)
1752 g_signal_handlers_disconnect_matched (sheet->vadjustment,
1753 G_SIGNAL_MATCH_DATA,
1757 g_object_unref (sheet->vadjustment);
1759 sheet->vadjustment = NULL;
1762 if (G_OBJECT_CLASS (parent_class)->dispose)
1763 (*G_OBJECT_CLASS (parent_class)->dispose) (object);
1767 psppire_sheet_style_set (GtkWidget *widget,
1768 GtkStyle *previous_style)
1770 PsppireSheet *sheet;
1772 g_return_if_fail (widget != NULL);
1773 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1775 if (GTK_WIDGET_CLASS (parent_class)->style_set)
1776 (*GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous_style);
1778 sheet = PSPPIRE_SHEET (widget);
1780 if (GTK_WIDGET_REALIZED (widget))
1782 gtk_style_set_background (widget->style, widget->window, widget->state);
1785 set_entry_widget_font (sheet);
1790 psppire_sheet_realize (GtkWidget *widget)
1792 PsppireSheet *sheet;
1793 GdkWindowAttr attributes;
1794 const gint attributes_mask =
1795 GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_CURSOR;
1798 GdkColormap *colormap;
1799 GdkDisplay *display;
1801 g_return_if_fail (widget != NULL);
1802 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1804 sheet = PSPPIRE_SHEET (widget);
1806 colormap = gtk_widget_get_colormap (widget);
1807 display = gtk_widget_get_display (widget);
1809 attributes.window_type = GDK_WINDOW_CHILD;
1810 attributes.x = widget->allocation.x;
1811 attributes.y = widget->allocation.y;
1812 attributes.width = widget->allocation.width;
1813 attributes.height = widget->allocation.height;
1814 attributes.wclass = GDK_INPUT_OUTPUT;
1816 attributes.visual = gtk_widget_get_visual (widget);
1817 attributes.colormap = colormap;
1819 attributes.event_mask = gtk_widget_get_events (widget);
1820 attributes.event_mask |= (GDK_EXPOSURE_MASK |
1821 GDK_BUTTON_PRESS_MASK |
1822 GDK_BUTTON_RELEASE_MASK |
1823 GDK_KEY_PRESS_MASK |
1824 GDK_ENTER_NOTIFY_MASK |
1825 GDK_LEAVE_NOTIFY_MASK |
1826 GDK_POINTER_MOTION_MASK |
1827 GDK_POINTER_MOTION_HINT_MASK);
1829 attributes.cursor = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
1832 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1834 gdk_window_set_user_data (widget->window, sheet);
1836 widget->style = gtk_style_attach (widget->style, widget->window);
1838 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1840 gdk_color_parse ("white", &sheet->color[BG_COLOR]);
1841 gdk_colormap_alloc_color (colormap, &sheet->color[BG_COLOR], FALSE,
1843 gdk_color_parse ("gray", &sheet->color[GRID_COLOR]);
1844 gdk_colormap_alloc_color (colormap, &sheet->color[GRID_COLOR], FALSE,
1849 attributes.width = sheet->column_title_area.width;
1850 attributes.height = sheet->column_title_area.height;
1853 /* column - title window */
1854 sheet->column_title_window =
1855 gdk_window_new (widget->window, &attributes, attributes_mask);
1856 gdk_window_set_user_data (sheet->column_title_window, sheet);
1857 gtk_style_set_background (widget->style, sheet->column_title_window,
1863 attributes.width = sheet->row_title_area.width;
1864 attributes.height = sheet->row_title_area.height;
1866 /* row - title window */
1867 sheet->row_title_window = gdk_window_new (widget->window,
1868 &attributes, attributes_mask);
1869 gdk_window_set_user_data (sheet->row_title_window, sheet);
1870 gtk_style_set_background (widget->style, sheet->row_title_window,
1873 /* sheet - window */
1874 attributes.cursor = gdk_cursor_new_for_display (display, GDK_PLUS);
1879 sheet->sheet_window = gdk_window_new (widget->window,
1880 &attributes, attributes_mask);
1881 gdk_window_set_user_data (sheet->sheet_window, sheet);
1883 gdk_cursor_unref (attributes.cursor);
1885 gdk_window_set_background (sheet->sheet_window, &widget->style->white);
1886 gdk_window_show (sheet->sheet_window);
1889 sheet->fg_gc = gdk_gc_new (widget->window);
1890 sheet->bg_gc = gdk_gc_new (widget->window);
1892 values.foreground = widget->style->white;
1893 values.function = GDK_INVERT;
1894 values.subwindow_mode = GDK_INCLUDE_INFERIORS;
1895 values.line_width = MAX (sheet->cell_padding->left,
1896 MAX (sheet->cell_padding->right,
1897 MAX (sheet->cell_padding->top,
1898 sheet->cell_padding->bottom)));
1900 sheet->xor_gc = gdk_gc_new_with_values (widget->window,
1908 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
1909 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
1911 gtk_widget_set_parent_window (sheet->button, sheet->sheet_window);
1912 gtk_widget_set_parent (sheet->button, GTK_WIDGET (sheet));
1914 sheet->button->style = gtk_style_attach (sheet->button->style,
1915 sheet->sheet_window);
1918 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
1920 if (sheet->column_titles_visible)
1921 gdk_window_show (sheet->column_title_window);
1922 if (sheet->row_titles_visible)
1923 gdk_window_show (sheet->row_title_window);
1925 sheet->hover_window = create_hover_window ();
1927 draw_row_title_buttons (sheet);
1928 draw_column_title_buttons (sheet);
1930 psppire_sheet_update_primary_selection (sheet);
1933 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1937 create_global_button (PsppireSheet *sheet)
1939 sheet->button = gtk_button_new_with_label (" ");
1941 GTK_WIDGET_UNSET_FLAGS(sheet->button, GTK_CAN_FOCUS);
1943 g_object_ref_sink (sheet->button);
1945 g_signal_connect (sheet->button,
1947 G_CALLBACK (global_button_clicked),
1952 size_allocate_global_button (PsppireSheet *sheet)
1954 GtkAllocation allocation;
1956 if (!sheet->column_titles_visible) return;
1957 if (!sheet->row_titles_visible) return;
1959 gtk_widget_size_request (sheet->button, NULL);
1963 allocation.width = sheet->row_title_area.width;
1964 allocation.height = sheet->column_title_area.height;
1966 gtk_widget_size_allocate (sheet->button, &allocation);
1970 global_button_clicked (GtkWidget *widget, gpointer data)
1972 psppire_sheet_click_cell (PSPPIRE_SHEET (data), -1, -1);
1977 psppire_sheet_unrealize (GtkWidget *widget)
1979 PsppireSheet *sheet;
1981 g_return_if_fail (widget != NULL);
1982 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1984 sheet = PSPPIRE_SHEET (widget);
1986 gdk_cursor_unref (sheet->cursor_drag);
1987 sheet->cursor_drag = NULL;
1989 gdk_colormap_free_colors (gtk_widget_get_colormap (widget),
1990 sheet->color, n_COLORS);
1992 g_object_unref (sheet->xor_gc);
1993 g_object_unref (sheet->fg_gc);
1994 g_object_unref (sheet->bg_gc);
1996 destroy_hover_window (sheet->hover_window);
1998 gdk_window_destroy (sheet->sheet_window);
1999 gdk_window_destroy (sheet->column_title_window);
2000 gdk_window_destroy (sheet->row_title_window);
2002 gtk_widget_unparent (sheet->entry_widget);
2003 if (sheet->button != NULL)
2004 gtk_widget_unparent (sheet->button);
2006 if (GTK_WIDGET_CLASS (parent_class)->unrealize)
2007 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
2011 psppire_sheet_map (GtkWidget *widget)
2013 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2015 g_return_if_fail (widget != NULL);
2016 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
2018 if (!GTK_WIDGET_MAPPED (widget))
2020 GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
2022 gdk_window_show (widget->window);
2023 gdk_window_show (sheet->sheet_window);
2025 if (sheet->column_titles_visible)
2027 draw_column_title_buttons (sheet);
2028 gdk_window_show (sheet->column_title_window);
2030 if (sheet->row_titles_visible)
2032 draw_row_title_buttons (sheet);
2033 gdk_window_show (sheet->row_title_window);
2036 if (!GTK_WIDGET_MAPPED (sheet->entry_widget)
2037 && sheet->active_cell.row >= 0
2038 && sheet->active_cell.col >= 0 )
2040 gtk_widget_show (sheet->entry_widget);
2041 gtk_widget_map (sheet->entry_widget);
2044 if (!GTK_WIDGET_MAPPED (sheet->button))
2046 gtk_widget_show (sheet->button);
2047 gtk_widget_map (sheet->button);
2050 redraw_range (sheet, NULL);
2051 change_active_cell (sheet,
2052 sheet->active_cell.row,
2053 sheet->active_cell.col);
2058 psppire_sheet_unmap (GtkWidget *widget)
2060 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2062 if (!GTK_WIDGET_MAPPED (widget))
2065 GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
2067 gdk_window_hide (sheet->sheet_window);
2068 if (sheet->column_titles_visible)
2069 gdk_window_hide (sheet->column_title_window);
2070 if (sheet->row_titles_visible)
2071 gdk_window_hide (sheet->row_title_window);
2072 gdk_window_hide (widget->window);
2074 gtk_widget_unmap (sheet->entry_widget);
2075 gtk_widget_unmap (sheet->button);
2076 gtk_widget_unmap (sheet->hover_window->window);
2079 /* get cell attributes of the given cell */
2080 /* TRUE means that the cell is currently allocated */
2081 static gboolean psppire_sheet_get_attributes (const PsppireSheet *sheet,
2083 PsppireSheetCellAttr *attributes);
2088 psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint col)
2090 PangoLayout *layout;
2091 PangoRectangle text;
2092 PangoFontDescription *font_desc = GTK_WIDGET (sheet)->style->font_desc;
2097 PsppireSheetCellAttr attributes;
2100 g_return_if_fail (sheet != NULL);
2102 /* bail now if we aren't yet drawable */
2103 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
2106 row >= psppire_axis_unit_count (sheet->vaxis))
2110 col >= psppire_axis_unit_count (sheet->haxis))
2113 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2115 /* select GC for background rectangle */
2116 gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground);
2117 gdk_gc_set_foreground (sheet->bg_gc, &attributes.background);
2119 rectangle_from_cell (sheet, row, col, &area);
2121 gdk_gc_set_line_attributes (sheet->fg_gc, 1, 0, 0, 0);
2123 if (sheet->show_grid)
2125 gdk_gc_set_foreground (sheet->bg_gc, &sheet->color[GRID_COLOR]);
2127 gdk_draw_rectangle (sheet->sheet_window,
2131 area.width, area.height);
2135 label = psppire_sheet_cell_get_text (sheet, row, col);
2140 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), label);
2141 dispose_string (sheet, label);
2144 pango_layout_set_font_description (layout, font_desc);
2146 pango_layout_get_pixel_extents (layout, NULL, &text);
2148 gdk_gc_set_clip_rectangle (sheet->fg_gc, &area);
2150 font_height = pango_font_description_get_size (font_desc);
2151 if ( !pango_font_description_get_size_is_absolute (font_desc))
2152 font_height /= PANGO_SCALE;
2155 if ( sheet->cell_padding )
2157 area.x += sheet->cell_padding->left;
2158 area.width -= sheet->cell_padding->right
2159 + sheet->cell_padding->left;
2161 area.y += sheet->cell_padding->top;
2162 area.height -= sheet->cell_padding->bottom
2164 sheet->cell_padding->top;
2167 /* Centre the text vertically */
2168 area.y += (area.height - font_height) / 2.0;
2170 switch (attributes.justification)
2172 case GTK_JUSTIFY_RIGHT:
2173 area.x += area.width - text.width;
2175 case GTK_JUSTIFY_CENTER:
2176 area.x += (area.width - text.width) / 2.0;
2178 case GTK_JUSTIFY_LEFT:
2182 g_critical ("Unhandled justification %d in column %d\n",
2183 attributes.justification, col);
2187 gdk_draw_layout (sheet->sheet_window, sheet->fg_gc,
2192 gdk_gc_set_clip_rectangle (sheet->fg_gc, NULL);
2193 g_object_unref (layout);
2198 draw_sheet_region (PsppireSheet *sheet, GdkRegion *region)
2200 PsppireSheetRange range;
2205 PsppireSheetRange drawing_range;
2207 gdk_region_get_clipbox (region, &area);
2209 y = area.y + sheet->vadjustment->value;
2210 x = area.x + sheet->hadjustment->value;
2212 if ( sheet->column_titles_visible)
2213 y -= sheet->column_title_area.height;
2215 if ( sheet->row_titles_visible)
2216 x -= sheet->row_title_area.width;
2218 maximize_int (&x, 0);
2219 maximize_int (&y, 0);
2221 range.row0 = row_from_ypixel (sheet, y);
2222 range.rowi = row_from_ypixel (sheet, y + area.height);
2224 range.col0 = column_from_xpixel (sheet, x);
2225 range.coli = column_from_xpixel (sheet, x + area.width);
2227 g_return_if_fail (sheet != NULL);
2228 g_return_if_fail (PSPPIRE_SHEET (sheet));
2230 if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (sheet))) return;
2231 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2232 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
2235 drawing_range.row0 = MAX (range.row0, min_visible_row (sheet));
2236 drawing_range.col0 = MAX (range.col0, min_visible_column (sheet));
2237 drawing_range.rowi = MIN (range.rowi, max_visible_row (sheet));
2238 drawing_range.coli = MIN (range.coli, max_visible_column (sheet));
2240 g_return_if_fail (drawing_range.rowi >= drawing_range.row0);
2241 g_return_if_fail (drawing_range.coli >= drawing_range.col0);
2243 for (i = drawing_range.row0; i <= drawing_range.rowi; i++)
2245 for (j = drawing_range.col0; j <= drawing_range.coli; j++)
2246 psppire_sheet_cell_draw (sheet, i, j);
2249 if (sheet->select_status == PSPPIRE_SHEET_NORMAL &&
2250 sheet->active_cell.row >= drawing_range.row0 &&
2251 sheet->active_cell.row <= drawing_range.rowi &&
2252 sheet->active_cell.col >= drawing_range.col0 &&
2253 sheet->active_cell.col <= drawing_range.coli)
2254 psppire_sheet_show_entry_widget (sheet);
2258 psppire_sheet_set_cell (PsppireSheet *sheet, gint row, gint col,
2259 GtkJustification justification,
2262 PsppireSheetModel *model ;
2265 g_return_if_fail (sheet != NULL);
2266 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2268 if (col >= psppire_axis_unit_count (sheet->haxis)
2269 || row >= psppire_axis_unit_count (sheet->vaxis))
2272 if (col < 0 || row < 0) return;
2274 model = psppire_sheet_get_model (sheet);
2276 old_text = psppire_sheet_model_get_string (model, row, col);
2278 if (0 != g_strcmp0 (old_text, text))
2280 g_signal_handler_block (sheet->model, sheet->update_handler_id);
2281 psppire_sheet_model_set_string (model, text, row, col);
2282 g_signal_handler_unblock (sheet->model, sheet->update_handler_id);
2285 if ( psppire_sheet_model_free_strings (model))
2291 psppire_sheet_cell_clear (PsppireSheet *sheet, gint row, gint column)
2293 PsppireSheetRange range;
2295 g_return_if_fail (sheet != NULL);
2296 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2297 if (column >= psppire_axis_unit_count (sheet->haxis) ||
2298 row >= psppire_axis_unit_count (sheet->vaxis)) return;
2300 if (column < 0 || row < 0) return;
2304 range.col0 = min_visible_column (sheet);
2305 range.coli = max_visible_column (sheet);
2307 psppire_sheet_real_cell_clear (sheet, row, column);
2309 redraw_range (sheet, &range);
2313 psppire_sheet_real_cell_clear (PsppireSheet *sheet, gint row, gint column)
2315 PsppireSheetModel *model = psppire_sheet_get_model (sheet);
2317 gchar *old_text = psppire_sheet_cell_get_text (sheet, row, column);
2319 if (old_text && strlen (old_text) > 0 )
2321 psppire_sheet_model_datum_clear (model, row, column);
2324 dispose_string (sheet, old_text);
2328 psppire_sheet_cell_get_text (const PsppireSheet *sheet, gint row, gint col)
2330 PsppireSheetModel *model;
2331 g_return_val_if_fail (sheet != NULL, NULL);
2332 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
2334 if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis))
2336 if (col < 0 || row < 0) return NULL;
2338 model = psppire_sheet_get_model (sheet);
2343 return psppire_sheet_model_get_string (model, row, col);
2347 /* Convert X, Y (in pixels) to *ROW, *COLUMN
2348 If the function returns FALSE, then the results will be unreliable.
2351 psppire_sheet_get_pixel_info (PsppireSheet *sheet,
2359 *column = -G_MAXINT;
2361 g_return_val_if_fail (sheet != NULL, 0);
2362 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2364 /* bounds checking, return false if the user clicked
2372 if ( sheet->column_titles_visible)
2373 y -= sheet->column_title_area.height;
2375 y += sheet->vadjustment->value;
2377 if ( y < 0 && sheet->column_titles_visible)
2383 trow = row_from_ypixel (sheet, y);
2384 if (trow > psppire_axis_unit_count (sheet->vaxis))
2390 if ( sheet->row_titles_visible)
2391 x -= sheet->row_title_area.width;
2393 x += sheet->hadjustment->value;
2395 if ( x < 0 && sheet->row_titles_visible)
2401 tcol = column_from_xpixel (sheet, x);
2402 if (tcol > psppire_axis_unit_count (sheet->haxis))
2412 psppire_sheet_get_cell_area (PsppireSheet *sheet,
2417 g_return_val_if_fail (sheet != NULL, 0);
2418 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2420 if (row >= psppire_axis_unit_count (sheet->vaxis) || column >= psppire_axis_unit_count (sheet->haxis))
2423 area->x = (column == -1) ? 0 : psppire_axis_start_pixel (sheet->haxis, column);
2424 area->y = (row == -1) ? 0 : psppire_axis_start_pixel (sheet->vaxis, row);
2426 area->width= (column == -1) ? sheet->row_title_area.width
2427 : psppire_axis_unit_size (sheet->haxis, column);
2429 area->height= (row == -1) ? sheet->column_title_area.height
2430 : psppire_axis_unit_size (sheet->vaxis, row);
2436 psppire_sheet_set_active_cell (PsppireSheet *sheet, gint row, gint col)
2438 g_return_if_fail (sheet != NULL);
2439 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2441 if (row < -1 || col < -1)
2444 if (row >= psppire_axis_unit_count (sheet->vaxis)
2446 col >= psppire_axis_unit_count (sheet->haxis))
2449 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2452 if ( row == -1 || col == -1)
2454 psppire_sheet_hide_entry_widget (sheet);
2458 change_active_cell (sheet, row, col);
2462 psppire_sheet_get_active_cell (PsppireSheet *sheet, gint *row, gint *column)
2464 g_return_if_fail (sheet != NULL);
2465 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2467 if ( row ) *row = sheet->active_cell.row;
2468 if (column) *column = sheet->active_cell.col;
2472 entry_load_text (PsppireSheet *sheet)
2476 GtkJustification justification;
2477 PsppireSheetCellAttr attributes;
2479 if (!GTK_WIDGET_VISIBLE (sheet->entry_widget)) return;
2480 if (sheet->select_status != PSPPIRE_SHEET_NORMAL) return;
2482 row = sheet->active_cell.row;
2483 col = sheet->active_cell.col;
2485 if (row < 0 || col < 0) return;
2487 text = gtk_entry_get_text (psppire_sheet_get_entry (sheet));
2489 if (text && strlen (text) > 0)
2491 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2492 justification = attributes.justification;
2493 psppire_sheet_set_cell (sheet, row, col, justification, text);
2499 psppire_sheet_hide_entry_widget (PsppireSheet *sheet)
2501 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2504 if (sheet->active_cell.row < 0 ||
2505 sheet->active_cell.col < 0) return;
2507 gtk_widget_hide (sheet->entry_widget);
2508 gtk_widget_unmap (sheet->entry_widget);
2510 GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2514 change_active_cell (PsppireSheet *sheet, gint row, gint col)
2516 gint old_row, old_col;
2518 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2520 if (row < 0 || col < 0)
2523 if ( row > psppire_axis_unit_count (sheet->vaxis)
2524 || col > psppire_axis_unit_count (sheet->haxis))
2527 old_row = sheet->active_cell.row;
2528 old_col = sheet->active_cell.col;
2530 entry_load_text (sheet);
2532 /* Erase the old cell border */
2533 psppire_sheet_draw_active_cell (sheet);
2535 sheet->active_cell.row = row;
2536 sheet->active_cell.col = col;
2538 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
2540 GTK_WIDGET_UNSET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2542 psppire_sheet_draw_active_cell (sheet);
2543 psppire_sheet_show_entry_widget (sheet);
2545 GTK_WIDGET_SET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2547 g_signal_emit (sheet, sheet_signals [ACTIVATE], 0,
2548 row, col, old_row, old_col);
2553 psppire_sheet_show_entry_widget (PsppireSheet *sheet)
2555 GtkEntry *sheet_entry;
2556 PsppireSheetCellAttr attributes;
2560 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2562 row = sheet->active_cell.row;
2563 col = sheet->active_cell.col;
2565 /* Don't show the active cell, if there is no active cell: */
2566 if (! (row >= 0 && col >= 0)) /* e.g row or coll == -1. */
2569 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2570 if (sheet->select_status != PSPPIRE_SHEET_NORMAL) return;
2571 if (PSPPIRE_SHEET_IN_SELECTION (sheet)) return;
2573 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2575 sheet_entry = psppire_sheet_get_entry (sheet);
2577 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2579 if (GTK_IS_ENTRY (sheet_entry))
2581 gchar *text = psppire_sheet_cell_get_text (sheet, row, col);
2582 const gchar *old_text = gtk_entry_get_text (GTK_ENTRY (sheet_entry));
2585 text = g_strdup ("");
2587 if (strcmp (old_text, text) != 0)
2588 gtk_entry_set_text (sheet_entry, text);
2590 dispose_string (sheet, text);
2593 switch (attributes.justification)
2595 case GTK_JUSTIFY_RIGHT:
2596 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 1.0);
2598 case GTK_JUSTIFY_CENTER:
2599 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.5);
2601 case GTK_JUSTIFY_LEFT:
2603 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.0);
2609 psppire_sheet_size_allocate_entry (sheet);
2611 gtk_widget_set_sensitive (GTK_WIDGET (sheet_entry),
2612 psppire_sheet_model_is_editable (sheet->model,
2614 gtk_widget_map (sheet->entry_widget);
2618 psppire_sheet_draw_active_cell (PsppireSheet *sheet)
2621 PsppireSheetRange range;
2623 row = sheet->active_cell.row;
2624 col = sheet->active_cell.col;
2626 if (row < 0 || col < 0) return FALSE;
2628 if (!psppire_sheet_cell_isvisible (sheet, row, col))
2631 range.col0 = range.coli = col;
2632 range.row0 = range.rowi = row;
2634 psppire_sheet_draw_border (sheet, range);
2642 psppire_sheet_draw_border (PsppireSheet *sheet, PsppireSheetRange new_range)
2646 rectangle_from_range (sheet, &new_range, &area);
2651 gdk_gc_set_clip_rectangle (sheet->xor_gc, &area);
2653 area.x += sheet->cell_padding->left / 2;
2654 area.y += sheet->cell_padding->top / 2;
2655 area.width -= (sheet->cell_padding->left + sheet->cell_padding->right ) / 2;
2656 area.height -= (sheet->cell_padding->top + sheet->cell_padding->bottom ) / 2;
2658 gdk_draw_rectangle (sheet->sheet_window, sheet->xor_gc,
2665 gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
2670 /* Selection related functions */
2673 psppire_sheet_select_row (PsppireSheet *sheet, gint row)
2676 sheet->select_status = PSPPIRE_SHEET_ROW_SELECTED;
2678 sheet->range.col0 = sheet->range.coli = -1;
2679 sheet->range.row0 = sheet->range.rowi = row;
2681 rectangle_from_range (sheet, &sheet->range, &area);
2685 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
2687 g_signal_emit (sheet, sheet_signals [SELECT_ROW], 0, row);
2691 psppire_sheet_select_column (PsppireSheet *sheet, gint column)
2694 sheet->select_status = PSPPIRE_SHEET_COLUMN_SELECTED;
2696 sheet->range.col0 = sheet->range.coli = column;
2697 sheet->range.row0 = sheet->range.rowi = -1;
2699 rectangle_from_range (sheet, &sheet->range, &area);
2703 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
2705 g_signal_emit (sheet, sheet_signals [SELECT_COLUMN], 0, column);
2710 psppire_sheet_select_range (PsppireSheet *sheet, const PsppireSheetRange *range)
2713 sheet->select_status = PSPPIRE_SHEET_RANGE_SELECTED;
2715 sheet->range = *range;
2717 rectangle_from_range (sheet, range, &area);
2720 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
2725 psppire_sheet_unselect_range (PsppireSheet *sheet)
2728 sheet->select_status = PSPPIRE_SHEET_NORMAL;
2730 rectangle_from_range (sheet, &sheet->range, &area);
2733 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
2735 g_signal_emit (sheet, sheet_signals [SELECT_COLUMN], 0, -1);
2736 g_signal_emit (sheet, sheet_signals [SELECT_ROW], 0, -1);
2740 psppire_sheet_get_selected_range (PsppireSheet *sheet, PsppireSheetRange *range)
2742 g_return_if_fail (sheet != NULL);
2743 *range = sheet->range;
2748 psppire_sheet_expose (GtkWidget *widget, GdkEventExpose *event)
2750 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2752 g_return_val_if_fail (event != NULL, FALSE);
2754 if (!GTK_WIDGET_DRAWABLE (widget))
2757 /* exposure events on the sheet */
2758 if (event->window == sheet->row_title_window &&
2759 sheet->row_titles_visible)
2761 draw_row_title_buttons_range (sheet,
2762 min_visible_row (sheet),
2763 max_visible_row (sheet));
2766 if (event->window == sheet->column_title_window &&
2767 sheet->column_titles_visible)
2769 draw_column_title_buttons_range (sheet,
2770 min_visible_column (sheet),
2771 max_visible_column (sheet));
2774 if (event->window == sheet->sheet_window)
2776 draw_sheet_region (sheet, event->region);
2778 if (sheet->select_status != PSPPIRE_SHEET_NORMAL)
2781 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
2782 psppire_sheet_range_draw (sheet, &sheet->range);
2784 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
2785 psppire_sheet_range_draw (sheet, &sheet->drag_range);
2791 rectangle_from_range (sheet, &sheet->range, &area);
2793 gdk_draw_rectangle (sheet->sheet_window,
2796 area.x + 1, area.y + 1,
2797 area.width, area.height);
2801 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
2802 draw_xor_rectangle (sheet, sheet->drag_range);
2807 if ((!PSPPIRE_SHEET_IN_XDRAG (sheet)) && (!PSPPIRE_SHEET_IN_YDRAG (sheet)))
2810 PsppireSheetRange range;
2811 range.row0 = range.rowi = sheet->active_cell.row;
2812 range.col0 = range.coli = sheet->active_cell.col;
2814 rectangle_from_range (sheet, &range, &rect);
2816 if (GDK_OVERLAP_RECTANGLE_OUT !=
2817 gdk_region_rect_in (event->region, &rect))
2819 psppire_sheet_draw_active_cell (sheet);
2825 (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
2832 psppire_sheet_button_press (GtkWidget *widget, GdkEventButton *event)
2834 PsppireSheet *sheet;
2835 GdkModifierType mods;
2840 g_return_val_if_fail (widget != NULL, FALSE);
2841 g_return_val_if_fail (PSPPIRE_IS_SHEET (widget), FALSE);
2842 g_return_val_if_fail (event != NULL, FALSE);
2844 sheet = PSPPIRE_SHEET (widget);
2846 /* Cancel any pending tooltips */
2847 if (sheet->motion_timer)
2849 g_source_remove (sheet->motion_timer);
2850 sheet->motion_timer = 0;
2853 gtk_widget_get_pointer (widget, &x, &y);
2854 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
2857 if (event->window == sheet->column_title_window)
2859 sheet->x_drag = event->x;
2860 g_signal_emit (sheet,
2861 sheet_signals[BUTTON_EVENT_COLUMN], 0,
2864 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
2866 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
2867 g_signal_emit (sheet,
2868 sheet_signals[DOUBLE_CLICK_COLUMN], 0, column);
2872 if (event->window == sheet->row_title_window)
2874 g_signal_emit (sheet,
2875 sheet_signals[BUTTON_EVENT_ROW], 0,
2878 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
2880 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
2881 g_signal_emit (sheet,
2882 sheet_signals[DOUBLE_CLICK_ROW], 0, row);
2886 gdk_window_get_pointer (widget->window, NULL, NULL, &mods);
2888 if (! (mods & GDK_BUTTON1_MASK)) return TRUE;
2891 /* press on resize windows */
2892 if (event->window == sheet->column_title_window)
2894 sheet->x_drag = event->x;
2896 if (on_column_boundary (sheet, sheet->x_drag, &sheet->drag_cell.col))
2898 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
2899 gdk_pointer_grab (sheet->column_title_window, FALSE,
2900 GDK_POINTER_MOTION_HINT_MASK |
2901 GDK_BUTTON1_MOTION_MASK |
2902 GDK_BUTTON_RELEASE_MASK,
2903 NULL, NULL, event->time);
2905 draw_xor_vline (sheet);
2910 if (event->window == sheet->row_title_window)
2912 sheet->y_drag = event->y;
2914 if (on_row_boundary (sheet, sheet->y_drag, &sheet->drag_cell.row))
2916 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
2917 gdk_pointer_grab (sheet->row_title_window, FALSE,
2918 GDK_POINTER_MOTION_HINT_MASK |
2919 GDK_BUTTON1_MOTION_MASK |
2920 GDK_BUTTON_RELEASE_MASK,
2921 NULL, NULL, event->time);
2923 draw_xor_hline (sheet);
2928 /* the sheet itself does not handle other than single click events */
2929 if (event->type != GDK_BUTTON_PRESS) return FALSE;
2931 /* selections on the sheet */
2932 if (event->window == sheet->sheet_window)
2934 gtk_widget_get_pointer (widget, &x, &y);
2935 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
2936 gdk_pointer_grab (sheet->sheet_window, FALSE,
2937 GDK_POINTER_MOTION_HINT_MASK |
2938 GDK_BUTTON1_MOTION_MASK |
2939 GDK_BUTTON_RELEASE_MASK,
2940 NULL, NULL, event->time);
2941 gtk_grab_add (GTK_WIDGET (sheet));
2943 if ( sheet->select_status == PSPPIRE_SHEET_NORMAL)
2945 sheet->range.row0 = row;
2946 sheet->range.col0 = column;
2950 psppire_sheet_unselect_range (sheet);
2952 psppire_sheet_click_cell (sheet, row, column);
2955 if (event->window == sheet->column_title_window)
2957 gtk_widget_get_pointer (widget, &x, &y);
2958 if ( sheet->row_titles_visible)
2959 x -= sheet->row_title_area.width;
2961 x += sheet->hadjustment->value;
2963 column = column_from_xpixel (sheet, x);
2965 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
2967 gtk_grab_add (GTK_WIDGET (sheet));
2968 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
2972 if (event->window == sheet->row_title_window)
2974 gtk_widget_get_pointer (widget, &x, &y);
2975 if ( sheet->column_titles_visible)
2976 y -= sheet->column_title_area.height;
2978 y += sheet->vadjustment->value;
2980 row = row_from_ypixel (sheet, y);
2981 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
2983 gtk_grab_add (GTK_WIDGET (sheet));
2984 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
2992 psppire_sheet_click_cell (PsppireSheet *sheet, gint row, gint column)
2994 PsppireSheetCell cell;
2995 gboolean forbid_move;
3000 if (row >= psppire_axis_unit_count (sheet->vaxis)
3001 || column >= psppire_axis_unit_count (sheet->haxis))
3006 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
3007 &sheet->active_cell,
3013 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
3016 row = sheet->active_cell.row;
3017 column = sheet->active_cell.col;
3019 change_active_cell (sheet, row, column);
3023 if (row == -1 && column >= 0)
3025 psppire_sheet_select_column (sheet, column);
3029 if (column == -1 && row >= 0)
3031 psppire_sheet_select_row (sheet, row);
3035 if (row == -1 && column == -1)
3037 sheet->range.row0 = 0;
3038 sheet->range.col0 = 0;
3039 sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
3041 psppire_axis_unit_count (sheet->haxis) - 1;
3042 psppire_sheet_select_range (sheet, NULL);
3046 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
3047 change_active_cell (sheet, row, column);
3049 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
3055 psppire_sheet_button_release (GtkWidget *widget,
3056 GdkEventButton *event)
3058 GdkDisplay *display = gtk_widget_get_display (widget);
3060 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3062 /* release on resize windows */
3063 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3066 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
3067 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3069 gdk_display_pointer_ungrab (display, event->time);
3070 draw_xor_vline (sheet);
3073 psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)
3074 + sheet->hadjustment->value;
3076 set_column_width (sheet, sheet->drag_cell.col, width);
3081 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3084 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
3085 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3087 gdk_display_pointer_ungrab (display, event->time);
3088 draw_xor_hline (sheet);
3091 psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row) +
3092 sheet->vadjustment->value;
3094 set_row_height (sheet, sheet->drag_cell.row, height);
3099 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3101 PsppireSheetRange old_range;
3102 draw_xor_rectangle (sheet, sheet->drag_range);
3103 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_DRAG);
3104 gdk_display_pointer_ungrab (display, event->time);
3106 psppire_sheet_unselect_range (sheet);
3108 old_range = sheet->range;
3109 sheet->range = sheet->drag_range;
3110 sheet->drag_range = old_range;
3111 g_signal_emit (sheet, sheet_signals[MOVE_RANGE], 0,
3112 &sheet->drag_range, &sheet->range);
3113 psppire_sheet_select_range (sheet, &sheet->range);
3116 if (PSPPIRE_SHEET_IN_SELECTION (sheet))
3118 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3119 sheet->select_status = PSPPIRE_SHEET_RANGE_SELECTED;
3121 change_active_cell (sheet, sheet->active_cell.row,
3122 sheet->active_cell.col);
3125 gdk_display_pointer_ungrab (display, event->time);
3126 gtk_grab_remove (GTK_WIDGET (sheet));
3128 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3137 /* Shamelessly lifted from gtktooltips */
3139 psppire_sheet_subtitle_paint_window (GtkWidget *tip_window)
3143 gtk_widget_size_request (tip_window, &req);
3144 gtk_paint_flat_box (tip_window->style, tip_window->window,
3145 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
3146 NULL, GTK_WIDGET(tip_window), "tooltip",
3147 0, 0, req.width, req.height);
3153 destroy_hover_window (PsppireSheetHoverTitle *h)
3155 gtk_widget_destroy (h->window);
3159 static PsppireSheetHoverTitle *
3160 create_hover_window (void)
3162 PsppireSheetHoverTitle *hw = g_malloc (sizeof (*hw));
3164 hw->window = gtk_window_new (GTK_WINDOW_POPUP);
3166 #if GTK_CHECK_VERSION (2, 9, 0)
3167 gtk_window_set_type_hint (GTK_WINDOW (hw->window),
3168 GDK_WINDOW_TYPE_HINT_TOOLTIP);
3171 gtk_widget_set_app_paintable (hw->window, TRUE);
3172 gtk_window_set_resizable (GTK_WINDOW (hw->window), FALSE);
3173 gtk_widget_set_name (hw->window, "gtk-tooltips");
3174 gtk_container_set_border_width (GTK_CONTAINER (hw->window), 4);
3176 g_signal_connect (hw->window,
3178 G_CALLBACK (psppire_sheet_subtitle_paint_window),
3181 hw->label = gtk_label_new (NULL);
3184 gtk_label_set_line_wrap (GTK_LABEL (hw->label), TRUE);
3185 gtk_misc_set_alignment (GTK_MISC (hw->label), 0.5, 0.5);
3187 gtk_container_add (GTK_CONTAINER (hw->window), hw->label);
3189 gtk_widget_show (hw->label);
3191 g_signal_connect (hw->window,
3193 G_CALLBACK (gtk_widget_destroyed),
3199 #define HOVER_WINDOW_Y_OFFSET 2
3202 show_subtitle (PsppireSheet *sheet, gint row, gint column,
3203 const gchar *subtitle)
3212 gtk_label_set_text (GTK_LABEL (sheet->hover_window->label),
3216 sheet->hover_window->row = row;
3217 sheet->hover_window->column = column;
3219 gdk_window_get_origin (GTK_WIDGET (sheet)->window, &x, &y);
3221 gtk_widget_get_pointer (GTK_WIDGET (sheet), &px, &py);
3223 gtk_widget_show (sheet->hover_window->window);
3225 width = GTK_WIDGET (sheet->hover_window->label)->allocation.width;
3231 y += sheet->column_title_area.y;
3232 y += sheet->column_title_area.height;
3233 y += HOVER_WINDOW_Y_OFFSET;
3239 x += sheet->row_title_area.x;
3240 x += sheet->row_title_area.width * 2 / 3.0;
3243 gtk_window_move (GTK_WINDOW (sheet->hover_window->window),
3248 motion_timeout_callback (gpointer data)
3250 PsppireSheet *sheet = PSPPIRE_SHEET (data);
3254 gdk_threads_enter ();
3255 gtk_widget_get_pointer (GTK_WIDGET (sheet), &x, &y);
3257 if ( psppire_sheet_get_pixel_info (sheet, x, y, &row, &column) )
3259 if (sheet->row_title_under && row >= 0)
3261 gchar *text = psppire_sheet_model_get_row_subtitle (sheet->model, row);
3263 show_subtitle (sheet, row, -1, text);
3267 if (sheet->column_title_under && column >= 0)
3269 gchar *text = psppire_sheet_model_get_column_subtitle (sheet->model,
3272 show_subtitle (sheet, -1, column, text);
3278 gdk_threads_leave ();
3283 psppire_sheet_motion (GtkWidget *widget, GdkEventMotion *event)
3285 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3286 GdkModifierType mods;
3287 GdkCursorType new_cursor;
3290 GdkDisplay *display;
3292 g_return_val_if_fail (event != NULL, FALSE);
3294 display = gtk_widget_get_display (widget);
3296 /* selections on the sheet */
3300 if (!GTK_WIDGET_VISIBLE (sheet->hover_window->window))
3302 if ( sheet->motion_timer > 0 )
3303 g_source_remove (sheet->motion_timer);
3304 sheet->motion_timer =
3305 g_timeout_add (TIMEOUT_HOVER, motion_timeout_callback, sheet);
3311 gtk_widget_get_pointer (widget, &wx, &wy);
3313 if ( psppire_sheet_get_pixel_info (sheet, wx, wy, &row, &column) )
3315 if ( row != sheet->hover_window->row ||
3316 column != sheet->hover_window->column)
3318 gtk_widget_hide (sheet->hover_window->window);
3323 if (event->window == sheet->column_title_window)
3325 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3326 on_column_boundary (sheet, x, &column))
3328 new_cursor = GDK_SB_H_DOUBLE_ARROW;
3329 if (new_cursor != sheet->cursor_drag->type)
3331 gdk_cursor_unref (sheet->cursor_drag);
3332 sheet->cursor_drag =
3333 gdk_cursor_new_for_display (display, new_cursor);
3335 gdk_window_set_cursor (sheet->column_title_window,
3336 sheet->cursor_drag);
3341 new_cursor = GDK_TOP_LEFT_ARROW;
3342 if (!PSPPIRE_SHEET_IN_XDRAG (sheet) &&
3343 new_cursor != sheet->cursor_drag->type)
3345 gdk_cursor_unref (sheet->cursor_drag);
3346 sheet->cursor_drag =
3347 gdk_cursor_new_for_display (display, new_cursor);
3348 gdk_window_set_cursor (sheet->column_title_window,
3349 sheet->cursor_drag);
3353 else if (event->window == sheet->row_title_window)
3355 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3356 on_row_boundary (sheet, y, &row))
3358 new_cursor = GDK_SB_V_DOUBLE_ARROW;
3359 if (new_cursor != sheet->cursor_drag->type)
3361 gdk_cursor_unref (sheet->cursor_drag);
3362 sheet->cursor_drag =
3363 gdk_cursor_new_for_display (display, new_cursor);
3364 gdk_window_set_cursor (sheet->row_title_window,
3365 sheet->cursor_drag);
3370 new_cursor = GDK_TOP_LEFT_ARROW;
3371 if (!PSPPIRE_SHEET_IN_YDRAG (sheet) &&
3372 new_cursor != sheet->cursor_drag->type)
3374 gdk_cursor_unref (sheet->cursor_drag);
3375 sheet->cursor_drag =
3376 gdk_cursor_new_for_display (display, new_cursor);
3377 gdk_window_set_cursor (sheet->row_title_window,
3378 sheet->cursor_drag);
3383 new_cursor = GDK_PLUS;
3384 if ( event->window == sheet->sheet_window &&
3385 !POSSIBLE_DRAG (sheet, x, y, &row, &column) &&
3386 !PSPPIRE_SHEET_IN_DRAG (sheet) &&
3387 !POSSIBLE_RESIZE (sheet, x, y, &row, &column) &&
3388 new_cursor != sheet->cursor_drag->type)
3390 gdk_cursor_unref (sheet->cursor_drag);
3391 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
3392 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3395 new_cursor = GDK_TOP_LEFT_ARROW;
3396 if ( event->window == sheet->sheet_window &&
3397 ! (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ) &&
3398 (POSSIBLE_DRAG (sheet, x, y, &row, &column) ||
3399 PSPPIRE_SHEET_IN_DRAG (sheet)) &&
3400 new_cursor != sheet->cursor_drag->type)
3402 gdk_cursor_unref (sheet->cursor_drag);
3403 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
3404 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3407 gdk_window_get_pointer (widget->window, &x, &y, &mods);
3408 if (! (mods & GDK_BUTTON1_MASK)) return FALSE;
3410 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3412 if (event->x != sheet->x_drag)
3414 draw_xor_vline (sheet);
3415 sheet->x_drag = event->x;
3416 draw_xor_vline (sheet);
3422 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3424 if (event->y != sheet->y_drag)
3426 draw_xor_hline (sheet);
3427 sheet->y_drag = event->y;
3428 draw_xor_hline (sheet);
3434 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3436 PsppireSheetRange aux;
3437 column = column_from_xpixel (sheet, x)- sheet->drag_cell.col;
3438 row = row_from_ypixel (sheet, y) - sheet->drag_cell.row;
3439 if (sheet->select_status == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
3440 if (sheet->select_status == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
3444 if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
3445 aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
3447 aux = sheet->drag_range;
3448 sheet->drag_range.row0 = sheet->range.row0 + row;
3449 sheet->drag_range.col0 = sheet->range.col0 + column;
3450 sheet->drag_range.rowi = sheet->range.rowi + row;
3451 sheet->drag_range.coli = sheet->range.coli + column;
3452 if (aux.row0 != sheet->drag_range.row0 ||
3453 aux.col0 != sheet->drag_range.col0)
3455 draw_xor_rectangle (sheet, aux);
3456 draw_xor_rectangle (sheet, sheet->drag_range);
3462 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
3464 if (sheet->select_status == PSPPIRE_SHEET_NORMAL && row == sheet->active_cell.row &&
3465 column == sheet->active_cell.col) return TRUE;
3467 if ( mods & GDK_BUTTON1_MASK)
3469 if (PSPPIRE_SHEET_IN_SELECTION (sheet) )
3471 /* Redraw the old range */
3472 psppire_sheet_unselect_range (sheet);
3474 sheet->range.rowi = row;
3475 sheet->range.coli = column;
3477 /* Redraw the new range */
3478 psppire_sheet_select_range (sheet, &sheet->range);
3482 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3490 psppire_sheet_crossing_notify (GtkWidget *widget,
3491 GdkEventCrossing *event)
3493 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3495 if (event->window == sheet->column_title_window)
3496 sheet->column_title_under = event->type == GDK_ENTER_NOTIFY;
3497 else if (event->window == sheet->row_title_window)
3498 sheet->row_title_under = event->type == GDK_ENTER_NOTIFY;
3500 if (event->type == GDK_LEAVE_NOTIFY)
3501 gtk_widget_hide (sheet->hover_window->window);
3508 psppire_sheet_focus_in (GtkWidget *w,
3509 GdkEventFocus *event)
3511 PsppireSheet *sheet = PSPPIRE_SHEET (w);
3513 gtk_widget_grab_focus (sheet->entry_widget);
3521 psppire_sheet_entry_key_press (GtkWidget *widget,
3525 g_signal_emit_by_name (widget, "key_press_event", key, &focus);
3530 /* Number of rows in a step-increment */
3531 #define ROWS_PER_STEP 1
3535 page_vertical (PsppireSheet *sheet, GtkScrollType dir)
3537 gint old_row = sheet->active_cell.row ;
3538 glong vpixel = psppire_axis_start_pixel (sheet->vaxis, old_row);
3542 vpixel -= psppire_axis_start_pixel (sheet->vaxis,
3543 min_visible_row (sheet));
3547 case GTK_SCROLL_PAGE_DOWN:
3548 gtk_adjustment_set_value (sheet->vadjustment,
3549 sheet->vadjustment->value +
3550 sheet->vadjustment->page_increment);
3552 case GTK_SCROLL_PAGE_UP:
3553 gtk_adjustment_set_value (sheet->vadjustment,
3554 sheet->vadjustment->value -
3555 sheet->vadjustment->page_increment);
3559 g_assert_not_reached ();
3564 vpixel += psppire_axis_start_pixel (sheet->vaxis,
3565 min_visible_row (sheet));
3567 new_row = row_from_ypixel (sheet, vpixel);
3569 change_active_cell (sheet, new_row,
3570 sheet->active_cell.col);
3575 step_sheet (PsppireSheet *sheet, GtkScrollType dir)
3577 gint current_row = sheet->active_cell.row;
3578 gint current_col = sheet->active_cell.col;
3579 PsppireSheetCell new_cell ;
3580 gboolean forbidden = FALSE;
3582 new_cell.row = current_row;
3583 new_cell.col = current_col;
3587 case GTK_SCROLL_STEP_DOWN:
3590 case GTK_SCROLL_STEP_UP:
3593 case GTK_SCROLL_STEP_RIGHT:
3596 case GTK_SCROLL_STEP_LEFT:
3599 case GTK_SCROLL_STEP_FORWARD:
3602 psppire_sheet_model_get_column_count (sheet->model))
3608 case GTK_SCROLL_STEP_BACKWARD:
3610 if (new_cell.col < 0)
3613 psppire_sheet_model_get_column_count (sheet->model) - 1;
3618 g_assert_not_reached ();
3622 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
3623 &sheet->active_cell,
3631 maximize_int (&new_cell.row, 0);
3632 maximize_int (&new_cell.col, 0);
3634 minimize_int (&new_cell.row,
3635 psppire_axis_unit_count (sheet->vaxis) - 1);
3637 minimize_int (&new_cell.col,
3638 psppire_axis_unit_count (sheet->haxis) - 1);
3640 change_active_cell (sheet, new_cell.row, new_cell.col);
3643 if ( new_cell.col > max_fully_visible_column (sheet))
3646 psppire_axis_start_pixel (sheet->haxis,
3648 hpos -= sheet->hadjustment->page_size;
3650 gtk_adjustment_set_value (sheet->hadjustment,
3653 else if ( new_cell.col < min_fully_visible_column (sheet))
3656 psppire_axis_start_pixel (sheet->haxis,
3659 gtk_adjustment_set_value (sheet->hadjustment,
3664 if ( new_cell.row > max_fully_visible_row (sheet))
3667 psppire_axis_start_pixel (sheet->vaxis,
3669 vpos -= sheet->vadjustment->page_size;
3671 gtk_adjustment_set_value (sheet->vadjustment,
3674 else if ( new_cell.row < min_fully_visible_row (sheet))
3677 psppire_axis_start_pixel (sheet->vaxis,
3680 gtk_adjustment_set_value (sheet->vadjustment,
3684 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
3689 psppire_sheet_key_press (GtkWidget *widget,
3692 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3694 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3696 switch (key->keyval)
3699 step_sheet (sheet, GTK_SCROLL_STEP_FORWARD);
3702 step_sheet (sheet, GTK_SCROLL_STEP_RIGHT);
3704 case GDK_ISO_Left_Tab:
3705 step_sheet (sheet, GTK_SCROLL_STEP_BACKWARD);
3708 step_sheet (sheet, GTK_SCROLL_STEP_LEFT);
3712 step_sheet (sheet, GTK_SCROLL_STEP_DOWN);
3715 step_sheet (sheet, GTK_SCROLL_STEP_UP);
3719 page_vertical (sheet, GTK_SCROLL_PAGE_DOWN);
3722 page_vertical (sheet, GTK_SCROLL_PAGE_UP);
3726 gtk_adjustment_set_value (sheet->vadjustment,
3727 sheet->vadjustment->lower);
3729 change_active_cell (sheet, 0,
3730 sheet->active_cell.col);
3735 gtk_adjustment_set_value (sheet->vadjustment,
3736 sheet->vadjustment->upper -
3737 sheet->vadjustment->page_size -
3738 sheet->vadjustment->page_increment);
3741 change_active_cellx (sheet,
3742 psppire_axis_unit_count (sheet->vaxis) - 1,
3743 sheet->active_cell.col);
3747 psppire_sheet_real_cell_clear (sheet, sheet->active_cell.row, sheet->active_cell.col);
3758 psppire_sheet_size_request (GtkWidget *widget,
3759 GtkRequisition *requisition)
3761 PsppireSheet *sheet;
3763 g_return_if_fail (widget != NULL);
3764 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
3765 g_return_if_fail (requisition != NULL);
3767 sheet = PSPPIRE_SHEET (widget);
3769 requisition->width = 3 * DEFAULT_COLUMN_WIDTH;
3770 requisition->height = 3 * DEFAULT_ROW_HEIGHT;
3772 /* compute the size of the column title area */
3773 if (sheet->column_titles_visible)
3774 requisition->height += sheet->column_title_area.height;
3776 /* compute the size of the row title area */
3777 if (sheet->row_titles_visible)
3778 requisition->width += sheet->row_title_area.width;
3783 psppire_sheet_size_allocate (GtkWidget *widget,
3784 GtkAllocation *allocation)
3786 PsppireSheet *sheet;
3787 GtkAllocation sheet_allocation;
3790 g_return_if_fail (widget != NULL);
3791 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
3792 g_return_if_fail (allocation != NULL);
3794 sheet = PSPPIRE_SHEET (widget);
3795 widget->allocation = *allocation;
3796 border_width = GTK_CONTAINER (widget)->border_width;
3798 if (GTK_WIDGET_REALIZED (widget))
3799 gdk_window_move_resize (widget->window,
3800 allocation->x + border_width,
3801 allocation->y + border_width,
3802 allocation->width - 2 * border_width,
3803 allocation->height - 2 * border_width);
3805 sheet_allocation.x = 0;
3806 sheet_allocation.y = 0;
3807 sheet_allocation.width = allocation->width - 2 * border_width;
3808 sheet_allocation.height = allocation->height - 2 * border_width;
3810 if (GTK_WIDGET_REALIZED (widget))
3811 gdk_window_move_resize (sheet->sheet_window,
3814 sheet_allocation.width,
3815 sheet_allocation.height);
3817 /* position the window which holds the column title buttons */
3818 sheet->column_title_area.x = 0;
3819 sheet->column_title_area.y = 0;
3820 sheet->column_title_area.width = sheet_allocation.width ;
3823 /* position the window which holds the row title buttons */
3824 sheet->row_title_area.x = 0;
3825 sheet->row_title_area.y = 0;
3826 sheet->row_title_area.height = sheet_allocation.height;
3828 if (sheet->row_titles_visible)
3829 sheet->column_title_area.x += sheet->row_title_area.width;
3831 if (sheet->column_titles_visible)
3832 sheet->row_title_area.y += sheet->column_title_area.height;
3834 if (GTK_WIDGET_REALIZED (widget) && sheet->column_titles_visible)
3835 gdk_window_move_resize (sheet->column_title_window,
3836 sheet->column_title_area.x,
3837 sheet->column_title_area.y,
3838 sheet->column_title_area.width,
3839 sheet->column_title_area.height);
3842 if (GTK_WIDGET_REALIZED (widget) && sheet->row_titles_visible)
3843 gdk_window_move_resize (sheet->row_title_window,
3844 sheet->row_title_area.x,
3845 sheet->row_title_area.y,
3846 sheet->row_title_area.width,
3847 sheet->row_title_area.height);
3849 size_allocate_global_button (sheet);
3853 gint width = sheet->column_title_area.width;
3855 if ( sheet->row_titles_visible)
3856 width -= sheet->row_title_area.width;
3858 g_object_set (sheet->haxis,
3859 "minimum-extent", width,
3866 gint height = sheet->row_title_area.height;
3868 if ( sheet->column_titles_visible)
3869 height -= sheet->column_title_area.height;
3871 g_object_set (sheet->vaxis,
3872 "minimum-extent", height,
3877 /* set the scrollbars adjustments */
3878 adjust_scrollbars (sheet);
3882 draw_column_title_buttons (PsppireSheet *sheet)
3886 if (!sheet->column_titles_visible) return;
3887 if (!GTK_WIDGET_REALIZED (sheet))
3890 gdk_drawable_get_size (sheet->sheet_window, &width, NULL);
3893 if (sheet->row_titles_visible)
3895 x = sheet->row_title_area.width;
3898 if (sheet->column_title_area.width != width || sheet->column_title_area.x != x)
3900 sheet->column_title_area.width = width;
3901 sheet->column_title_area.x = x;
3902 gdk_window_move_resize (sheet->column_title_window,
3903 sheet->column_title_area.x,
3904 sheet->column_title_area.y,
3905 sheet->column_title_area.width,
3906 sheet->column_title_area.height);
3909 if (max_visible_column (sheet) ==
3910 psppire_axis_unit_count (sheet->haxis) - 1)
3911 gdk_window_clear_area (sheet->column_title_window,
3913 sheet->column_title_area.width,
3914 sheet->column_title_area.height);
3916 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
3918 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
3919 max_visible_column (sheet));
3923 draw_row_title_buttons (PsppireSheet *sheet)
3928 if (!sheet->row_titles_visible) return;
3929 if (!GTK_WIDGET_REALIZED (sheet))
3932 gdk_drawable_get_size (sheet->sheet_window, NULL, &height);
3934 if (sheet->column_titles_visible)
3936 y = sheet->column_title_area.height;
3939 if (sheet->row_title_area.height != height || sheet->row_title_area.y != y)
3941 sheet->row_title_area.y = y;
3942 sheet->row_title_area.height = height;
3943 gdk_window_move_resize (sheet->row_title_window,
3944 sheet->row_title_area.x,
3945 sheet->row_title_area.y,
3946 sheet->row_title_area.width,
3947 sheet->row_title_area.height);
3950 if (max_visible_row (sheet) == psppire_axis_unit_count (sheet->vaxis) - 1)
3951 gdk_window_clear_area (sheet->row_title_window,
3953 sheet->row_title_area.width,
3954 sheet->row_title_area.height);
3956 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
3958 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
3959 max_visible_row (sheet));
3964 psppire_sheet_size_allocate_entry (PsppireSheet *sheet)
3966 GtkAllocation entry_alloc;
3967 PsppireSheetCellAttr attributes = { 0 };
3968 GtkEntry *sheet_entry;
3970 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
3971 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
3973 sheet_entry = psppire_sheet_get_entry (sheet);
3975 if ( ! psppire_sheet_get_attributes (sheet, sheet->active_cell.row,
3976 sheet->active_cell.col,
3980 if ( GTK_WIDGET_REALIZED (sheet->entry_widget) )
3982 GtkStyle *style = GTK_WIDGET (sheet_entry)->style;
3984 style->bg[GTK_STATE_NORMAL] = attributes.background;
3985 style->fg[GTK_STATE_NORMAL] = attributes.foreground;
3986 style->text[GTK_STATE_NORMAL] = attributes.foreground;
3987 style->bg[GTK_STATE_ACTIVE] = attributes.background;
3988 style->fg[GTK_STATE_ACTIVE] = attributes.foreground;
3989 style->text[GTK_STATE_ACTIVE] = attributes.foreground;
3992 rectangle_from_cell (sheet, sheet->active_cell.row,
3993 sheet->active_cell.col, &entry_alloc);
3995 entry_alloc.x += sheet->cell_padding->left;
3996 entry_alloc.y += sheet->cell_padding->right;
3997 entry_alloc.width -= sheet->cell_padding->left + sheet->cell_padding->right;
3998 entry_alloc.height -= sheet->cell_padding->top + sheet->cell_padding->bottom;
4001 gtk_widget_set_size_request (sheet->entry_widget, entry_alloc.width,
4002 entry_alloc.height);
4003 gtk_widget_size_allocate (sheet->entry_widget, &entry_alloc);
4007 /* Copy the sheet's font to the entry widget */
4009 set_entry_widget_font (PsppireSheet *sheet)
4011 GtkRcStyle *style = gtk_widget_get_modifier_style (sheet->entry_widget);
4013 pango_font_description_free (style->font_desc);
4014 style->font_desc = pango_font_description_copy (GTK_WIDGET (sheet)->style->font_desc);
4016 gtk_widget_modify_style (sheet->entry_widget, style);
4020 create_sheet_entry (PsppireSheet *sheet)
4022 if (sheet->entry_widget)
4024 gtk_widget_unparent (sheet->entry_widget);
4027 sheet->entry_widget = g_object_new (sheet->entry_type, NULL);
4028 g_object_ref_sink (sheet->entry_widget);
4030 gtk_widget_size_request (sheet->entry_widget, NULL);
4032 if ( GTK_IS_ENTRY (sheet->entry_widget))
4034 g_object_set (sheet->entry_widget,
4039 if (GTK_WIDGET_REALIZED (sheet))
4041 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
4042 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
4043 gtk_widget_realize (sheet->entry_widget);
4046 g_signal_connect_swapped (sheet->entry_widget, "key_press_event",
4047 G_CALLBACK (psppire_sheet_entry_key_press),
4050 set_entry_widget_font (sheet);
4052 gtk_widget_show (sheet->entry_widget);
4056 /* Finds the last child widget that happens to be of type GtkEntry */
4058 find_entry (GtkWidget *w, gpointer user_data)
4060 GtkWidget **entry = user_data;
4061 if ( GTK_IS_ENTRY (w))
4069 psppire_sheet_get_entry (PsppireSheet *sheet)
4071 GtkWidget *w = sheet->entry_widget;
4073 g_return_val_if_fail (sheet != NULL, NULL);
4074 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
4075 g_return_val_if_fail (sheet->entry_widget != NULL, NULL);
4077 while (! GTK_IS_ENTRY (w))
4079 GtkWidget *entry = NULL;
4081 if (GTK_IS_CONTAINER (w))
4083 gtk_container_forall (GTK_CONTAINER (w), find_entry, &entry);
4092 return GTK_ENTRY (w);
4097 draw_button (PsppireSheet *sheet, GdkWindow *window,
4098 PsppireSheetButton *button, gboolean is_sensitive,
4099 GdkRectangle allocation)
4101 GtkShadowType shadow_type;
4102 gint text_width = 0, text_height = 0;
4103 PangoAlignment align = PANGO_ALIGN_LEFT;
4109 g_return_if_fail (sheet != NULL);
4110 g_return_if_fail (button != NULL);
4113 rtl = gtk_widget_get_direction (GTK_WIDGET (sheet)) == GTK_TEXT_DIR_RTL;
4115 gdk_window_clear_area (window,
4116 allocation.x, allocation.y,
4117 allocation.width, allocation.height);
4119 gtk_widget_ensure_style (sheet->button);
4121 gtk_paint_box (sheet->button->style, window,
4122 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
4124 GTK_WIDGET (sheet->button),
4126 allocation.x, allocation.y,
4127 allocation.width, allocation.height);
4129 state = button->state;
4130 if (!is_sensitive) state = GTK_STATE_INSENSITIVE;
4132 if (state == GTK_STATE_ACTIVE)
4133 shadow_type = GTK_SHADOW_IN;
4135 shadow_type = GTK_SHADOW_OUT;
4137 if (state != GTK_STATE_NORMAL && state != GTK_STATE_INSENSITIVE)
4138 gtk_paint_box (sheet->button->style, window,
4139 button->state, shadow_type,
4140 &allocation, GTK_WIDGET (sheet->button),
4142 allocation.x, allocation.y,
4143 allocation.width, allocation.height);
4145 if ( button->overstruck)
4147 GdkPoint points[2] = {
4148 {allocation.x, allocation.y},
4149 {allocation.x + allocation.width,
4150 allocation.y + allocation.height}
4153 gtk_paint_polygon (sheet->button->style,
4165 if (button->label_visible)
4167 text_height = DEFAULT_ROW_HEIGHT -
4168 2 * COLUMN_TITLES_HEIGHT;
4170 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4172 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc,
4175 allocation.y += 2 * sheet->button->style->ythickness;
4177 if (button->label && strlen (button->label) > 0)
4179 PangoRectangle rect;
4180 gchar *line = button->label;
4182 PangoLayout *layout = NULL;
4183 gint real_x = allocation.x;
4184 gint real_y = allocation.y;
4186 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), line);
4187 pango_layout_get_extents (layout, NULL, &rect);
4189 text_width = PANGO_PIXELS (rect.width);
4190 switch (button->justification)
4192 case GTK_JUSTIFY_LEFT:
4193 real_x = allocation.x + COLUMN_TITLES_HEIGHT;
4194 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4196 case GTK_JUSTIFY_RIGHT:
4197 real_x = allocation.x + allocation.width - text_width - COLUMN_TITLES_HEIGHT;
4198 align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
4200 case GTK_JUSTIFY_CENTER:
4202 real_x = allocation.x + (allocation.width - text_width)/2;
4203 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4204 pango_layout_set_justify (layout, TRUE);
4206 pango_layout_set_alignment (layout, align);
4207 gtk_paint_layout (GTK_WIDGET (sheet)->style,
4216 g_object_unref (layout);
4219 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4221 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc, NULL);
4225 psppire_sheet_button_free (button);
4229 /* Draw the column title buttons FIRST through to LAST */
4231 draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4235 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4237 if (!sheet->column_titles_visible) return;
4239 g_return_if_fail (first >= min_visible_column (sheet));
4240 g_return_if_fail (last <= max_visible_column (sheet));
4243 rect.height = sheet->column_title_area.height;
4244 rect.x = psppire_axis_start_pixel (sheet->haxis, first) + CELL_SPACING;
4245 rect.width = psppire_axis_start_pixel (sheet->haxis, last) + CELL_SPACING
4246 + psppire_axis_unit_size (sheet->haxis, last);
4248 rect.x -= sheet->hadjustment->value;
4250 minimize_int (&rect.width, sheet->column_title_area.width);
4251 maximize_int (&rect.x, 0);
4253 gdk_window_begin_paint_rect (sheet->column_title_window, &rect);
4255 for (col = first ; col <= last ; ++col)
4257 GdkRectangle allocation;
4258 gboolean is_sensitive = FALSE;
4260 PsppireSheetButton *
4261 button = psppire_sheet_model_get_column_button (sheet->model, col);
4263 allocation.x = psppire_axis_start_pixel (sheet->haxis, col)
4265 allocation.x -= sheet->hadjustment->value;
4267 allocation.height = sheet->column_title_area.height;
4268 allocation.width = psppire_axis_unit_size (sheet->haxis, col);
4269 is_sensitive = psppire_sheet_model_get_column_sensitivity (sheet->model, col);
4271 draw_button (sheet, sheet->column_title_window,
4272 button, is_sensitive, allocation);
4275 gdk_window_end_paint (sheet->column_title_window);
4280 draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4284 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4286 if (!sheet->row_titles_visible) return;
4288 g_return_if_fail (first >= min_visible_row (sheet));
4289 g_return_if_fail (last <= max_visible_row (sheet));
4292 rect.width = sheet->row_title_area.width;
4293 rect.y = psppire_axis_start_pixel (sheet->vaxis, first) + CELL_SPACING;
4294 rect.height = psppire_axis_start_pixel (sheet->vaxis, last) + CELL_SPACING
4295 + psppire_axis_unit_size (sheet->vaxis, last);
4297 rect.y -= sheet->vadjustment->value;
4299 minimize_int (&rect.height, sheet->row_title_area.height);
4300 maximize_int (&rect.y, 0);
4302 gdk_window_begin_paint_rect (sheet->row_title_window, &rect);
4303 for (row = first; row <= last; ++row)
4305 GdkRectangle allocation;
4307 gboolean is_sensitive = FALSE;
4309 PsppireSheetButton *button =
4310 psppire_sheet_model_get_row_button (sheet->model, row);
4312 allocation.y = psppire_axis_start_pixel (sheet->vaxis, row)
4314 allocation.y -= sheet->vadjustment->value;
4316 allocation.width = sheet->row_title_area.width;
4317 allocation.height = psppire_axis_unit_size (sheet->vaxis, row);
4318 is_sensitive = psppire_sheet_model_get_row_sensitivity (sheet->model, row);
4320 draw_button (sheet, sheet->row_title_window,
4321 button, is_sensitive, allocation);
4324 gdk_window_end_paint (sheet->row_title_window);
4331 * vadjustment_value_changed
4332 * hadjustment_value_changed */
4336 update_adjustment (GtkAdjustment *adj, PsppireAxis *axis, gint page_size)
4339 (adj->value + adj->page_size)
4341 (adj->upper - adj->lower);
4343 const glong last_item = psppire_axis_unit_count (axis) - 1;
4345 if (isnan (position) || position < 0)
4349 psppire_axis_start_pixel (axis, last_item)
4351 psppire_axis_unit_size (axis, last_item)
4355 adj->page_size = page_size;
4358 adj->value = position * (adj->upper - adj->lower) - adj->page_size;
4360 if ( adj->value < adj->lower)
4361 adj->value = adj->lower;
4364 gtk_adjustment_changed (adj);
4369 adjust_scrollbars (PsppireSheet *sheet)
4373 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4376 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
4378 if ( sheet->row_titles_visible)
4379 width -= sheet->row_title_area.width;
4381 if (sheet->column_titles_visible)
4382 height -= sheet->column_title_area.height;
4384 if (sheet->vadjustment)
4386 glong last_row = psppire_axis_unit_count (sheet->vaxis) - 1;
4388 sheet->vadjustment->step_increment =
4390 psppire_axis_unit_size (sheet->vaxis, last_row);
4392 sheet->vadjustment->page_increment =
4394 sheet->column_title_area.height -
4395 psppire_axis_unit_size (sheet->vaxis, last_row);
4397 update_adjustment (sheet->vadjustment, sheet->vaxis, height);
4400 if (sheet->hadjustment)
4402 gint last_col = psppire_axis_unit_count (sheet->haxis) - 1;
4403 sheet->hadjustment->step_increment = 1;
4405 sheet->hadjustment->page_increment = width;
4407 sheet->hadjustment->upper =
4408 psppire_axis_start_pixel (sheet->haxis, last_col)
4410 psppire_axis_unit_size (sheet->haxis, last_col)
4413 update_adjustment (sheet->hadjustment, sheet->haxis, width);
4417 /* Subtracts the region of WIDGET from REGION */
4419 subtract_widget_region (GdkRegion *region, GtkWidget *widget)
4422 GdkRectangle intersect;
4425 gdk_region_get_clipbox (region, &rect);
4426 gtk_widget_intersect (widget,
4430 region2 = gdk_region_rectangle (&intersect);
4431 gdk_region_subtract (region, region2);
4432 gdk_region_destroy (region2);
4436 vadjustment_value_changed (GtkAdjustment *adjustment,
4440 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4442 g_return_if_fail (adjustment != NULL);
4444 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
4446 gtk_widget_hide (sheet->entry_widget);
4449 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
4451 subtract_widget_region (region, sheet->button);
4452 gdk_window_begin_paint_region (sheet->sheet_window, region);
4454 draw_sheet_region (sheet, region);
4456 draw_row_title_buttons (sheet);
4457 psppire_sheet_draw_active_cell (sheet);
4459 gdk_window_end_paint (sheet->sheet_window);
4460 gdk_region_destroy (region);
4465 hadjustment_value_changed (GtkAdjustment *adjustment,
4469 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4471 g_return_if_fail (adjustment != NULL);
4473 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
4475 gtk_widget_hide (sheet->entry_widget);
4479 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
4481 subtract_widget_region (region, sheet->button);
4482 gdk_window_begin_paint_region (sheet->sheet_window, region);
4484 draw_sheet_region (sheet, region);
4486 draw_column_title_buttons (sheet);
4488 psppire_sheet_draw_active_cell (sheet);
4490 gdk_window_end_paint (sheet->sheet_window);
4492 gdk_region_destroy (region);
4496 /* COLUMN RESIZING */
4498 draw_xor_vline (PsppireSheet *sheet)
4501 gint xpos = sheet->x_drag;
4502 gdk_drawable_get_size (sheet->sheet_window,
4505 if (sheet->row_titles_visible)
4506 xpos += sheet->row_title_area.width;
4508 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
4510 sheet->column_title_area.height,
4512 height + CELL_SPACING);
4517 draw_xor_hline (PsppireSheet *sheet)
4521 gint ypos = sheet->y_drag;
4523 gdk_drawable_get_size (sheet->sheet_window,
4527 if (sheet->column_titles_visible)
4528 ypos += sheet->column_title_area.height;
4530 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
4531 sheet->row_title_area.width,
4533 width + CELL_SPACING,
4537 /* SELECTED RANGE */
4539 draw_xor_rectangle (PsppireSheet *sheet, PsppireSheetRange range)
4542 GdkRectangle clip_area, area;
4545 area.x = psppire_axis_start_pixel (sheet->haxis, range.col0);
4546 area.y = psppire_axis_start_pixel (sheet->vaxis, range.row0);
4547 area.width = psppire_axis_start_pixel (sheet->haxis, range.coli)- area.x+
4548 psppire_axis_unit_size (sheet->haxis, range.coli);
4549 area.height = psppire_axis_start_pixel (sheet->vaxis, range.rowi)- area.y +
4550 psppire_axis_unit_size (sheet->vaxis, range.rowi);
4552 clip_area.x = sheet->row_title_area.width;
4553 clip_area.y = sheet->column_title_area.height;
4555 gdk_drawable_get_size (sheet->sheet_window,
4556 &clip_area.width, &clip_area.height);
4558 if (!sheet->row_titles_visible) clip_area.x = 0;
4559 if (!sheet->column_titles_visible) clip_area.y = 0;
4563 area.width = area.width + area.x;
4566 if (area.width > clip_area.width) area.width = clip_area.width + 10;
4569 area.height = area.height + area.y;
4572 if (area.height > clip_area.height) area.height = clip_area.height + 10;
4576 clip_area.width += 3;
4577 clip_area.height += 3;
4579 gdk_gc_get_values (sheet->xor_gc, &values);
4581 gdk_gc_set_clip_rectangle (sheet->xor_gc, &clip_area);
4583 gdk_draw_rectangle (sheet->sheet_window,
4586 area.x + i, area.y + i,
4587 area.width - 2 * i, area.height - 2 * i);
4590 gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
4592 gdk_gc_set_foreground (sheet->xor_gc, &values.foreground);
4597 set_column_width (PsppireSheet *sheet,
4601 g_return_if_fail (sheet != NULL);
4602 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
4604 if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis))
4610 psppire_axis_resize (sheet->haxis, column,
4611 width - sheet->cell_padding->left -
4612 sheet->cell_padding->right);
4614 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4616 draw_column_title_buttons (sheet);
4617 adjust_scrollbars (sheet);
4618 psppire_sheet_size_allocate_entry (sheet);
4619 redraw_range (sheet, NULL);
4624 set_row_height (PsppireSheet *sheet,
4628 g_return_if_fail (sheet != NULL);
4629 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
4631 if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis))
4637 psppire_axis_resize (sheet->vaxis, row,
4638 height - sheet->cell_padding->top -
4639 sheet->cell_padding->bottom);
4641 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) )
4643 draw_row_title_buttons (sheet);
4644 adjust_scrollbars (sheet);
4645 psppire_sheet_size_allocate_entry (sheet);
4646 redraw_range (sheet, NULL);
4651 psppire_sheet_get_attributes (const PsppireSheet *sheet, gint row, gint col,
4652 PsppireSheetCellAttr *attr)
4655 const GtkJustification *j ;
4656 GdkColormap *colormap;
4658 g_return_val_if_fail (sheet != NULL, FALSE);
4659 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), FALSE);
4661 if (row < 0 || col < 0) return FALSE;
4663 attr->foreground = GTK_WIDGET (sheet)->style->black;
4664 attr->background = sheet->color[BG_COLOR];
4666 attr->border.width = 0;
4667 attr->border.line_style = GDK_LINE_SOLID;
4668 attr->border.cap_style = GDK_CAP_NOT_LAST;
4669 attr->border.join_style = GDK_JOIN_MITER;
4670 attr->border.mask = 0;
4671 attr->border.color = GTK_WIDGET (sheet)->style->black;
4673 colormap = gtk_widget_get_colormap (GTK_WIDGET (sheet));
4674 fg = psppire_sheet_model_get_foreground (sheet->model, row, col);
4677 gdk_colormap_alloc_color (colormap, fg, TRUE, TRUE);
4678 attr->foreground = *fg;
4681 bg = psppire_sheet_model_get_background (sheet->model, row, col);
4684 gdk_colormap_alloc_color (colormap, bg, TRUE, TRUE);
4685 attr->background = *bg;
4688 attr->justification =
4689 psppire_sheet_model_get_column_justification (sheet->model, col);
4691 j = psppire_sheet_model_get_justification (sheet->model, row, col);
4693 attr->justification = *j;
4699 psppire_sheet_button_size_request (PsppireSheet *sheet,
4700 const PsppireSheetButton *button,
4701 GtkRequisition *button_requisition)
4703 GtkRequisition requisition;
4704 GtkRequisition label_requisition;
4706 label_requisition.height = DEFAULT_ROW_HEIGHT;
4707 label_requisition.width = COLUMN_MIN_WIDTH;
4709 requisition.height = DEFAULT_ROW_HEIGHT;
4710 requisition.width = COLUMN_MIN_WIDTH;
4713 *button_requisition = requisition;
4714 button_requisition->width = MAX (requisition.width, label_requisition.width);
4715 button_requisition->height = MAX (requisition.height, label_requisition.height);
4720 psppire_sheet_forall (GtkContainer *container,
4721 gboolean include_internals,
4722 GtkCallback callback,
4723 gpointer callback_data)
4725 PsppireSheet *sheet = PSPPIRE_SHEET (container);
4727 g_return_if_fail (callback != NULL);
4729 if (sheet->button && sheet->button->parent)
4730 (* callback) (sheet->button, callback_data);
4732 if (sheet->entry_widget && GTK_IS_CONTAINER (sheet->entry_widget))
4733 (* callback) (sheet->entry_widget, callback_data);
4738 psppire_sheet_get_model (const PsppireSheet *sheet)
4740 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
4742 return sheet->model;
4746 PsppireSheetButton *
4747 psppire_sheet_button_new (void)
4749 PsppireSheetButton *button = g_malloc (sizeof (PsppireSheetButton));
4751 button->state = GTK_STATE_NORMAL;
4752 button->label = NULL;
4753 button->label_visible = TRUE;
4754 button->justification = GTK_JUSTIFY_FILL;
4755 button->overstruck = FALSE;
4762 psppire_sheet_button_free (PsppireSheetButton *button)
4764 if (!button) return ;
4766 g_free (button->label);
4771 append_cell_text (GString *string, const PsppireSheet *sheet, gint r, gint c)
4773 gchar *celltext = psppire_sheet_cell_get_text (sheet, r, c);
4775 if ( NULL == celltext)
4778 g_string_append (string, celltext);
4784 range_to_text (const PsppireSheet *sheet)
4789 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
4792 string = g_string_sized_new (80);
4794 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
4796 for (c = sheet->range.col0; c < sheet->range.coli; ++c)
4798 append_cell_text (string, sheet, r, c);
4799 g_string_append (string, "\t");
4801 append_cell_text (string, sheet, r, c);
4802 if ( r < sheet->range.rowi)
4803 g_string_append (string, "\n");
4810 range_to_html (const PsppireSheet *sheet)
4815 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
4818 string = g_string_sized_new (480);
4820 g_string_append (string, "<html>\n");
4821 g_string_append (string, "<body>\n");
4822 g_string_append (string, "<table>\n");
4823 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
4825 g_string_append (string, "<tr>\n");
4826 for (c = sheet->range.col0; c <= sheet->range.coli; ++c)
4828 g_string_append (string, "<td>");
4829 append_cell_text (string, sheet, r, c);
4830 g_string_append (string, "</td>\n");
4832 g_string_append (string, "</tr>\n");
4834 g_string_append (string, "</table>\n");
4835 g_string_append (string, "</body>\n");
4836 g_string_append (string, "</html>\n");
4848 primary_get_cb (GtkClipboard *clipboard,
4849 GtkSelectionData *selection_data,
4853 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4854 GString *string = NULL;
4858 case SELECT_FMT_TEXT:
4859 string = range_to_text (sheet);
4861 case SELECT_FMT_HTML:
4862 string = range_to_html (sheet);
4865 g_assert_not_reached ();
4868 gtk_selection_data_set (selection_data, selection_data->target,
4870 (const guchar *) string->str, string->len);
4871 g_string_free (string, TRUE);
4875 primary_clear_cb (GtkClipboard *clipboard,
4878 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4879 if ( ! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4882 psppire_sheet_unselect_range (sheet);
4886 psppire_sheet_update_primary_selection (PsppireSheet *sheet)
4888 static const GtkTargetEntry targets[] = {
4889 { "UTF8_STRING", 0, SELECT_FMT_TEXT },
4890 { "STRING", 0, SELECT_FMT_TEXT },
4891 { "TEXT", 0, SELECT_FMT_TEXT },
4892 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
4893 { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
4894 { "text/plain", 0, SELECT_FMT_TEXT },
4895 { "text/html", 0, SELECT_FMT_HTML }
4898 GtkClipboard *clipboard;
4900 if (!GTK_WIDGET_REALIZED (sheet))
4903 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (sheet),
4904 GDK_SELECTION_PRIMARY);
4906 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
4908 if (!gtk_clipboard_set_with_owner (clipboard, targets,
4909 G_N_ELEMENTS (targets),
4910 primary_get_cb, primary_clear_cb,
4912 primary_clear_cb (clipboard, sheet);
4916 if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (sheet))
4917 gtk_clipboard_clear (clipboard);