2 Copyright (C) 2006, 2008, 2009 Free Software Foundation
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 This file is derived from the gtksheet.c and extensively modified for the
19 requirements of PSPPIRE. The changes are copyright by the
20 Free Software Foundation. The copyright notice for the original work is
24 /* GtkSheet widget for Gtk+.
25 * Copyright (C) 1999-2001 Adrian E. Feiguin <adrian@ifir.ifir.edu.ar>
27 * Based on GtkClist widget by Jay Painter, but major changes.
28 * Memory allocation routines inspired on SC (Spreadsheet Calculator)
30 * This library is free software; you can redistribute it and/or
31 * modify it under the terms of the GNU Lesser General Public
32 * License as published by the Free Software Foundation; either
33 * version 2.1 of the License, or (at your option) any later version.
35 * This library is distributed in the hope that it will be useful,
36 * but WITHOUT ANY WARRANTY; without even the implied warranty of
37 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
38 * Lesser General Public License for more details.
40 * You should have received a copy of the GNU Lesser General Public
41 * License along with this library; if not, write to the Free Software
42 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
46 * SECTION:psppiresheet
47 * @short_description: spreadsheet widget for gtk2
49 * PsppireSheet is a matrix widget for GTK+. It consists of an scrollable grid of
50 * cells where you can allocate text. Cell contents can be edited interactively
51 * through a specially designed entry, GtkItemEntry.
61 #include <gdk/gdkkeysyms.h>
62 #include <gtk/gtksignal.h>
63 #include <gtk/gtkbutton.h>
64 #include <gtk/gtkadjustment.h>
65 #include <gtk/gtktypeutils.h>
66 #include <gtk/gtkentry.h>
67 #include <gtk/gtkcontainer.h>
68 #include <pango/pango.h>
69 #include "psppire-sheet.h"
70 #include <ui/gui/psppire-marshal.h>
71 #include <ui/gui/sheet/psppire-sheetmodel.h>
72 #include <ui/gui/sheet/psppire-axis.h>
73 #include <libpspp/misc.h>
80 PSPPIRE_SHEET_IN_XDRAG = 1 << 1,
81 PSPPIRE_SHEET_IN_YDRAG = 1 << 2,
82 PSPPIRE_SHEET_IN_DRAG = 1 << 3,
83 PSPPIRE_SHEET_IN_SELECTION = 1 << 4,
84 PSPPIRE_SHEET_IN_RESIZE = 1 << 5
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)
95 #define PSPPIRE_SHEET_IN_RESIZE(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_RESIZE)
97 #define CELL_SPACING 1
99 #define TIMEOUT_HOVER 300
100 #define COLUMN_MIN_WIDTH 10
101 #define COLUMN_TITLES_HEIGHT 4
102 #define DEFAULT_COLUMN_WIDTH 80
103 #define DEFAULT_ROW_HEIGHT 25
105 static void set_entry_widget_font (PsppireSheet *sheet);
107 static void psppire_sheet_update_primary_selection (PsppireSheet *sheet);
108 static void draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint n);
109 static void draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint n);
110 static void redraw_range (PsppireSheet *sheet, PsppireSheetRange *range);
113 static void set_row_height (PsppireSheet *sheet,
117 static void destroy_hover_window (PsppireSheetHoverTitle *);
118 static PsppireSheetHoverTitle *create_hover_window (void);
120 static GtkStateType psppire_sheet_cell_get_state (PsppireSheet *sheet, gint row, gint col);
124 dispose_string (const PsppireSheet *sheet, gchar *text)
126 PsppireSheetModel *model = psppire_sheet_get_model (sheet);
131 if (psppire_sheet_model_free_strings (model))
136 /* FIXME: Why bother with these two ? */
138 /* returns the column index from a pixel location */
140 column_from_xpixel (const PsppireSheet *sheet, gint pixel)
142 return psppire_axis_unit_at_pixel (sheet->haxis, pixel);
146 row_from_ypixel (const PsppireSheet *sheet, gint pixel)
148 return psppire_axis_unit_at_pixel (sheet->vaxis, pixel);
152 /* Return the lowest row number which is wholly or partially on
153 the visible range of the sheet */
155 min_visible_row (const PsppireSheet *sheet)
157 return row_from_ypixel (sheet, sheet->vadjustment->value);
161 min_fully_visible_row (const PsppireSheet *sheet)
163 glong row = min_visible_row (sheet);
165 if ( psppire_axis_start_pixel (sheet->vaxis, row) < sheet->vadjustment->value)
172 max_visible_row (const PsppireSheet *sheet)
174 return row_from_ypixel (sheet, sheet->vadjustment->value + sheet->vadjustment->page_size);
179 max_fully_visible_row (const PsppireSheet *sheet)
181 glong row = max_visible_row (sheet);
183 if ( psppire_axis_start_pixel (sheet->vaxis, row)
185 psppire_axis_unit_size (sheet->vaxis, row)
186 > sheet->vadjustment->value)
193 /* Returns the lowest column number which is wholly or partially
196 min_visible_column (const PsppireSheet *sheet)
198 return column_from_xpixel (sheet, sheet->hadjustment->value);
202 min_fully_visible_column (const PsppireSheet *sheet)
204 glong col = min_visible_column (sheet);
206 if ( psppire_axis_start_pixel (sheet->haxis, col) < sheet->hadjustment->value)
213 /* Returns the highest column number which is wholly or partially
216 max_visible_column (const PsppireSheet *sheet)
218 return column_from_xpixel (sheet, sheet->hadjustment->value + sheet->hadjustment->page_size);
222 max_fully_visible_column (const PsppireSheet *sheet)
224 glong col = max_visible_column (sheet);
226 if ( psppire_axis_start_pixel (sheet->haxis, col)
228 psppire_axis_unit_size (sheet->haxis, col)
229 > sheet->hadjustment->value)
237 /* The size of the region (in pixels) around the row/column boundaries
238 where the height/width may be grabbed to change size */
242 on_column_boundary (const PsppireSheet *sheet, gint x, gint *column)
247 x += sheet->hadjustment->value;
252 col = column_from_xpixel (sheet, x);
254 pixel = x - DRAG_WIDTH / 2;
258 if ( column_from_xpixel (sheet, pixel) < col )
264 if ( column_from_xpixel (sheet, x + DRAG_WIDTH / 2) > col )
274 on_row_boundary (const PsppireSheet *sheet, gint y, gint *row)
279 y += sheet->vadjustment->value;
284 r = row_from_ypixel (sheet, y);
286 pixel = y - DRAG_WIDTH / 2;
290 if ( row_from_ypixel (sheet, pixel) < r )
296 if ( row_from_ypixel (sheet, y + DRAG_WIDTH / 2) > r )
306 static inline gboolean
307 POSSIBLE_DRAG (const PsppireSheet *sheet, gint x, gint y,
308 gint *drag_row, gint *drag_column)
312 /* Can't drag if nothing is selected */
313 if ( sheet->range.row0 < 0 || sheet->range.rowi < 0 ||
314 sheet->range.col0 < 0 || sheet->range.coli < 0 )
317 *drag_column = column_from_xpixel (sheet, x);
318 *drag_row = row_from_ypixel (sheet, y);
320 if (x >= psppire_axis_start_pixel (sheet->haxis, sheet->range.col0) - DRAG_WIDTH / 2 &&
321 x <= psppire_axis_start_pixel (sheet->haxis, sheet->range.coli) +
322 psppire_axis_unit_size (sheet->haxis, sheet->range.coli) + DRAG_WIDTH / 2)
324 ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.row0);
325 if (y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2)
327 *drag_row = sheet->range.row0;
330 ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) +
331 psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi);
332 if (y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2)
334 *drag_row = sheet->range.rowi;
339 if (y >= psppire_axis_start_pixel (sheet->vaxis, sheet->range.row0) - DRAG_WIDTH / 2 &&
340 y <= psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) +
341 psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi) + DRAG_WIDTH / 2)
343 xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.col0);
344 if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2)
346 *drag_column = sheet->range.col0;
349 xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.coli) +
350 psppire_axis_unit_size (sheet->haxis, sheet->range.coli);
351 if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2)
353 *drag_column = sheet->range.coli;
361 static inline gboolean
362 POSSIBLE_RESIZE (const PsppireSheet *sheet, gint x, gint y,
363 gint *drag_row, gint *drag_column)
367 /* Can't drag if nothing is selected */
368 if ( sheet->range.row0 < 0 || sheet->range.rowi < 0 ||
369 sheet->range.col0 < 0 || sheet->range.coli < 0 )
372 xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.coli)+
373 psppire_axis_unit_size (sheet->haxis, sheet->range.coli);
375 ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) +
376 psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi);
378 if (sheet->state == PSPPIRE_SHEET_COLUMN_SELECTED)
379 ydrag = psppire_axis_start_pixel (sheet->vaxis, min_visible_row (sheet));
381 if (sheet->state == PSPPIRE_SHEET_ROW_SELECTED)
382 xdrag = psppire_axis_start_pixel (sheet->haxis, min_visible_column (sheet));
384 *drag_column = column_from_xpixel (sheet, x);
385 *drag_row = row_from_ypixel (sheet, y);
387 if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2 &&
388 y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2) return TRUE;
395 rectangle_from_range (PsppireSheet *sheet, const PsppireSheetRange *range,
398 g_return_val_if_fail (range, FALSE);
400 r->x = psppire_axis_start_pixel (sheet->haxis, range->col0);
401 r->x -= round (sheet->hadjustment->value);
403 r->y = psppire_axis_start_pixel (sheet->vaxis, range->row0);
404 r->y -= round (sheet->vadjustment->value);
406 r->width = psppire_axis_start_pixel (sheet->haxis, range->coli) -
407 psppire_axis_start_pixel (sheet->haxis, range->col0) +
408 psppire_axis_unit_size (sheet->haxis, range->coli);
410 r->height = psppire_axis_start_pixel (sheet->vaxis, range->rowi) -
411 psppire_axis_start_pixel (sheet->vaxis, range->row0) +
412 psppire_axis_unit_size (sheet->vaxis, range->rowi);
414 if ( sheet->column_titles_visible)
416 r->y += sheet->column_title_area.height;
419 if ( sheet->row_titles_visible)
421 r->x += sheet->row_title_area.width;
428 rectangle_from_cell (PsppireSheet *sheet, gint row, gint col,
431 PsppireSheetRange range;
432 g_return_val_if_fail (row >= 0, FALSE);
433 g_return_val_if_fail (col >= 0, FALSE);
435 range.row0 = range.rowi = row;
436 range.col0 = range.coli = col;
438 return rectangle_from_range (sheet, &range, r);
442 static void psppire_sheet_class_init (PsppireSheetClass *klass);
443 static void psppire_sheet_init (PsppireSheet *sheet);
444 static void psppire_sheet_dispose (GObject *object);
445 static void psppire_sheet_finalize (GObject *object);
446 static void psppire_sheet_style_set (GtkWidget *widget,
447 GtkStyle *previous_style);
448 static void psppire_sheet_realize (GtkWidget *widget);
449 static void psppire_sheet_unrealize (GtkWidget *widget);
450 static void psppire_sheet_map (GtkWidget *widget);
451 static void psppire_sheet_unmap (GtkWidget *widget);
452 static gint psppire_sheet_expose (GtkWidget *widget,
453 GdkEventExpose *event);
455 static void psppire_sheet_forall (GtkContainer *container,
456 gboolean include_internals,
457 GtkCallback callback,
458 gpointer callback_data);
460 static gboolean psppire_sheet_set_scroll_adjustments (PsppireSheet *sheet,
461 GtkAdjustment *hadjustment,
462 GtkAdjustment *vadjustment);
464 static gint psppire_sheet_button_press (GtkWidget *widget,
465 GdkEventButton *event);
466 static gint psppire_sheet_button_release (GtkWidget *widget,
467 GdkEventButton *event);
468 static gint psppire_sheet_motion (GtkWidget *widget,
469 GdkEventMotion *event);
470 static gboolean psppire_sheet_crossing_notify (GtkWidget *widget,
471 GdkEventCrossing *event);
472 static gint psppire_sheet_entry_key_press (GtkWidget *widget,
474 static gboolean psppire_sheet_key_press (GtkWidget *widget,
476 static void psppire_sheet_size_request (GtkWidget *widget,
477 GtkRequisition *requisition);
478 static void psppire_sheet_size_allocate (GtkWidget *widget,
479 GtkAllocation *allocation);
481 static gboolean psppire_sheet_focus_in (GtkWidget *widget,
482 GdkEventFocus *event);
486 static gboolean psppire_sheet_range_isvisible (const PsppireSheet *sheet,
487 const PsppireSheetRange *range);
488 static gboolean psppire_sheet_cell_isvisible (PsppireSheet *sheet,
489 gint row, gint column);
490 /* Drawing Routines */
493 static void psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint column);
496 /* draw visible part of range. */
497 static void draw_sheet_region (PsppireSheet *sheet, GdkRegion *region);
500 /* highlight the visible part of the selected range */
501 static void psppire_sheet_range_draw_selection (PsppireSheet *sheet,
502 PsppireSheetRange range);
506 static void psppire_sheet_real_select_range (PsppireSheet *sheet,
507 const PsppireSheetRange *range);
508 static void psppire_sheet_real_unselect_range (PsppireSheet *sheet,
509 const PsppireSheetRange *range);
510 static void psppire_sheet_extend_selection (PsppireSheet *sheet,
511 gint row, gint column);
512 static void psppire_sheet_new_selection (PsppireSheet *sheet,
513 PsppireSheetRange *range);
514 static void psppire_sheet_draw_border (PsppireSheet *sheet,
515 PsppireSheetRange range);
517 /* Active Cell handling */
519 static void psppire_sheet_hide_entry_widget (PsppireSheet *sheet);
520 static void change_active_cell (PsppireSheet *sheet, gint row, gint col);
521 static gboolean psppire_sheet_draw_active_cell (PsppireSheet *sheet);
522 static void psppire_sheet_show_entry_widget (PsppireSheet *sheet);
523 static gboolean psppire_sheet_click_cell (PsppireSheet *sheet,
530 static void adjust_scrollbars (PsppireSheet *sheet);
531 static void vadjustment_value_changed (GtkAdjustment *adjustment,
533 static void hadjustment_value_changed (GtkAdjustment *adjustment,
537 static void draw_xor_vline (PsppireSheet *sheet);
538 static void draw_xor_hline (PsppireSheet *sheet);
539 static void draw_xor_rectangle (PsppireSheet *sheet,
540 PsppireSheetRange range);
544 static void create_global_button (PsppireSheet *sheet);
545 static void global_button_clicked (GtkWidget *widget,
549 static void create_sheet_entry (PsppireSheet *sheet);
550 static void psppire_sheet_size_allocate_entry (PsppireSheet *sheet);
552 /* Sheet button gadgets */
554 static void draw_column_title_buttons (PsppireSheet *sheet);
555 static void draw_row_title_buttons (PsppireSheet *sheet);
558 static void size_allocate_global_button (PsppireSheet *sheet);
559 static void psppire_sheet_button_size_request (PsppireSheet *sheet,
560 const PsppireSheetButton *button,
561 GtkRequisition *requisition);
563 static void psppire_sheet_real_cell_clear (PsppireSheet *sheet,
585 static GtkContainerClass *parent_class = NULL;
586 static guint sheet_signals[LAST_SIGNAL] = { 0 };
590 psppire_sheet_get_type ()
592 static GType sheet_type = 0;
596 static const GTypeInfo sheet_info =
598 sizeof (PsppireSheetClass),
601 (GClassInitFunc) psppire_sheet_class_init,
604 sizeof (PsppireSheet),
606 (GInstanceInitFunc) psppire_sheet_init,
611 g_type_register_static (GTK_TYPE_BIN, "PsppireSheet",
619 static PsppireSheetRange*
620 psppire_sheet_range_copy (const PsppireSheetRange *range)
622 PsppireSheetRange *new_range;
624 g_return_val_if_fail (range != NULL, NULL);
626 new_range = g_new (PsppireSheetRange, 1);
634 psppire_sheet_range_free (PsppireSheetRange *range)
636 g_return_if_fail (range != NULL);
642 psppire_sheet_range_get_type (void)
644 static GType sheet_range_type = 0;
646 if (!sheet_range_type)
649 g_boxed_type_register_static ("PsppireSheetRange",
650 (GBoxedCopyFunc) psppire_sheet_range_copy,
651 (GBoxedFreeFunc) psppire_sheet_range_free);
654 return sheet_range_type;
657 static PsppireSheetCell*
658 psppire_sheet_cell_copy (const PsppireSheetCell *cell)
660 PsppireSheetCell *new_cell;
662 g_return_val_if_fail (cell != NULL, NULL);
664 new_cell = g_new (PsppireSheetCell, 1);
672 psppire_sheet_cell_free (PsppireSheetCell *cell)
674 g_return_if_fail (cell != NULL);
680 psppire_sheet_cell_get_type (void)
682 static GType sheet_cell_type = 0;
684 if (!sheet_cell_type)
687 g_boxed_type_register_static ("PsppireSheetCell",
688 (GBoxedCopyFunc) psppire_sheet_cell_copy,
689 (GBoxedFreeFunc) psppire_sheet_cell_free);
692 return sheet_cell_type;
707 resize_column (PsppireSheet *sheet, gint unit, glong size)
709 PsppireSheetRange range;
711 range.coli = max_visible_column (sheet);
712 range.row0 = min_visible_row (sheet);
713 range.rowi = max_visible_row (sheet);
715 redraw_range (sheet, &range);
717 draw_column_title_buttons_range (sheet, range.col0, range.coli);
722 psppire_sheet_set_horizontal_axis (PsppireSheet *sheet, PsppireAxis *a)
725 g_object_unref (sheet->haxis);
728 g_signal_connect_swapped (a, "resize-unit", G_CALLBACK (resize_column), sheet);
731 g_object_ref (sheet->haxis);
735 resize_row (PsppireSheet *sheet, gint unit, glong size)
737 PsppireSheetRange range;
738 range.col0 = min_visible_column (sheet);
739 range.coli = max_visible_column (sheet);
741 range.rowi = max_visible_row (sheet);
743 redraw_range (sheet, &range);
745 draw_row_title_buttons_range (sheet, range.row0, range.rowi);
749 psppire_sheet_set_vertical_axis (PsppireSheet *sheet, PsppireAxis *a)
752 g_object_unref (sheet->vaxis);
756 g_signal_connect_swapped (a, "resize-unit", G_CALLBACK (resize_row), sheet);
759 g_object_ref (sheet->vaxis);
762 static const GtkBorder default_cell_padding = {3, 3, 3, 3};
765 psppire_sheet_set_property (GObject *object,
771 PsppireSheet *sheet = PSPPIRE_SHEET (object);
775 case PROP_CELL_PADDING:
776 if ( sheet->cell_padding)
777 g_boxed_free (GTK_TYPE_BORDER, sheet->cell_padding);
779 sheet->cell_padding = g_value_dup_boxed (value);
781 if (NULL == sheet->cell_padding)
782 sheet->cell_padding = g_boxed_copy (GTK_TYPE_BORDER,
783 &default_cell_padding);
786 g_object_set (sheet->vaxis, "padding",
787 sheet->cell_padding->top + sheet->cell_padding->bottom,
791 g_object_set (sheet->haxis, "padding",
792 sheet->cell_padding->left + sheet->cell_padding->right,
796 psppire_sheet_set_vertical_axis (sheet, g_value_get_pointer (value));
797 g_object_set (sheet->vaxis, "padding",
798 sheet->cell_padding->top + sheet->cell_padding->bottom,
802 psppire_sheet_set_horizontal_axis (sheet, g_value_get_pointer (value));
803 g_object_set (sheet->haxis, "padding",
804 sheet->cell_padding->left + sheet->cell_padding->right,
808 psppire_sheet_set_model (sheet, g_value_get_pointer (value));
811 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
817 psppire_sheet_get_property (GObject *object,
822 PsppireSheet *sheet = PSPPIRE_SHEET (object);
826 case PROP_CELL_PADDING:
827 g_value_set_boxed (value, sheet->cell_padding);
830 g_value_set_pointer (value, sheet->vaxis);
833 g_value_set_pointer (value, sheet->haxis);
836 g_value_set_pointer (value, sheet->model);
839 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
846 psppire_sheet_class_init (PsppireSheetClass *klass)
848 GObjectClass *object_class = G_OBJECT_CLASS (klass);
850 GParamSpec *haxis_spec ;
851 GParamSpec *vaxis_spec ;
852 GParamSpec *model_spec ;
853 GParamSpec *cell_padding_spec ;
855 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
856 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
858 parent_class = g_type_class_peek_parent (klass);
861 * PsppireSheet::select-row
862 * @sheet: the sheet widget that emitted the signal
863 * @row: the newly selected row index
865 * A row has been selected.
867 sheet_signals[SELECT_ROW] =
868 g_signal_new ("select-row",
869 G_TYPE_FROM_CLASS (object_class),
871 offsetof (PsppireSheetClass, select_row),
873 g_cclosure_marshal_VOID__INT,
880 * PsppireSheet::select - column
881 * @sheet: the sheet widget that emitted the signal
882 * @column: the newly selected column index
884 * A column has been selected.
886 sheet_signals[SELECT_COLUMN] =
887 g_signal_new ("select-column",
888 G_TYPE_FROM_CLASS (object_class),
890 offsetof (PsppireSheetClass, select_column),
892 g_cclosure_marshal_VOID__INT,
899 * PsppireSheet::double-click-row
900 * @sheet: the sheet widget that emitted the signal
901 * @row: the row that was double clicked.
903 * A row's title button has been double clicked
905 sheet_signals[DOUBLE_CLICK_ROW] =
906 g_signal_new ("double-click-row",
907 G_TYPE_FROM_CLASS (object_class),
911 g_cclosure_marshal_VOID__INT,
918 * PsppireSheet::double-click-column
919 * @sheet: the sheet widget that emitted the signal
920 * @column: the column that was double clicked.
922 * A column's title button has been double clicked
924 sheet_signals[DOUBLE_CLICK_COLUMN] =
925 g_signal_new ("double-click-column",
926 G_TYPE_FROM_CLASS (object_class),
930 g_cclosure_marshal_VOID__INT,
937 * PsppireSheet::button-event-column
938 * @sheet: the sheet widget that emitted the signal
939 * @column: the column on which the event occured.
941 * A button event occured on a column title button
943 sheet_signals[BUTTON_EVENT_COLUMN] =
944 g_signal_new ("button-event-column",
945 G_TYPE_FROM_CLASS (object_class),
949 psppire_marshal_VOID__INT_POINTER,
958 * PsppireSheet::button-event-row
959 * @sheet: the sheet widget that emitted the signal
960 * @column: the column on which the event occured.
962 * A button event occured on a row title button
964 sheet_signals[BUTTON_EVENT_ROW] =
965 g_signal_new ("button-event-row",
966 G_TYPE_FROM_CLASS (object_class),
970 psppire_marshal_VOID__INT_POINTER,
978 sheet_signals[SELECT_RANGE] =
979 g_signal_new ("select-range",
980 G_TYPE_FROM_CLASS (object_class),
982 offsetof (PsppireSheetClass, select_range),
984 g_cclosure_marshal_VOID__BOXED,
987 PSPPIRE_TYPE_SHEET_RANGE);
990 sheet_signals[RESIZE_RANGE] =
991 g_signal_new ("resize-range",
992 G_TYPE_FROM_CLASS (object_class),
994 offsetof (PsppireSheetClass, resize_range),
996 psppire_marshal_VOID__BOXED_BOXED,
999 PSPPIRE_TYPE_SHEET_RANGE, PSPPIRE_TYPE_SHEET_RANGE
1002 sheet_signals[MOVE_RANGE] =
1003 g_signal_new ("move-range",
1004 G_TYPE_FROM_CLASS (object_class),
1006 offsetof (PsppireSheetClass, move_range),
1008 psppire_marshal_VOID__BOXED_BOXED,
1011 PSPPIRE_TYPE_SHEET_RANGE, PSPPIRE_TYPE_SHEET_RANGE
1014 sheet_signals[TRAVERSE] =
1015 g_signal_new ("traverse",
1016 G_TYPE_FROM_CLASS (object_class),
1018 offsetof (PsppireSheetClass, traverse),
1020 psppire_marshal_BOOLEAN__BOXED_POINTER,
1022 PSPPIRE_TYPE_SHEET_CELL,
1026 sheet_signals[ACTIVATE] =
1027 g_signal_new ("activate",
1028 G_TYPE_FROM_CLASS (object_class),
1030 offsetof (PsppireSheetClass, activate),
1032 psppire_marshal_VOID__INT_INT_INT_INT,
1034 G_TYPE_INT, G_TYPE_INT,
1035 G_TYPE_INT, G_TYPE_INT);
1037 widget_class->set_scroll_adjustments_signal =
1038 g_signal_new ("set-scroll-adjustments",
1039 G_TYPE_FROM_CLASS (object_class),
1041 offsetof (PsppireSheetClass, set_scroll_adjustments),
1043 psppire_marshal_VOID__OBJECT_OBJECT,
1044 G_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
1047 container_class->add = NULL;
1048 container_class->remove = NULL;
1049 container_class->forall = psppire_sheet_forall;
1050 container_class->set_focus_child = NULL;
1052 object_class->dispose = psppire_sheet_dispose;
1053 object_class->finalize = psppire_sheet_finalize;
1056 g_param_spec_boxed ("cell-padding",
1058 "The space between a cell's contents and its border",
1060 G_PARAM_CONSTRUCT | G_PARAM_READABLE | G_PARAM_WRITABLE);
1063 g_param_spec_pointer ("vertical-axis",
1065 "A pointer to the PsppireAxis object for the rows",
1066 G_PARAM_READABLE | G_PARAM_WRITABLE );
1069 g_param_spec_pointer ("horizontal-axis",
1071 "A pointer to the PsppireAxis object for the columns",
1072 G_PARAM_READABLE | G_PARAM_WRITABLE );
1075 g_param_spec_pointer ("model",
1077 "A pointer to the data model",
1078 G_PARAM_READABLE | G_PARAM_WRITABLE );
1081 object_class->set_property = psppire_sheet_set_property;
1082 object_class->get_property = psppire_sheet_get_property;
1084 g_object_class_install_property (object_class,
1088 g_object_class_install_property (object_class,
1092 g_object_class_install_property (object_class,
1096 g_object_class_install_property (object_class,
1101 widget_class->realize = psppire_sheet_realize;
1102 widget_class->unrealize = psppire_sheet_unrealize;
1103 widget_class->map = psppire_sheet_map;
1104 widget_class->unmap = psppire_sheet_unmap;
1105 widget_class->style_set = psppire_sheet_style_set;
1106 widget_class->button_press_event = psppire_sheet_button_press;
1107 widget_class->button_release_event = psppire_sheet_button_release;
1108 widget_class->motion_notify_event = psppire_sheet_motion;
1109 widget_class->enter_notify_event = psppire_sheet_crossing_notify;
1110 widget_class->leave_notify_event = psppire_sheet_crossing_notify;
1111 widget_class->key_press_event = psppire_sheet_key_press;
1112 widget_class->expose_event = psppire_sheet_expose;
1113 widget_class->size_request = psppire_sheet_size_request;
1114 widget_class->size_allocate = psppire_sheet_size_allocate;
1115 widget_class->focus_in_event = psppire_sheet_focus_in;
1116 widget_class->focus_out_event = NULL;
1118 klass->set_scroll_adjustments = psppire_sheet_set_scroll_adjustments;
1119 klass->select_row = NULL;
1120 klass->select_column = NULL;
1121 klass->select_range = NULL;
1122 klass->resize_range = NULL;
1123 klass->move_range = NULL;
1124 klass->traverse = NULL;
1125 klass->activate = NULL;
1126 klass->changed = NULL;
1130 psppire_sheet_init (PsppireSheet *sheet)
1132 sheet->model = NULL;
1133 sheet->haxis = NULL;
1134 sheet->vaxis = NULL;
1137 sheet->selection_mode = GTK_SELECTION_NONE;
1138 sheet->state = PSPPIRE_SHEET_NORMAL;
1140 GTK_WIDGET_UNSET_FLAGS (sheet, GTK_NO_WINDOW);
1141 GTK_WIDGET_SET_FLAGS (sheet, GTK_CAN_FOCUS);
1143 sheet->column_title_window = NULL;
1144 sheet->column_title_area.x = 0;
1145 sheet->column_title_area.y = 0;
1146 sheet->column_title_area.width = 0;
1147 sheet->column_title_area.height = DEFAULT_ROW_HEIGHT;
1149 sheet->row_title_window = NULL;
1150 sheet->row_title_area.x = 0;
1151 sheet->row_title_area.y = 0;
1152 sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
1153 sheet->row_title_area.height = 0;
1156 sheet->active_cell.row = 0;
1157 sheet->active_cell.col = 0;
1158 sheet->selection_cell.row = 0;
1159 sheet->selection_cell.col = 0;
1161 sheet->range.row0 = 0;
1162 sheet->range.rowi = 0;
1163 sheet->range.col0 = 0;
1164 sheet->range.coli = 0;
1166 sheet->state = PSPPIRE_SHEET_NORMAL;
1168 sheet->sheet_window = NULL;
1169 sheet->entry_widget = NULL;
1170 sheet->button = NULL;
1172 sheet->hadjustment = NULL;
1173 sheet->vadjustment = NULL;
1175 sheet->cursor_drag = NULL;
1177 sheet->xor_gc = NULL;
1178 sheet->fg_gc = NULL;
1179 sheet->bg_gc = NULL;
1182 sheet->show_grid = TRUE;
1184 sheet->motion_timer = 0;
1186 sheet->row_titles_visible = TRUE;
1187 sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
1189 sheet->column_titles_visible = TRUE;
1192 /* create sheet entry */
1193 sheet->entry_type = GTK_TYPE_ENTRY;
1194 create_sheet_entry (sheet);
1196 /* create global selection button */
1197 create_global_button (sheet);
1201 /* Cause RANGE to be redrawn. If RANGE is null, then the
1202 entire visible range will be redrawn.
1205 redraw_range (PsppireSheet *sheet, PsppireSheetRange *range)
1209 if ( ! GTK_WIDGET_REALIZED (sheet))
1212 if ( NULL != range )
1213 rectangle_from_range (sheet, range, &rect);
1216 GdkRegion *r = gdk_drawable_get_visible_region (sheet->sheet_window);
1217 gdk_region_get_clipbox (r, &rect);
1219 if ( sheet->column_titles_visible)
1221 rect.y += sheet->column_title_area.height;
1222 rect.height -= sheet->column_title_area.height;
1225 if ( sheet->row_titles_visible)
1227 rect.x += sheet->row_title_area.width;
1228 rect.width -= sheet->row_title_area.width;
1232 gdk_window_invalidate_rect (sheet->sheet_window, &rect, FALSE);
1236 /* Callback which occurs whenever columns are inserted / deleted in the model */
1238 columns_inserted_deleted_callback (PsppireSheetModel *model, gint first_column,
1242 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1244 PsppireSheetRange range;
1245 gint model_columns = psppire_sheet_model_get_column_count (model);
1248 /* Need to update all the columns starting from the first column and onwards.
1249 * Previous column are unchanged, so don't need to be updated.
1251 range.col0 = first_column;
1253 range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1254 range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1256 adjust_scrollbars (sheet);
1258 if (sheet->active_cell.col >= model_columns)
1259 change_active_cell (sheet, sheet->active_cell.row, model_columns - 1);
1261 draw_column_title_buttons_range (sheet,
1262 first_column, max_visible_column (sheet));
1265 redraw_range (sheet, &range);
1271 /* Callback which occurs whenever rows are inserted / deleted in the model */
1273 rows_inserted_deleted_callback (PsppireSheetModel *model, gint first_row,
1274 gint n_rows, gpointer data)
1276 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1278 PsppireSheetRange range;
1280 gint model_rows = psppire_sheet_model_get_row_count (model);
1282 /* Need to update all the rows starting from the first row and onwards.
1283 * Previous rows are unchanged, so don't need to be updated.
1285 range.row0 = first_row;
1287 range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1288 range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1290 adjust_scrollbars (sheet);
1292 if (sheet->active_cell.row >= model_rows)
1293 change_active_cell (sheet, model_rows - 1, sheet->active_cell.col);
1295 draw_row_title_buttons_range (sheet, first_row, max_visible_row (sheet));
1297 redraw_range (sheet, &range);
1301 If row0 or rowi are negative, then all rows will be updated.
1302 If col0 or coli are negative, then all columns will be updated.
1305 range_update_callback (PsppireSheetModel *m, gint row0, gint col0,
1306 gint rowi, gint coli, gpointer data)
1308 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1310 PsppireSheetRange range;
1317 if ( !GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1320 if ( ( row0 < 0 && col0 < 0 ) || ( rowi < 0 && coli < 0 ) )
1322 redraw_range (sheet, NULL);
1323 adjust_scrollbars (sheet);
1325 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
1326 max_visible_row (sheet));
1328 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
1329 max_visible_column (sheet));
1333 else if ( row0 < 0 || rowi < 0 )
1335 range.row0 = min_visible_row (sheet);
1336 range.rowi = max_visible_row (sheet);
1338 else if ( col0 < 0 || coli < 0 )
1340 range.col0 = min_visible_column (sheet);
1341 range.coli = max_visible_column (sheet);
1344 redraw_range (sheet, &range);
1349 * psppire_sheet_new:
1350 * @rows: initial number of rows
1351 * @columns: initial number of columns
1352 * @title: sheet title
1353 * @model: the model to use for the sheet data
1355 * Creates a new sheet widget with the given number of rows and columns.
1357 * Returns: the new sheet widget
1360 psppire_sheet_new (PsppireSheetModel *model)
1362 GtkWidget *widget = g_object_new (PSPPIRE_TYPE_SHEET,
1370 * psppire_sheet_set_model
1371 * @sheet: the sheet to set the model for
1372 * @model: the model to use for the sheet data
1374 * Sets the model for a PsppireSheet
1378 psppire_sheet_set_model (PsppireSheet *sheet, PsppireSheetModel *model)
1380 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1382 if (sheet->model ) g_object_unref (sheet->model);
1384 sheet->model = model;
1388 g_object_ref (model);
1390 sheet->update_handler_id = g_signal_connect (model, "range_changed",
1391 G_CALLBACK (range_update_callback),
1394 g_signal_connect (model, "rows_inserted",
1395 G_CALLBACK (rows_inserted_deleted_callback), sheet);
1397 g_signal_connect (model, "rows_deleted",
1398 G_CALLBACK (rows_inserted_deleted_callback), sheet);
1400 g_signal_connect (model, "columns_inserted",
1401 G_CALLBACK (columns_inserted_deleted_callback), sheet);
1403 g_signal_connect (model, "columns_deleted",
1404 G_CALLBACK (columns_inserted_deleted_callback), sheet);
1410 psppire_sheet_change_entry (PsppireSheet *sheet, GtkType entry_type)
1414 g_return_if_fail (sheet != NULL);
1415 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1417 state = sheet->state;
1419 if (sheet->state == PSPPIRE_SHEET_NORMAL)
1420 psppire_sheet_hide_entry_widget (sheet);
1422 sheet->entry_type = entry_type;
1424 create_sheet_entry (sheet);
1426 if (state == PSPPIRE_SHEET_NORMAL)
1428 psppire_sheet_show_entry_widget (sheet);
1434 psppire_sheet_show_grid (PsppireSheet *sheet, gboolean show)
1436 g_return_if_fail (sheet != NULL);
1437 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1439 if (show == sheet->show_grid) return;
1441 sheet->show_grid = show;
1443 redraw_range (sheet, NULL);
1447 psppire_sheet_grid_visible (PsppireSheet *sheet)
1449 g_return_val_if_fail (sheet != NULL, 0);
1450 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
1452 return sheet->show_grid;
1456 psppire_sheet_get_columns_count (PsppireSheet *sheet)
1458 g_return_val_if_fail (sheet != NULL, 0);
1459 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
1461 return psppire_axis_unit_count (sheet->haxis);
1464 static void set_column_width (PsppireSheet *sheet,
1470 psppire_sheet_show_column_titles (PsppireSheet *sheet)
1472 if (sheet->column_titles_visible) return;
1474 sheet->column_titles_visible = TRUE;
1476 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1479 gdk_window_show (sheet->column_title_window);
1480 gdk_window_move_resize (sheet->column_title_window,
1481 sheet->column_title_area.x,
1482 sheet->column_title_area.y,
1483 sheet->column_title_area.width,
1484 sheet->column_title_area.height);
1486 adjust_scrollbars (sheet);
1488 if (sheet->vadjustment)
1489 g_signal_emit_by_name (sheet->vadjustment,
1492 size_allocate_global_button (sheet);
1494 if ( sheet->row_titles_visible)
1495 gtk_widget_show (sheet->button);
1500 psppire_sheet_show_row_titles (PsppireSheet *sheet)
1502 if (sheet->row_titles_visible) return;
1504 sheet->row_titles_visible = TRUE;
1507 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1509 gdk_window_show (sheet->row_title_window);
1510 gdk_window_move_resize (sheet->row_title_window,
1511 sheet->row_title_area.x,
1512 sheet->row_title_area.y,
1513 sheet->row_title_area.width,
1514 sheet->row_title_area.height);
1516 adjust_scrollbars (sheet);
1519 if (sheet->hadjustment)
1520 g_signal_emit_by_name (sheet->hadjustment,
1523 size_allocate_global_button (sheet);
1525 if ( sheet->column_titles_visible)
1526 gtk_widget_show (sheet->button);
1530 psppire_sheet_hide_column_titles (PsppireSheet *sheet)
1532 if (!sheet->column_titles_visible) return;
1534 sheet->column_titles_visible = FALSE;
1536 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1538 if (sheet->column_title_window)
1539 gdk_window_hide (sheet->column_title_window);
1541 gtk_widget_hide (sheet->button);
1543 adjust_scrollbars (sheet);
1546 if (sheet->vadjustment)
1547 g_signal_emit_by_name (sheet->vadjustment,
1552 psppire_sheet_hide_row_titles (PsppireSheet *sheet)
1554 if (!sheet->row_titles_visible) return;
1556 sheet->row_titles_visible = FALSE;
1558 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1560 if (sheet->row_title_window)
1561 gdk_window_hide (sheet->row_title_window);
1563 gtk_widget_hide (sheet->button);
1565 adjust_scrollbars (sheet);
1568 if (sheet->hadjustment)
1569 g_signal_emit_by_name (sheet->hadjustment,
1574 /* Scroll the sheet so that the cell ROW, COLUMN is visible.
1575 If {ROW,COL}_ALIGN is zero, then the cell will be placed
1576 at the {top,left} of the sheet. If it's 1, then it'll
1577 be placed at the {bottom,right}.
1578 ROW or COL may be -1, in which case scrolling in that dimension
1582 psppire_sheet_moveto (PsppireSheet *sheet,
1590 g_return_if_fail (row_align >= 0);
1591 g_return_if_fail (col_align >= 0);
1593 g_return_if_fail (row_align <= 1);
1594 g_return_if_fail (col_align <= 1);
1596 g_return_if_fail (col <
1597 psppire_axis_unit_count (sheet->haxis));
1598 g_return_if_fail (row <
1599 psppire_axis_unit_count (sheet->vaxis));
1601 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
1606 gint y = psppire_axis_start_pixel (sheet->vaxis, row);
1608 gtk_adjustment_set_value (sheet->vadjustment, y - height * row_align);
1614 gint x = psppire_axis_start_pixel (sheet->haxis, col);
1616 gtk_adjustment_set_value (sheet->hadjustment, x - width * col_align);
1622 psppire_sheet_select_row (PsppireSheet *sheet, gint row)
1624 g_return_if_fail (sheet != NULL);
1625 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1627 if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis))
1630 if (sheet->state != PSPPIRE_SHEET_NORMAL)
1631 psppire_sheet_real_unselect_range (sheet, NULL);
1633 sheet->state = PSPPIRE_SHEET_ROW_SELECTED;
1634 sheet->range.row0 = row;
1635 sheet->range.col0 = 0;
1636 sheet->range.rowi = row;
1637 sheet->range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1639 g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, row);
1640 psppire_sheet_real_select_range (sheet, NULL);
1645 psppire_sheet_select_column (PsppireSheet *sheet, gint column)
1647 g_return_if_fail (sheet != NULL);
1648 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1650 if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis))
1653 if (sheet->state != PSPPIRE_SHEET_NORMAL)
1654 psppire_sheet_real_unselect_range (sheet, NULL);
1656 sheet->state = PSPPIRE_SHEET_COLUMN_SELECTED;
1657 sheet->range.row0 = 0;
1658 sheet->range.col0 = column;
1659 sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1660 sheet->range.coli = column;
1662 g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, column);
1663 psppire_sheet_real_select_range (sheet, NULL);
1670 psppire_sheet_range_isvisible (const PsppireSheet *sheet,
1671 const PsppireSheetRange *range)
1673 g_return_val_if_fail (sheet != NULL, FALSE);
1675 if (range->row0 < 0 || range->row0 >= psppire_axis_unit_count (sheet->vaxis))
1678 if (range->rowi < 0 || range->rowi >= psppire_axis_unit_count (sheet->vaxis))
1681 if (range->col0 < 0 || range->col0 >= psppire_axis_unit_count (sheet->haxis))
1684 if (range->coli < 0 || range->coli >= psppire_axis_unit_count (sheet->haxis))
1687 if (range->rowi < min_visible_row (sheet))
1690 if (range->row0 > max_visible_row (sheet))
1693 if (range->coli < min_visible_column (sheet))
1696 if (range->col0 > max_visible_column (sheet))
1703 psppire_sheet_cell_isvisible (PsppireSheet *sheet,
1704 gint row, gint column)
1706 PsppireSheetRange range;
1709 range.col0 = column;
1711 range.coli = column;
1713 return psppire_sheet_range_isvisible (sheet, &range);
1717 psppire_sheet_get_visible_range (PsppireSheet *sheet, PsppireSheetRange *range)
1719 g_return_if_fail (sheet != NULL);
1720 g_return_if_fail (PSPPIRE_IS_SHEET (sheet)) ;
1721 g_return_if_fail (range != NULL);
1723 range->row0 = min_visible_row (sheet);
1724 range->col0 = min_visible_column (sheet);
1725 range->rowi = max_visible_row (sheet);
1726 range->coli = max_visible_column (sheet);
1731 psppire_sheet_set_scroll_adjustments (PsppireSheet *sheet,
1732 GtkAdjustment *hadjustment,
1733 GtkAdjustment *vadjustment)
1735 if ( sheet->vadjustment != vadjustment )
1737 if (sheet->vadjustment)
1738 g_object_unref (sheet->vadjustment);
1739 sheet->vadjustment = vadjustment;
1743 g_object_ref (vadjustment);
1745 g_signal_connect (sheet->vadjustment, "value_changed",
1746 G_CALLBACK (vadjustment_value_changed),
1751 if ( sheet->hadjustment != hadjustment )
1753 if (sheet->hadjustment)
1754 g_object_unref (sheet->hadjustment);
1756 sheet->hadjustment = hadjustment;
1760 g_object_ref (hadjustment);
1762 g_signal_connect (sheet->hadjustment, "value_changed",
1763 G_CALLBACK (hadjustment_value_changed),
1771 psppire_sheet_finalize (GObject *object)
1773 PsppireSheet *sheet;
1775 g_return_if_fail (object != NULL);
1776 g_return_if_fail (PSPPIRE_IS_SHEET (object));
1778 sheet = PSPPIRE_SHEET (object);
1780 if (G_OBJECT_CLASS (parent_class)->finalize)
1781 (*G_OBJECT_CLASS (parent_class)->finalize) (object);
1785 psppire_sheet_dispose (GObject *object)
1787 PsppireSheet *sheet = PSPPIRE_SHEET (object);
1789 g_return_if_fail (object != NULL);
1790 g_return_if_fail (PSPPIRE_IS_SHEET (object));
1792 if ( sheet->dispose_has_run )
1795 sheet->dispose_has_run = TRUE;
1797 if ( sheet->cell_padding)
1798 g_boxed_free (GTK_TYPE_BORDER, sheet->cell_padding);
1800 if (sheet->model) g_object_unref (sheet->model);
1801 if (sheet->vaxis) g_object_unref (sheet->vaxis);
1802 if (sheet->haxis) g_object_unref (sheet->haxis);
1804 g_object_unref (sheet->button);
1805 sheet->button = NULL;
1807 /* unref adjustments */
1808 if (sheet->hadjustment)
1810 g_signal_handlers_disconnect_matched (sheet->hadjustment,
1811 G_SIGNAL_MATCH_DATA,
1815 g_object_unref (sheet->hadjustment);
1816 sheet->hadjustment = NULL;
1819 if (sheet->vadjustment)
1821 g_signal_handlers_disconnect_matched (sheet->vadjustment,
1822 G_SIGNAL_MATCH_DATA,
1826 g_object_unref (sheet->vadjustment);
1828 sheet->vadjustment = NULL;
1831 if (G_OBJECT_CLASS (parent_class)->dispose)
1832 (*G_OBJECT_CLASS (parent_class)->dispose) (object);
1836 psppire_sheet_style_set (GtkWidget *widget,
1837 GtkStyle *previous_style)
1839 PsppireSheet *sheet;
1841 g_return_if_fail (widget != NULL);
1842 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1844 if (GTK_WIDGET_CLASS (parent_class)->style_set)
1845 (*GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous_style);
1847 sheet = PSPPIRE_SHEET (widget);
1849 if (GTK_WIDGET_REALIZED (widget))
1851 gtk_style_set_background (widget->style, widget->window, widget->state);
1854 set_entry_widget_font (sheet);
1859 psppire_sheet_realize (GtkWidget *widget)
1861 PsppireSheet *sheet;
1862 GdkWindowAttr attributes;
1863 const gint attributes_mask =
1864 GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_CURSOR;
1867 GdkColormap *colormap;
1868 GdkDisplay *display;
1870 g_return_if_fail (widget != NULL);
1871 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1873 sheet = PSPPIRE_SHEET (widget);
1875 colormap = gtk_widget_get_colormap (widget);
1876 display = gtk_widget_get_display (widget);
1878 attributes.window_type = GDK_WINDOW_CHILD;
1879 attributes.x = widget->allocation.x;
1880 attributes.y = widget->allocation.y;
1881 attributes.width = widget->allocation.width;
1882 attributes.height = widget->allocation.height;
1883 attributes.wclass = GDK_INPUT_OUTPUT;
1885 attributes.visual = gtk_widget_get_visual (widget);
1886 attributes.colormap = colormap;
1888 attributes.event_mask = gtk_widget_get_events (widget);
1889 attributes.event_mask |= (GDK_EXPOSURE_MASK |
1890 GDK_BUTTON_PRESS_MASK |
1891 GDK_BUTTON_RELEASE_MASK |
1892 GDK_KEY_PRESS_MASK |
1893 GDK_ENTER_NOTIFY_MASK |
1894 GDK_LEAVE_NOTIFY_MASK |
1895 GDK_POINTER_MOTION_MASK |
1896 GDK_POINTER_MOTION_HINT_MASK);
1898 attributes.cursor = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
1901 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1903 gdk_window_set_user_data (widget->window, sheet);
1905 widget->style = gtk_style_attach (widget->style, widget->window);
1907 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1909 gdk_color_parse ("white", &sheet->color[BG_COLOR]);
1910 gdk_colormap_alloc_color (colormap, &sheet->color[BG_COLOR], FALSE,
1912 gdk_color_parse ("gray", &sheet->color[GRID_COLOR]);
1913 gdk_colormap_alloc_color (colormap, &sheet->color[GRID_COLOR], FALSE,
1918 attributes.width = sheet->column_title_area.width;
1919 attributes.height = sheet->column_title_area.height;
1922 /* column - title window */
1923 sheet->column_title_window =
1924 gdk_window_new (widget->window, &attributes, attributes_mask);
1925 gdk_window_set_user_data (sheet->column_title_window, sheet);
1926 gtk_style_set_background (widget->style, sheet->column_title_window,
1932 attributes.width = sheet->row_title_area.width;
1933 attributes.height = sheet->row_title_area.height;
1935 /* row - title window */
1936 sheet->row_title_window = gdk_window_new (widget->window,
1937 &attributes, attributes_mask);
1938 gdk_window_set_user_data (sheet->row_title_window, sheet);
1939 gtk_style_set_background (widget->style, sheet->row_title_window,
1942 /* sheet - window */
1943 attributes.cursor = gdk_cursor_new_for_display (display, GDK_PLUS);
1948 sheet->sheet_window = gdk_window_new (widget->window,
1949 &attributes, attributes_mask);
1950 gdk_window_set_user_data (sheet->sheet_window, sheet);
1952 gdk_cursor_unref (attributes.cursor);
1954 gdk_window_set_background (sheet->sheet_window, &widget->style->white);
1955 gdk_window_show (sheet->sheet_window);
1958 sheet->fg_gc = gdk_gc_new (widget->window);
1959 sheet->bg_gc = gdk_gc_new (widget->window);
1961 values.foreground = widget->style->white;
1962 values.function = GDK_INVERT;
1963 values.subwindow_mode = GDK_INCLUDE_INFERIORS;
1964 values.line_width = MAX (sheet->cell_padding->left,
1965 MAX (sheet->cell_padding->right,
1966 MAX (sheet->cell_padding->top,
1967 sheet->cell_padding->bottom)));
1969 sheet->xor_gc = gdk_gc_new_with_values (widget->window,
1977 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
1978 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
1980 gtk_widget_set_parent_window (sheet->button, sheet->sheet_window);
1981 gtk_widget_set_parent (sheet->button, GTK_WIDGET (sheet));
1983 sheet->button->style = gtk_style_attach (sheet->button->style,
1984 sheet->sheet_window);
1987 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
1989 if (sheet->column_titles_visible)
1990 gdk_window_show (sheet->column_title_window);
1991 if (sheet->row_titles_visible)
1992 gdk_window_show (sheet->row_title_window);
1994 sheet->hover_window = create_hover_window ();
1996 draw_row_title_buttons (sheet);
1997 draw_column_title_buttons (sheet);
1999 psppire_sheet_update_primary_selection (sheet);
2002 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
2006 create_global_button (PsppireSheet *sheet)
2008 sheet->button = gtk_button_new_with_label (" ");
2010 GTK_WIDGET_UNSET_FLAGS(sheet->button, GTK_CAN_FOCUS);
2012 g_object_ref_sink (sheet->button);
2014 g_signal_connect (sheet->button,
2016 G_CALLBACK (global_button_clicked),
2021 size_allocate_global_button (PsppireSheet *sheet)
2023 GtkAllocation allocation;
2025 if (!sheet->column_titles_visible) return;
2026 if (!sheet->row_titles_visible) return;
2028 gtk_widget_size_request (sheet->button, NULL);
2032 allocation.width = sheet->row_title_area.width;
2033 allocation.height = sheet->column_title_area.height;
2035 gtk_widget_size_allocate (sheet->button, &allocation);
2039 global_button_clicked (GtkWidget *widget, gpointer data)
2041 psppire_sheet_click_cell (PSPPIRE_SHEET (data), -1, -1);
2046 psppire_sheet_unrealize (GtkWidget *widget)
2048 PsppireSheet *sheet;
2050 g_return_if_fail (widget != NULL);
2051 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
2053 sheet = PSPPIRE_SHEET (widget);
2055 gdk_cursor_unref (sheet->cursor_drag);
2056 sheet->cursor_drag = NULL;
2058 gdk_colormap_free_colors (gtk_widget_get_colormap (widget),
2059 sheet->color, n_COLORS);
2061 g_object_unref (sheet->xor_gc);
2062 g_object_unref (sheet->fg_gc);
2063 g_object_unref (sheet->bg_gc);
2065 destroy_hover_window (sheet->hover_window);
2067 gdk_window_destroy (sheet->sheet_window);
2068 gdk_window_destroy (sheet->column_title_window);
2069 gdk_window_destroy (sheet->row_title_window);
2071 gtk_widget_unparent (sheet->entry_widget);
2072 if (sheet->button != NULL)
2073 gtk_widget_unparent (sheet->button);
2075 if (GTK_WIDGET_CLASS (parent_class)->unrealize)
2076 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
2080 psppire_sheet_map (GtkWidget *widget)
2082 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2084 g_return_if_fail (widget != NULL);
2085 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
2087 if (!GTK_WIDGET_MAPPED (widget))
2089 GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
2091 gdk_window_show (widget->window);
2092 gdk_window_show (sheet->sheet_window);
2094 if (sheet->column_titles_visible)
2096 draw_column_title_buttons (sheet);
2097 gdk_window_show (sheet->column_title_window);
2099 if (sheet->row_titles_visible)
2101 draw_row_title_buttons (sheet);
2102 gdk_window_show (sheet->row_title_window);
2105 if (!GTK_WIDGET_MAPPED (sheet->entry_widget)
2106 && sheet->active_cell.row >= 0
2107 && sheet->active_cell.col >= 0 )
2109 gtk_widget_show (sheet->entry_widget);
2110 gtk_widget_map (sheet->entry_widget);
2113 if (!GTK_WIDGET_MAPPED (sheet->button))
2115 gtk_widget_show (sheet->button);
2116 gtk_widget_map (sheet->button);
2119 redraw_range (sheet, NULL);
2120 change_active_cell (sheet,
2121 sheet->active_cell.row,
2122 sheet->active_cell.col);
2127 psppire_sheet_unmap (GtkWidget *widget)
2129 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2131 if (!GTK_WIDGET_MAPPED (widget))
2134 GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
2136 gdk_window_hide (sheet->sheet_window);
2137 if (sheet->column_titles_visible)
2138 gdk_window_hide (sheet->column_title_window);
2139 if (sheet->row_titles_visible)
2140 gdk_window_hide (sheet->row_title_window);
2141 gdk_window_hide (widget->window);
2143 gtk_widget_unmap (sheet->entry_widget);
2144 gtk_widget_unmap (sheet->button);
2145 gtk_widget_unmap (sheet->hover_window->window);
2148 /* get cell attributes of the given cell */
2149 /* TRUE means that the cell is currently allocated */
2150 static gboolean psppire_sheet_get_attributes (const PsppireSheet *sheet,
2152 PsppireSheetCellAttr *attributes);
2157 psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint col)
2159 PangoLayout *layout;
2160 PangoRectangle text;
2161 PangoFontDescription *font_desc = GTK_WIDGET (sheet)->style->font_desc;
2166 PsppireSheetCellAttr attributes;
2169 g_return_if_fail (sheet != NULL);
2171 /* bail now if we aren't yet drawable */
2172 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
2175 row >= psppire_axis_unit_count (sheet->vaxis))
2179 col >= psppire_axis_unit_count (sheet->haxis))
2182 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2184 /* select GC for background rectangle */
2185 gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground);
2186 gdk_gc_set_foreground (sheet->bg_gc, &attributes.background);
2188 rectangle_from_cell (sheet, row, col, &area);
2190 gdk_gc_set_line_attributes (sheet->fg_gc, 1, 0, 0, 0);
2192 if (sheet->show_grid)
2194 gdk_gc_set_foreground (sheet->bg_gc, &sheet->color[GRID_COLOR]);
2196 gdk_draw_rectangle (sheet->sheet_window,
2200 area.width, area.height);
2204 label = psppire_sheet_cell_get_text (sheet, row, col);
2209 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), label);
2210 dispose_string (sheet, label);
2213 pango_layout_set_font_description (layout, font_desc);
2215 pango_layout_get_pixel_extents (layout, NULL, &text);
2217 gdk_gc_set_clip_rectangle (sheet->fg_gc, &area);
2219 font_height = pango_font_description_get_size (font_desc);
2220 if ( !pango_font_description_get_size_is_absolute (font_desc))
2221 font_height /= PANGO_SCALE;
2224 if ( sheet->cell_padding )
2226 area.x += sheet->cell_padding->left;
2227 area.width -= sheet->cell_padding->right
2228 + sheet->cell_padding->left;
2230 area.y += sheet->cell_padding->top;
2231 area.height -= sheet->cell_padding->bottom
2233 sheet->cell_padding->top;
2236 /* Centre the text vertically */
2237 area.y += (area.height - font_height) / 2.0;
2239 switch (attributes.justification)
2241 case GTK_JUSTIFY_RIGHT:
2242 area.x += area.width - text.width;
2244 case GTK_JUSTIFY_CENTER:
2245 area.x += (area.width - text.width) / 2.0;
2247 case GTK_JUSTIFY_LEFT:
2251 g_critical ("Unhandled justification %d in column %d\n",
2252 attributes.justification, col);
2256 gdk_draw_layout (sheet->sheet_window, sheet->fg_gc,
2261 gdk_gc_set_clip_rectangle (sheet->fg_gc, NULL);
2262 g_object_unref (layout);
2267 draw_sheet_region (PsppireSheet *sheet, GdkRegion *region)
2269 PsppireSheetRange range;
2274 PsppireSheetRange drawing_range;
2276 gdk_region_get_clipbox (region, &area);
2278 y = area.y + sheet->vadjustment->value;
2279 x = area.x + sheet->hadjustment->value;
2281 if ( sheet->column_titles_visible)
2282 y -= sheet->column_title_area.height;
2284 if ( sheet->row_titles_visible)
2285 x -= sheet->row_title_area.width;
2287 maximize_int (&x, 0);
2288 maximize_int (&y, 0);
2290 range.row0 = row_from_ypixel (sheet, y);
2291 range.rowi = row_from_ypixel (sheet, y + area.height);
2293 range.col0 = column_from_xpixel (sheet, x);
2294 range.coli = column_from_xpixel (sheet, x + area.width);
2296 g_return_if_fail (sheet != NULL);
2297 g_return_if_fail (PSPPIRE_SHEET (sheet));
2299 if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (sheet))) return;
2300 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2301 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
2304 drawing_range.row0 = MAX (range.row0, min_visible_row (sheet));
2305 drawing_range.col0 = MAX (range.col0, min_visible_column (sheet));
2306 drawing_range.rowi = MIN (range.rowi, max_visible_row (sheet));
2307 drawing_range.coli = MIN (range.coli, max_visible_column (sheet));
2309 g_return_if_fail (drawing_range.rowi >= drawing_range.row0);
2310 g_return_if_fail (drawing_range.coli >= drawing_range.col0);
2312 for (i = drawing_range.row0; i <= drawing_range.rowi; i++)
2314 for (j = drawing_range.col0; j <= drawing_range.coli; j++)
2315 psppire_sheet_cell_draw (sheet, i, j);
2318 if (sheet->state != PSPPIRE_SHEET_NORMAL &&
2319 psppire_sheet_range_isvisible (sheet, &sheet->range))
2320 psppire_sheet_range_draw_selection (sheet, drawing_range);
2323 if (sheet->state == GTK_STATE_NORMAL &&
2324 sheet->active_cell.row >= drawing_range.row0 &&
2325 sheet->active_cell.row <= drawing_range.rowi &&
2326 sheet->active_cell.col >= drawing_range.col0 &&
2327 sheet->active_cell.col <= drawing_range.coli)
2328 psppire_sheet_show_entry_widget (sheet);
2333 psppire_sheet_range_draw_selection (PsppireSheet *sheet, PsppireSheetRange range)
2337 PsppireSheetRange aux;
2339 if (range.col0 > sheet->range.coli || range.coli < sheet->range.col0 ||
2340 range.row0 > sheet->range.rowi || range.rowi < sheet->range.row0)
2343 if (!psppire_sheet_range_isvisible (sheet, &range)) return;
2344 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2348 range.col0 = MAX (sheet->range.col0, range.col0);
2349 range.coli = MIN (sheet->range.coli, range.coli);
2350 range.row0 = MAX (sheet->range.row0, range.row0);
2351 range.rowi = MIN (sheet->range.rowi, range.rowi);
2353 range.col0 = MAX (range.col0, min_visible_column (sheet));
2354 range.coli = MIN (range.coli, max_visible_column (sheet));
2355 range.row0 = MAX (range.row0, min_visible_row (sheet));
2356 range.rowi = MIN (range.rowi, max_visible_row (sheet));
2358 for (i = range.row0; i <= range.rowi; i++)
2360 for (j = range.col0; j <= range.coli; j++)
2362 if (psppire_sheet_cell_get_state (sheet, i, j) == GTK_STATE_SELECTED)
2364 rectangle_from_cell (sheet, i, j, &area);
2366 if (i == sheet->range.row0)
2368 area.y = area.y + 2;
2369 area.height = area.height - 2;
2371 if (i == sheet->range.rowi) area.height = area.height - 3;
2372 if (j == sheet->range.col0)
2374 area.x = area.x + 2;
2375 area.width = area.width - 2;
2377 if (j == sheet->range.coli) area.width = area.width - 3;
2379 if (i != sheet->active_cell.row || j != sheet->active_cell.col)
2381 gdk_draw_rectangle (sheet->sheet_window,
2384 area.x + 1, area.y + 1,
2385 area.width, area.height);
2392 psppire_sheet_draw_border (sheet, sheet->range);
2396 safe_strcmp (const gchar *s1, const gchar *s2)
2398 if ( !s1 && !s2) return 0;
2399 if ( !s1) return -1;
2400 if ( !s2) return +1;
2401 return strcmp (s1, s2);
2405 psppire_sheet_set_cell (PsppireSheet *sheet, gint row, gint col,
2406 GtkJustification justification,
2409 PsppireSheetModel *model ;
2412 g_return_if_fail (sheet != NULL);
2413 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2415 if (col >= psppire_axis_unit_count (sheet->haxis)
2416 || row >= psppire_axis_unit_count (sheet->vaxis))
2419 if (col < 0 || row < 0) return;
2421 model = psppire_sheet_get_model (sheet);
2423 old_text = psppire_sheet_model_get_string (model, row, col);
2425 if (0 != safe_strcmp (old_text, text))
2427 g_signal_handler_block (sheet->model, sheet->update_handler_id);
2428 psppire_sheet_model_set_string (model, text, row, col);
2429 g_signal_handler_unblock (sheet->model, sheet->update_handler_id);
2432 if ( psppire_sheet_model_free_strings (model))
2438 psppire_sheet_cell_clear (PsppireSheet *sheet, gint row, gint column)
2440 PsppireSheetRange range;
2442 g_return_if_fail (sheet != NULL);
2443 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2444 if (column >= psppire_axis_unit_count (sheet->haxis) ||
2445 row >= psppire_axis_unit_count (sheet->vaxis)) return;
2447 if (column < 0 || row < 0) return;
2451 range.col0 = min_visible_column (sheet);
2452 range.coli = max_visible_column (sheet);
2454 psppire_sheet_real_cell_clear (sheet, row, column);
2456 redraw_range (sheet, &range);
2460 psppire_sheet_real_cell_clear (PsppireSheet *sheet, gint row, gint column)
2462 PsppireSheetModel *model = psppire_sheet_get_model (sheet);
2464 gchar *old_text = psppire_sheet_cell_get_text (sheet, row, column);
2466 if (old_text && strlen (old_text) > 0 )
2468 psppire_sheet_model_datum_clear (model, row, column);
2471 dispose_string (sheet, old_text);
2475 psppire_sheet_cell_get_text (const PsppireSheet *sheet, gint row, gint col)
2477 PsppireSheetModel *model;
2478 g_return_val_if_fail (sheet != NULL, NULL);
2479 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
2481 if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis))
2483 if (col < 0 || row < 0) return NULL;
2485 model = psppire_sheet_get_model (sheet);
2490 return psppire_sheet_model_get_string (model, row, col);
2495 psppire_sheet_cell_get_state (PsppireSheet *sheet, gint row, gint col)
2498 PsppireSheetRange *range;
2500 g_return_val_if_fail (sheet != NULL, 0);
2501 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2502 if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis)) return 0;
2503 if (col < 0 || row < 0) return 0;
2505 state = sheet->state;
2506 range = &sheet->range;
2510 case PSPPIRE_SHEET_NORMAL:
2511 return GTK_STATE_NORMAL;
2513 case PSPPIRE_SHEET_ROW_SELECTED:
2514 if (row >= range->row0 && row <= range->rowi)
2515 return GTK_STATE_SELECTED;
2517 case PSPPIRE_SHEET_COLUMN_SELECTED:
2518 if (col >= range->col0 && col <= range->coli)
2519 return GTK_STATE_SELECTED;
2521 case PSPPIRE_SHEET_RANGE_SELECTED:
2522 if (row >= range->row0 && row <= range->rowi && \
2523 col >= range->col0 && col <= range->coli)
2524 return GTK_STATE_SELECTED;
2527 return GTK_STATE_NORMAL;
2530 /* Convert X, Y (in pixels) to *ROW, *COLUMN
2531 If the function returns FALSE, then the results will be unreliable.
2534 psppire_sheet_get_pixel_info (PsppireSheet *sheet,
2542 *column = -G_MAXINT;
2544 g_return_val_if_fail (sheet != NULL, 0);
2545 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2547 /* bounds checking, return false if the user clicked
2555 if ( sheet->column_titles_visible)
2556 y -= sheet->column_title_area.height;
2558 y += sheet->vadjustment->value;
2560 if ( y < 0 && sheet->column_titles_visible)
2566 trow = row_from_ypixel (sheet, y);
2567 if (trow > psppire_axis_unit_count (sheet->vaxis))
2573 if ( sheet->row_titles_visible)
2574 x -= sheet->row_title_area.width;
2576 x += sheet->hadjustment->value;
2578 if ( x < 0 && sheet->row_titles_visible)
2584 tcol = column_from_xpixel (sheet, x);
2585 if (tcol > psppire_axis_unit_count (sheet->haxis))
2595 psppire_sheet_get_cell_area (PsppireSheet *sheet,
2600 g_return_val_if_fail (sheet != NULL, 0);
2601 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2603 if (row >= psppire_axis_unit_count (sheet->vaxis) || column >= psppire_axis_unit_count (sheet->haxis))
2606 area->x = (column == -1) ? 0 : psppire_axis_start_pixel (sheet->haxis, column);
2607 area->y = (row == -1) ? 0 : psppire_axis_start_pixel (sheet->vaxis, row);
2609 area->width= (column == -1) ? sheet->row_title_area.width
2610 : psppire_axis_unit_size (sheet->haxis, column);
2612 area->height= (row == -1) ? sheet->column_title_area.height
2613 : psppire_axis_unit_size (sheet->vaxis, row);
2619 psppire_sheet_set_active_cell (PsppireSheet *sheet, gint row, gint col)
2621 g_return_if_fail (sheet != NULL);
2622 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2624 if (row < -1 || col < -1)
2627 if (row >= psppire_axis_unit_count (sheet->vaxis)
2629 col >= psppire_axis_unit_count (sheet->haxis))
2632 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2635 if ( row == -1 || col == -1)
2637 psppire_sheet_hide_entry_widget (sheet);
2641 change_active_cell (sheet, row, col);
2645 psppire_sheet_get_active_cell (PsppireSheet *sheet, gint *row, gint *column)
2647 g_return_if_fail (sheet != NULL);
2648 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2650 if ( row ) *row = sheet->active_cell.row;
2651 if (column) *column = sheet->active_cell.col;
2655 entry_load_text (PsppireSheet *sheet)
2659 GtkJustification justification;
2660 PsppireSheetCellAttr attributes;
2662 if (!GTK_WIDGET_VISIBLE (sheet->entry_widget)) return;
2663 if (sheet->state != GTK_STATE_NORMAL) return;
2665 row = sheet->active_cell.row;
2666 col = sheet->active_cell.col;
2668 if (row < 0 || col < 0) return;
2670 text = gtk_entry_get_text (psppire_sheet_get_entry (sheet));
2672 if (text && strlen (text) > 0)
2674 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2675 justification = attributes.justification;
2676 psppire_sheet_set_cell (sheet, row, col, justification, text);
2682 psppire_sheet_hide_entry_widget (PsppireSheet *sheet)
2684 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2687 if (sheet->active_cell.row < 0 ||
2688 sheet->active_cell.col < 0) return;
2690 gtk_widget_hide (sheet->entry_widget);
2691 gtk_widget_unmap (sheet->entry_widget);
2693 GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2697 change_active_cell (PsppireSheet *sheet, gint row, gint col)
2699 gint old_row, old_col;
2701 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2703 if (row < 0 || col < 0)
2706 if ( row > psppire_axis_unit_count (sheet->vaxis)
2707 || col > psppire_axis_unit_count (sheet->haxis))
2710 if (sheet->state != PSPPIRE_SHEET_NORMAL)
2712 sheet->state = PSPPIRE_SHEET_NORMAL;
2713 psppire_sheet_real_unselect_range (sheet, NULL);
2716 old_row = sheet->active_cell.row;
2717 old_col = sheet->active_cell.col;
2719 entry_load_text (sheet);
2721 /* Erase the old cell border */
2722 psppire_sheet_draw_active_cell (sheet);
2724 sheet->range.row0 = row;
2725 sheet->range.col0 = col;
2726 sheet->range.rowi = row;
2727 sheet->range.coli = col;
2728 sheet->active_cell.row = row;
2729 sheet->active_cell.col = col;
2730 sheet->selection_cell.row = row;
2731 sheet->selection_cell.col = col;
2733 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
2735 GTK_WIDGET_UNSET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2737 psppire_sheet_draw_active_cell (sheet);
2738 psppire_sheet_show_entry_widget (sheet);
2740 GTK_WIDGET_SET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2742 g_signal_emit (sheet, sheet_signals [ACTIVATE], 0,
2743 row, col, old_row, old_col);
2748 psppire_sheet_show_entry_widget (PsppireSheet *sheet)
2750 GtkEntry *sheet_entry;
2751 PsppireSheetCellAttr attributes;
2755 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2757 row = sheet->active_cell.row;
2758 col = sheet->active_cell.col;
2760 /* Don't show the active cell, if there is no active cell: */
2761 if (! (row >= 0 && col >= 0)) /* e.g row or coll == -1. */
2764 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2765 if (sheet->state != PSPPIRE_SHEET_NORMAL) return;
2766 if (PSPPIRE_SHEET_IN_SELECTION (sheet)) return;
2768 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2770 sheet_entry = psppire_sheet_get_entry (sheet);
2772 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2774 if (GTK_IS_ENTRY (sheet_entry))
2776 gchar *text = psppire_sheet_cell_get_text (sheet, row, col);
2777 const gchar *old_text = gtk_entry_get_text (GTK_ENTRY (sheet_entry));
2780 text = g_strdup ("");
2782 if (strcmp (old_text, text) != 0)
2783 gtk_entry_set_text (sheet_entry, text);
2785 dispose_string (sheet, text);
2788 switch (attributes.justification)
2790 case GTK_JUSTIFY_RIGHT:
2791 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 1.0);
2793 case GTK_JUSTIFY_CENTER:
2794 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.5);
2796 case GTK_JUSTIFY_LEFT:
2798 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.0);
2804 psppire_sheet_size_allocate_entry (sheet);
2806 gtk_widget_set_sensitive (GTK_WIDGET (sheet_entry),
2807 psppire_sheet_model_is_editable (sheet->model,
2809 gtk_widget_map (sheet->entry_widget);
2813 psppire_sheet_draw_active_cell (PsppireSheet *sheet)
2816 PsppireSheetRange range;
2818 row = sheet->active_cell.row;
2819 col = sheet->active_cell.col;
2821 if (row < 0 || col < 0) return FALSE;
2823 if (!psppire_sheet_cell_isvisible (sheet, row, col))
2826 range.col0 = range.coli = col;
2827 range.row0 = range.rowi = row;
2829 psppire_sheet_draw_border (sheet, range);
2837 psppire_sheet_new_selection (PsppireSheet *sheet, PsppireSheetRange *range)
2839 gint i, j, mask1, mask2;
2840 gint state, selected;
2841 gint x, y, width, height;
2842 PsppireSheetRange new_range, aux_range;
2844 g_return_if_fail (sheet != NULL);
2846 if (range == NULL) range=&sheet->range;
2850 range->row0 = MIN (range->row0, sheet->range.row0);
2851 range->rowi = MAX (range->rowi, sheet->range.rowi);
2852 range->col0 = MIN (range->col0, sheet->range.col0);
2853 range->coli = MAX (range->coli, sheet->range.coli);
2855 range->row0 = MAX (range->row0, min_visible_row (sheet));
2856 range->rowi = MIN (range->rowi, max_visible_row (sheet));
2857 range->col0 = MAX (range->col0, min_visible_column (sheet));
2858 range->coli = MIN (range->coli, max_visible_column (sheet));
2860 aux_range.row0 = MAX (new_range.row0, min_visible_row (sheet));
2861 aux_range.rowi = MIN (new_range.rowi, max_visible_row (sheet));
2862 aux_range.col0 = MAX (new_range.col0, min_visible_column (sheet));
2863 aux_range.coli = MIN (new_range.coli, max_visible_column (sheet));
2865 for (i = range->row0; i <= range->rowi; i++)
2867 for (j = range->col0; j <= range->coli; j++)
2870 state = psppire_sheet_cell_get_state (sheet, i, j);
2871 selected= (i <= new_range.rowi && i >= new_range.row0 &&
2872 j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
2874 if (state == GTK_STATE_SELECTED && selected &&
2875 (i == sheet->range.row0 || i == sheet->range.rowi ||
2876 j == sheet->range.col0 || j == sheet->range.coli ||
2877 i == new_range.row0 || i == new_range.rowi ||
2878 j == new_range.col0 || j == new_range.coli))
2881 mask1 = i == sheet->range.row0 ? 1 : 0;
2882 mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1;
2883 mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1;
2884 mask1 = j == sheet->range.coli ? mask1 + 8 : mask1;
2886 mask2 = i == new_range.row0 ? 1 : 0;
2887 mask2 = i == new_range.rowi ? mask2 + 2 : mask2;
2888 mask2 = j == new_range.col0 ? mask2 + 4 : mask2;
2889 mask2 = j == new_range.coli ? mask2 + 8 : mask2;
2893 x = psppire_axis_start_pixel (sheet->haxis, j);
2894 y = psppire_axis_start_pixel (sheet->vaxis, i);
2895 width = psppire_axis_start_pixel (sheet->haxis, j)- x+
2896 psppire_axis_unit_size (sheet->haxis, j);
2897 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
2899 if (i == sheet->range.row0)
2902 height = height + 3;
2904 if (i == sheet->range.rowi) height = height + 3;
2905 if (j == sheet->range.col0)
2910 if (j == sheet->range.coli) width = width + 3;
2912 if (i != sheet->active_cell.row || j != sheet->active_cell.col)
2914 x = psppire_axis_start_pixel (sheet->haxis, j);
2915 y = psppire_axis_start_pixel (sheet->vaxis, i);
2916 width = psppire_axis_start_pixel (sheet->haxis, j)- x+
2917 psppire_axis_unit_size (sheet->haxis, j);
2919 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
2921 if (i == new_range.row0)
2924 height = height - 2;
2926 if (i == new_range.rowi) height = height - 3;
2927 if (j == new_range.col0)
2932 if (j == new_range.coli) width = width - 3;
2934 gdk_draw_rectangle (sheet->sheet_window,
2945 for (i = range->row0; i <= range->rowi; i++)
2947 for (j = range->col0; j <= range->coli; j++)
2950 state = psppire_sheet_cell_get_state (sheet, i, j);
2951 selected= (i <= new_range.rowi && i >= new_range.row0 &&
2952 j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
2954 if (state == GTK_STATE_SELECTED && !selected)
2957 x = psppire_axis_start_pixel (sheet->haxis, j);
2958 y = psppire_axis_start_pixel (sheet->vaxis, i);
2959 width = psppire_axis_start_pixel (sheet->haxis, j) - x + psppire_axis_unit_size (sheet->haxis, j);
2960 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
2962 if (i == sheet->range.row0)
2965 height = height + 3;
2967 if (i == sheet->range.rowi) height = height + 3;
2968 if (j == sheet->range.col0)
2973 if (j == sheet->range.coli) width = width + 3;
2979 for (i = range->row0; i <= range->rowi; i++)
2981 for (j = range->col0; j <= range->coli; j++)
2984 state = psppire_sheet_cell_get_state (sheet, i, j);
2985 selected= (i <= new_range.rowi && i >= new_range.row0 &&
2986 j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
2988 if (state != GTK_STATE_SELECTED && selected &&
2989 (i != sheet->active_cell.row || j != sheet->active_cell.col))
2992 x = psppire_axis_start_pixel (sheet->haxis, j);
2993 y = psppire_axis_start_pixel (sheet->vaxis, i);
2994 width = psppire_axis_start_pixel (sheet->haxis, j) - x + psppire_axis_unit_size (sheet->haxis, j);
2995 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
2997 if (i == new_range.row0)
3000 height = height - 2;
3002 if (i == new_range.rowi) height = height - 3;
3003 if (j == new_range.col0)
3008 if (j == new_range.coli) width = width - 3;
3010 gdk_draw_rectangle (sheet->sheet_window,
3021 for (i = aux_range.row0; i <= aux_range.rowi; i++)
3023 for (j = aux_range.col0; j <= aux_range.coli; j++)
3025 state = psppire_sheet_cell_get_state (sheet, i, j);
3027 mask1 = i == sheet->range.row0 ? 1 : 0;
3028 mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1;
3029 mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1;
3030 mask1 = j == sheet->range.coli ? mask1 + 8 : mask1;
3032 mask2 = i == new_range.row0 ? 1 : 0;
3033 mask2 = i == new_range.rowi ? mask2 + 2 : mask2;
3034 mask2 = j == new_range.col0 ? mask2 + 4 : mask2;
3035 mask2 = j == new_range.coli ? mask2 + 8 : mask2;
3036 if (mask2 != mask1 || (mask2 == mask1 && state != GTK_STATE_SELECTED))
3038 x = psppire_axis_start_pixel (sheet->haxis, j);
3039 y = psppire_axis_start_pixel (sheet->vaxis, i);
3040 width = psppire_axis_unit_size (sheet->haxis, j);
3041 height = psppire_axis_unit_size (sheet->vaxis, i);
3043 gdk_draw_rectangle (sheet->sheet_window,
3051 gdk_draw_rectangle (sheet->sheet_window,
3054 x + 1, y + height - 1,
3058 gdk_draw_rectangle (sheet->sheet_window,
3066 gdk_draw_rectangle (sheet->sheet_window,
3069 x + width - 1, y + 1,
3081 psppire_sheet_draw_border (PsppireSheet *sheet, PsppireSheetRange new_range)
3085 rectangle_from_range (sheet, &new_range, &area);
3090 gdk_gc_set_clip_rectangle (sheet->xor_gc, &area);
3092 area.x += sheet->cell_padding->left / 2;
3093 area.y += sheet->cell_padding->top / 2;
3094 area.width -= (sheet->cell_padding->left + sheet->cell_padding->right ) / 2;
3095 area.height -= (sheet->cell_padding->top + sheet->cell_padding->bottom ) / 2;
3097 gdk_draw_rectangle (sheet->sheet_window, sheet->xor_gc,
3104 gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
3109 psppire_sheet_real_select_range (PsppireSheet *sheet,
3110 const PsppireSheetRange *range)
3114 g_return_if_fail (sheet != NULL);
3116 if (range == NULL) range = &sheet->range;
3118 memcpy (&sheet->range, range, sizeof (*range));
3120 if (range->row0 < 0 || range->rowi < 0) return;
3121 if (range->col0 < 0 || range->coli < 0) return;
3123 state = sheet->state;
3126 if (range->coli != sheet->range.coli || range->col0 != sheet->range.col0 ||
3127 range->rowi != sheet->range.rowi || range->row0 != sheet->range.row0)
3129 psppire_sheet_new_selection (sheet, &sheet->range);
3133 psppire_sheet_range_draw_selection (sheet, sheet->range);
3137 psppire_sheet_update_primary_selection (sheet);
3139 g_signal_emit (sheet, sheet_signals[SELECT_RANGE], 0, &sheet->range);
3144 psppire_sheet_get_selected_range (PsppireSheet *sheet, PsppireSheetRange *range)
3146 g_return_if_fail (sheet != NULL);
3147 *range = sheet->range;
3152 psppire_sheet_select_range (PsppireSheet *sheet, const PsppireSheetRange *range)
3154 g_return_if_fail (sheet != NULL);
3156 if (range == NULL) range=&sheet->range;
3158 if (range->row0 < 0 || range->rowi < 0) return;
3159 if (range->col0 < 0 || range->coli < 0) return;
3162 if (sheet->state != PSPPIRE_SHEET_NORMAL)
3163 psppire_sheet_real_unselect_range (sheet, NULL);
3165 sheet->range.row0 = range->row0;
3166 sheet->range.rowi = range->rowi;
3167 sheet->range.col0 = range->col0;
3168 sheet->range.coli = range->coli;
3169 sheet->selection_cell.row = range->rowi;
3170 sheet->selection_cell.col = range->coli;
3172 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
3173 psppire_sheet_real_select_range (sheet, NULL);
3177 psppire_sheet_unselect_range (PsppireSheet *sheet)
3179 if (! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
3182 psppire_sheet_real_unselect_range (sheet, NULL);
3183 sheet->state = GTK_STATE_NORMAL;
3185 change_active_cell (sheet,
3186 sheet->active_cell.row, sheet->active_cell.col);
3191 psppire_sheet_real_unselect_range (PsppireSheet *sheet,
3192 const PsppireSheetRange *range)
3194 g_return_if_fail (sheet != NULL);
3195 g_return_if_fail (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)));
3198 range = &sheet->range;
3200 if (range->row0 < 0 || range->rowi < 0) return;
3201 if (range->col0 < 0 || range->coli < 0) return;
3203 g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, -1);
3204 g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, -1);
3206 sheet->range.row0 = -1;
3207 sheet->range.rowi = -1;
3208 sheet->range.col0 = -1;
3209 sheet->range.coli = -1;
3214 psppire_sheet_expose (GtkWidget *widget,
3215 GdkEventExpose *event)
3217 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3219 g_return_val_if_fail (event != NULL, FALSE);
3221 if (!GTK_WIDGET_DRAWABLE (widget))
3224 /* exposure events on the sheet */
3225 if (event->window == sheet->row_title_window &&
3226 sheet->row_titles_visible)
3228 draw_row_title_buttons_range (sheet,
3229 min_visible_row (sheet),
3230 max_visible_row (sheet));
3233 if (event->window == sheet->column_title_window &&
3234 sheet->column_titles_visible)
3236 draw_column_title_buttons_range (sheet,
3237 min_visible_column (sheet),
3238 max_visible_column (sheet));
3241 if (event->window == sheet->sheet_window)
3243 draw_sheet_region (sheet, event->region);
3246 if (sheet->state != PSPPIRE_SHEET_NORMAL)
3248 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
3249 psppire_sheet_range_draw (sheet, &sheet->range);
3251 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
3252 psppire_sheet_range_draw (sheet, &sheet->drag_range);
3254 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
3255 psppire_sheet_range_draw_selection (sheet, sheet->range);
3256 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
3257 draw_xor_rectangle (sheet, sheet->drag_range);
3261 if ((!PSPPIRE_SHEET_IN_XDRAG (sheet)) && (!PSPPIRE_SHEET_IN_YDRAG (sheet)))
3264 PsppireSheetRange range;
3265 range.row0 = range.rowi = sheet->active_cell.row;
3266 range.col0 = range.coli = sheet->active_cell.col;
3268 rectangle_from_range (sheet, &range, &rect);
3270 if (GDK_OVERLAP_RECTANGLE_OUT !=
3271 gdk_region_rect_in (event->region, &rect))
3273 psppire_sheet_draw_active_cell (sheet);
3279 (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
3286 psppire_sheet_button_press (GtkWidget *widget,
3287 GdkEventButton *event)
3289 PsppireSheet *sheet;
3290 GdkModifierType mods;
3295 g_return_val_if_fail (widget != NULL, FALSE);
3296 g_return_val_if_fail (PSPPIRE_IS_SHEET (widget), FALSE);
3297 g_return_val_if_fail (event != NULL, FALSE);
3299 sheet = PSPPIRE_SHEET (widget);
3301 /* Cancel any pending tooltips */
3302 if (sheet->motion_timer)
3304 g_source_remove (sheet->motion_timer);
3305 sheet->motion_timer = 0;
3308 gtk_widget_get_pointer (widget, &x, &y);
3309 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
3312 if (event->window == sheet->column_title_window)
3314 sheet->x_drag = event->x;
3315 g_signal_emit (sheet,
3316 sheet_signals[BUTTON_EVENT_COLUMN], 0,
3319 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
3321 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
3322 g_signal_emit (sheet,
3323 sheet_signals[DOUBLE_CLICK_COLUMN], 0, column);
3326 else if (event->window == sheet->row_title_window)
3328 g_signal_emit (sheet,
3329 sheet_signals[BUTTON_EVENT_ROW], 0,
3332 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
3334 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
3335 g_signal_emit (sheet,
3336 sheet_signals[DOUBLE_CLICK_ROW], 0, row);
3340 gdk_window_get_pointer (widget->window, NULL, NULL, &mods);
3342 if (! (mods & GDK_BUTTON1_MASK)) return TRUE;
3345 /* press on resize windows */
3346 if (event->window == sheet->column_title_window)
3348 sheet->x_drag = event->x;
3350 if (on_column_boundary (sheet, sheet->x_drag, &sheet->drag_cell.col))
3352 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
3353 gdk_pointer_grab (sheet->column_title_window, FALSE,
3354 GDK_POINTER_MOTION_HINT_MASK |
3355 GDK_BUTTON1_MOTION_MASK |
3356 GDK_BUTTON_RELEASE_MASK,
3357 NULL, NULL, event->time);
3359 draw_xor_vline (sheet);
3364 if (event->window == sheet->row_title_window)
3366 sheet->y_drag = event->y;
3368 if (on_row_boundary (sheet, sheet->y_drag, &sheet->drag_cell.row))
3370 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
3371 gdk_pointer_grab (sheet->row_title_window, FALSE,
3372 GDK_POINTER_MOTION_HINT_MASK |
3373 GDK_BUTTON1_MOTION_MASK |
3374 GDK_BUTTON_RELEASE_MASK,
3375 NULL, NULL, event->time);
3377 draw_xor_hline (sheet);
3382 /* the sheet itself does not handle other than single click events */
3383 if (event->type != GDK_BUTTON_PRESS) return FALSE;
3385 /* selections on the sheet */
3386 if (event->window == sheet->sheet_window)
3388 gtk_widget_get_pointer (widget, &x, &y);
3389 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
3390 gdk_pointer_grab (sheet->sheet_window, FALSE,
3391 GDK_POINTER_MOTION_HINT_MASK |
3392 GDK_BUTTON1_MOTION_MASK |
3393 GDK_BUTTON_RELEASE_MASK,
3394 NULL, NULL, event->time);
3395 gtk_grab_add (GTK_WIDGET (sheet));
3397 if (psppire_sheet_click_cell (sheet, row, column))
3398 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3401 if (event->window == sheet->column_title_window)
3403 gtk_widget_get_pointer (widget, &x, &y);
3404 if ( sheet->row_titles_visible)
3405 x -= sheet->row_title_area.width;
3407 x += sheet->hadjustment->value;
3409 column = column_from_xpixel (sheet, x);
3411 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
3413 gtk_grab_add (GTK_WIDGET (sheet));
3414 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3418 if (event->window == sheet->row_title_window)
3420 gtk_widget_get_pointer (widget, &x, &y);
3421 if ( sheet->column_titles_visible)
3422 y -= sheet->column_title_area.height;
3424 y += sheet->vadjustment->value;
3426 row = row_from_ypixel (sheet, y);
3427 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
3429 gtk_grab_add (GTK_WIDGET (sheet));
3430 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3438 psppire_sheet_click_cell (PsppireSheet *sheet, gint row, gint column)
3440 PsppireSheetCell cell;
3441 gboolean forbid_move;
3446 if (row >= psppire_axis_unit_count (sheet->vaxis)
3447 || column >= psppire_axis_unit_count (sheet->haxis))
3452 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
3453 &sheet->active_cell,
3459 if (sheet->state == GTK_STATE_NORMAL)
3462 row = sheet->active_cell.row;
3463 column = sheet->active_cell.col;
3465 change_active_cell (sheet, row, column);
3469 if (row == -1 && column >= 0)
3471 psppire_sheet_select_column (sheet, column);
3475 if (column == -1 && row >= 0)
3477 psppire_sheet_select_row (sheet, row);
3481 if (row == -1 && column == -1)
3483 sheet->range.row0 = 0;
3484 sheet->range.col0 = 0;
3485 sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
3487 psppire_axis_unit_count (sheet->haxis) - 1;
3488 psppire_sheet_select_range (sheet, NULL);
3492 if (sheet->state != PSPPIRE_SHEET_NORMAL)
3494 sheet->state = PSPPIRE_SHEET_NORMAL;
3495 psppire_sheet_real_unselect_range (sheet, NULL);
3499 change_active_cell (sheet, row, column);
3502 sheet->selection_cell.row = row;
3503 sheet->selection_cell.col = column;
3504 sheet->range.row0 = row;
3505 sheet->range.col0 = column;
3506 sheet->range.rowi = row;
3507 sheet->range.coli = column;
3508 sheet->state = PSPPIRE_SHEET_NORMAL;
3509 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3511 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
3517 psppire_sheet_button_release (GtkWidget *widget,
3518 GdkEventButton *event)
3520 GdkDisplay *display = gtk_widget_get_display (widget);
3522 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3524 /* release on resize windows */
3525 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3528 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
3529 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3531 gdk_display_pointer_ungrab (display, event->time);
3532 draw_xor_vline (sheet);
3535 psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)
3536 + sheet->hadjustment->value;
3538 set_column_width (sheet, sheet->drag_cell.col, width);
3543 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3546 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
3547 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3549 gdk_display_pointer_ungrab (display, event->time);
3550 draw_xor_hline (sheet);
3553 psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row) +
3554 sheet->vadjustment->value;
3556 set_row_height (sheet, sheet->drag_cell.row, height);
3561 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3563 PsppireSheetRange old_range;
3564 draw_xor_rectangle (sheet, sheet->drag_range);
3565 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_DRAG);
3566 gdk_display_pointer_ungrab (display, event->time);
3568 psppire_sheet_real_unselect_range (sheet, NULL);
3570 sheet->selection_cell.row = sheet->selection_cell.row +
3571 (sheet->drag_range.row0 - sheet->range.row0);
3572 sheet->selection_cell.col = sheet->selection_cell.col +
3573 (sheet->drag_range.col0 - sheet->range.col0);
3574 old_range = sheet->range;
3575 sheet->range = sheet->drag_range;
3576 sheet->drag_range = old_range;
3577 g_signal_emit (sheet, sheet_signals[MOVE_RANGE], 0,
3578 &sheet->drag_range, &sheet->range);
3579 psppire_sheet_select_range (sheet, &sheet->range);
3582 if (PSPPIRE_SHEET_IN_RESIZE (sheet))
3584 PsppireSheetRange old_range;
3585 draw_xor_rectangle (sheet, sheet->drag_range);
3586 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_RESIZE);
3587 gdk_display_pointer_ungrab (display, event->time);
3589 psppire_sheet_real_unselect_range (sheet, NULL);
3591 if (sheet->drag_range.row0 < sheet->range.row0)
3592 sheet->selection_cell.row = sheet->drag_range.row0;
3593 if (sheet->drag_range.rowi >= sheet->range.rowi)
3594 sheet->selection_cell.row = sheet->drag_range.rowi;
3595 if (sheet->drag_range.col0 < sheet->range.col0)
3596 sheet->selection_cell.col = sheet->drag_range.col0;
3597 if (sheet->drag_range.coli >= sheet->range.coli)
3598 sheet->selection_cell.col = sheet->drag_range.coli;
3599 old_range = sheet->range;
3600 sheet->range = sheet->drag_range;
3601 sheet->drag_range = old_range;
3603 if (sheet->state == GTK_STATE_NORMAL) sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
3604 g_signal_emit (sheet, sheet_signals[RESIZE_RANGE], 0,
3605 &sheet->drag_range, &sheet->range);
3606 psppire_sheet_select_range (sheet, &sheet->range);
3609 if (sheet->state == PSPPIRE_SHEET_NORMAL && PSPPIRE_SHEET_IN_SELECTION (sheet))
3611 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3612 gdk_display_pointer_ungrab (display, event->time);
3613 change_active_cell (sheet, sheet->active_cell.row,
3614 sheet->active_cell.col);
3617 if (PSPPIRE_SHEET_IN_SELECTION)
3618 gdk_display_pointer_ungrab (display, event->time);
3619 gtk_grab_remove (GTK_WIDGET (sheet));
3621 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3630 /* Shamelessly lifted from gtktooltips */
3632 psppire_sheet_subtitle_paint_window (GtkWidget *tip_window)
3636 gtk_widget_size_request (tip_window, &req);
3637 gtk_paint_flat_box (tip_window->style, tip_window->window,
3638 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
3639 NULL, GTK_WIDGET(tip_window), "tooltip",
3640 0, 0, req.width, req.height);
3646 destroy_hover_window (PsppireSheetHoverTitle *h)
3648 gtk_widget_destroy (h->window);
3652 static PsppireSheetHoverTitle *
3653 create_hover_window (void)
3655 PsppireSheetHoverTitle *hw = g_malloc (sizeof (*hw));
3657 hw->window = gtk_window_new (GTK_WINDOW_POPUP);
3659 #if GTK_CHECK_VERSION (2, 9, 0)
3660 gtk_window_set_type_hint (GTK_WINDOW (hw->window),
3661 GDK_WINDOW_TYPE_HINT_TOOLTIP);
3664 gtk_widget_set_app_paintable (hw->window, TRUE);
3665 gtk_window_set_resizable (GTK_WINDOW (hw->window), FALSE);
3666 gtk_widget_set_name (hw->window, "gtk-tooltips");
3667 gtk_container_set_border_width (GTK_CONTAINER (hw->window), 4);
3669 g_signal_connect (hw->window,
3671 G_CALLBACK (psppire_sheet_subtitle_paint_window),
3674 hw->label = gtk_label_new (NULL);
3677 gtk_label_set_line_wrap (GTK_LABEL (hw->label), TRUE);
3678 gtk_misc_set_alignment (GTK_MISC (hw->label), 0.5, 0.5);
3680 gtk_container_add (GTK_CONTAINER (hw->window), hw->label);
3682 gtk_widget_show (hw->label);
3684 g_signal_connect (hw->window,
3686 G_CALLBACK (gtk_widget_destroyed),
3692 #define HOVER_WINDOW_Y_OFFSET 2
3695 show_subtitle (PsppireSheet *sheet, gint row, gint column,
3696 const gchar *subtitle)
3705 gtk_label_set_text (GTK_LABEL (sheet->hover_window->label),
3709 sheet->hover_window->row = row;
3710 sheet->hover_window->column = column;
3712 gdk_window_get_origin (GTK_WIDGET (sheet)->window, &x, &y);
3714 gtk_widget_get_pointer (GTK_WIDGET (sheet), &px, &py);
3716 gtk_widget_show (sheet->hover_window->window);
3718 width = GTK_WIDGET (sheet->hover_window->label)->allocation.width;
3724 y += sheet->column_title_area.y;
3725 y += sheet->column_title_area.height;
3726 y += HOVER_WINDOW_Y_OFFSET;
3732 x += sheet->row_title_area.x;
3733 x += sheet->row_title_area.width * 2 / 3.0;
3736 gtk_window_move (GTK_WINDOW (sheet->hover_window->window),
3741 motion_timeout_callback (gpointer data)
3743 PsppireSheet *sheet = PSPPIRE_SHEET (data);
3747 gdk_threads_enter ();
3748 gtk_widget_get_pointer (GTK_WIDGET (sheet), &x, &y);
3750 if ( psppire_sheet_get_pixel_info (sheet, x, y, &row, &column) )
3752 if (sheet->row_title_under && row >= 0)
3754 gchar *text = psppire_sheet_model_get_row_subtitle (sheet->model, row);
3756 show_subtitle (sheet, row, -1, text);
3760 if (sheet->column_title_under && column >= 0)
3762 gchar *text = psppire_sheet_model_get_column_subtitle (sheet->model,
3765 show_subtitle (sheet, -1, column, text);
3771 gdk_threads_leave ();
3776 psppire_sheet_motion (GtkWidget *widget, GdkEventMotion *event)
3778 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3779 GdkModifierType mods;
3780 GdkCursorType new_cursor;
3783 GdkDisplay *display;
3785 g_return_val_if_fail (event != NULL, FALSE);
3787 display = gtk_widget_get_display (widget);
3789 /* selections on the sheet */
3793 if (!GTK_WIDGET_VISIBLE (sheet->hover_window->window))
3795 if ( sheet->motion_timer > 0 )
3796 g_source_remove (sheet->motion_timer);
3797 sheet->motion_timer =
3798 g_timeout_add (TIMEOUT_HOVER, motion_timeout_callback, sheet);
3804 gtk_widget_get_pointer (widget, &wx, &wy);
3806 if ( psppire_sheet_get_pixel_info (sheet, wx, wy, &row, &column) )
3808 if ( row != sheet->hover_window->row ||
3809 column != sheet->hover_window->column)
3811 gtk_widget_hide (sheet->hover_window->window);
3816 if (event->window == sheet->column_title_window)
3818 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3819 on_column_boundary (sheet, x, &column))
3821 new_cursor = GDK_SB_H_DOUBLE_ARROW;
3822 if (new_cursor != sheet->cursor_drag->type)
3824 gdk_cursor_unref (sheet->cursor_drag);
3825 sheet->cursor_drag =
3826 gdk_cursor_new_for_display (display, new_cursor);
3828 gdk_window_set_cursor (sheet->column_title_window,
3829 sheet->cursor_drag);
3834 new_cursor = GDK_TOP_LEFT_ARROW;
3835 if (!PSPPIRE_SHEET_IN_XDRAG (sheet) &&
3836 new_cursor != sheet->cursor_drag->type)
3838 gdk_cursor_unref (sheet->cursor_drag);
3839 sheet->cursor_drag =
3840 gdk_cursor_new_for_display (display, new_cursor);
3841 gdk_window_set_cursor (sheet->column_title_window,
3842 sheet->cursor_drag);
3846 else if (event->window == sheet->row_title_window)
3848 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3849 on_row_boundary (sheet, y, &row))
3851 new_cursor = GDK_SB_V_DOUBLE_ARROW;
3852 if (new_cursor != sheet->cursor_drag->type)
3854 gdk_cursor_unref (sheet->cursor_drag);
3855 sheet->cursor_drag =
3856 gdk_cursor_new_for_display (display, new_cursor);
3857 gdk_window_set_cursor (sheet->row_title_window,
3858 sheet->cursor_drag);
3863 new_cursor = GDK_TOP_LEFT_ARROW;
3864 if (!PSPPIRE_SHEET_IN_YDRAG (sheet) &&
3865 new_cursor != sheet->cursor_drag->type)
3867 gdk_cursor_unref (sheet->cursor_drag);
3868 sheet->cursor_drag =
3869 gdk_cursor_new_for_display (display, new_cursor);
3870 gdk_window_set_cursor (sheet->row_title_window,
3871 sheet->cursor_drag);
3876 new_cursor = GDK_PLUS;
3877 if ( event->window == sheet->sheet_window &&
3878 !POSSIBLE_DRAG (sheet, x, y, &row, &column) &&
3879 !PSPPIRE_SHEET_IN_DRAG (sheet) &&
3880 !POSSIBLE_RESIZE (sheet, x, y, &row, &column) &&
3881 !PSPPIRE_SHEET_IN_RESIZE (sheet) &&
3882 new_cursor != sheet->cursor_drag->type)
3884 gdk_cursor_unref (sheet->cursor_drag);
3885 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
3886 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3889 new_cursor = GDK_TOP_LEFT_ARROW;
3890 if ( event->window == sheet->sheet_window &&
3891 ! (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
3892 PSPPIRE_SHEET_IN_RESIZE (sheet)) &&
3893 (POSSIBLE_DRAG (sheet, x, y, &row, &column) ||
3894 PSPPIRE_SHEET_IN_DRAG (sheet)) &&
3895 new_cursor != sheet->cursor_drag->type)
3897 gdk_cursor_unref (sheet->cursor_drag);
3898 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
3899 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3902 new_cursor = GDK_SIZING;
3903 if ( event->window == sheet->sheet_window &&
3904 sheet->selection_mode != GTK_SELECTION_NONE &&
3905 !PSPPIRE_SHEET_IN_DRAG (sheet) &&
3906 (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
3907 PSPPIRE_SHEET_IN_RESIZE (sheet)) &&
3908 new_cursor != sheet->cursor_drag->type)
3910 gdk_cursor_unref (sheet->cursor_drag);
3911 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_SIZING);
3912 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3916 gdk_window_get_pointer (widget->window, &x, &y, &mods);
3917 if (! (mods & GDK_BUTTON1_MASK)) return FALSE;
3919 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3921 if (event->x != sheet->x_drag)
3923 draw_xor_vline (sheet);
3924 sheet->x_drag = event->x;
3925 draw_xor_vline (sheet);
3931 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3933 if (event->y != sheet->y_drag)
3935 draw_xor_hline (sheet);
3936 sheet->y_drag = event->y;
3937 draw_xor_hline (sheet);
3943 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3945 PsppireSheetRange aux;
3946 column = column_from_xpixel (sheet, x)- sheet->drag_cell.col;
3947 row = row_from_ypixel (sheet, y) - sheet->drag_cell.row;
3948 if (sheet->state == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
3949 if (sheet->state == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
3953 if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
3954 aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
3956 aux = sheet->drag_range;
3957 sheet->drag_range.row0 = sheet->range.row0 + row;
3958 sheet->drag_range.col0 = sheet->range.col0 + column;
3959 sheet->drag_range.rowi = sheet->range.rowi + row;
3960 sheet->drag_range.coli = sheet->range.coli + column;
3961 if (aux.row0 != sheet->drag_range.row0 ||
3962 aux.col0 != sheet->drag_range.col0)
3964 draw_xor_rectangle (sheet, aux);
3965 draw_xor_rectangle (sheet, sheet->drag_range);
3971 if (PSPPIRE_SHEET_IN_RESIZE (sheet))
3973 PsppireSheetRange aux;
3974 gint v_h, current_col, current_row, col_threshold, row_threshold;
3976 if (abs (x - psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)) >
3977 abs (y - psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row))) v_h = 2;
3979 current_col = column_from_xpixel (sheet, x);
3980 current_row = row_from_ypixel (sheet, y);
3981 column = current_col - sheet->drag_cell.col;
3982 row = current_row - sheet->drag_cell.row;
3984 /*use half of column width resp. row height as threshold to
3986 col_threshold = psppire_axis_start_pixel (sheet->haxis, current_col) +
3987 psppire_axis_unit_size (sheet->haxis, current_col) / 2;
3990 if (x < col_threshold)
3993 else if (column < 0)
3995 if (x > col_threshold)
3998 row_threshold = psppire_axis_start_pixel (sheet->vaxis, current_row) +
3999 psppire_axis_unit_size (sheet->vaxis, current_row)/2;
4002 if (y < row_threshold)
4007 if (y > row_threshold)
4011 if (sheet->state == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
4012 if (sheet->state == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
4022 if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
4023 aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
4025 aux = sheet->drag_range;
4026 sheet->drag_range = sheet->range;
4028 if (row < 0) sheet->drag_range.row0 = sheet->range.row0 + row;
4029 if (row > 0) sheet->drag_range.rowi = sheet->range.rowi + row;
4030 if (column < 0) sheet->drag_range.col0 = sheet->range.col0 + column;
4031 if (column > 0) sheet->drag_range.coli = sheet->range.coli + column;
4033 if (aux.row0 != sheet->drag_range.row0 ||
4034 aux.rowi != sheet->drag_range.rowi ||
4035 aux.col0 != sheet->drag_range.col0 ||
4036 aux.coli != sheet->drag_range.coli)
4038 draw_xor_rectangle (sheet, aux);
4039 draw_xor_rectangle (sheet, sheet->drag_range);
4045 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
4047 if (sheet->state == PSPPIRE_SHEET_NORMAL && row == sheet->active_cell.row &&
4048 column == sheet->active_cell.col) return TRUE;
4050 if (PSPPIRE_SHEET_IN_SELECTION (sheet) && mods&GDK_BUTTON1_MASK)
4051 psppire_sheet_extend_selection (sheet, row, column);
4057 psppire_sheet_crossing_notify (GtkWidget *widget,
4058 GdkEventCrossing *event)
4060 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
4062 if (event->window == sheet->column_title_window)
4063 sheet->column_title_under = event->type == GDK_ENTER_NOTIFY;
4064 else if (event->window == sheet->row_title_window)
4065 sheet->row_title_under = event->type == GDK_ENTER_NOTIFY;
4067 if (event->type == GDK_LEAVE_NOTIFY)
4068 gtk_widget_hide (sheet->hover_window->window);
4075 psppire_sheet_focus_in (GtkWidget *w,
4076 GdkEventFocus *event)
4078 PsppireSheet *sheet = PSPPIRE_SHEET (w);
4080 gtk_widget_grab_focus (sheet->entry_widget);
4087 psppire_sheet_extend_selection (PsppireSheet *sheet, gint row, gint column)
4089 PsppireSheetRange range;
4093 if (row == sheet->selection_cell.row && column == sheet->selection_cell.col)
4096 if (sheet->selection_mode == GTK_SELECTION_SINGLE) return;
4098 gtk_widget_grab_focus (GTK_WIDGET (sheet));
4100 if (PSPPIRE_SHEET_IN_DRAG (sheet)) return;
4102 state = sheet->state;
4104 switch (sheet->state)
4106 case PSPPIRE_SHEET_ROW_SELECTED:
4107 column = psppire_axis_unit_count (sheet->haxis) - 1;
4109 case PSPPIRE_SHEET_COLUMN_SELECTED:
4110 row = psppire_axis_unit_count (sheet->vaxis) - 1;
4112 case PSPPIRE_SHEET_NORMAL:
4113 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
4114 r = sheet->active_cell.row;
4115 c = sheet->active_cell.col;
4116 sheet->range.col0 = c;
4117 sheet->range.row0 = r;
4118 sheet->range.coli = c;
4119 sheet->range.rowi = r;
4120 psppire_sheet_range_draw_selection (sheet, sheet->range);
4121 case PSPPIRE_SHEET_RANGE_SELECTED:
4122 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
4125 sheet->selection_cell.row = row;
4126 sheet->selection_cell.col = column;
4128 range.col0 = MIN (column, sheet->active_cell.col);
4129 range.coli = MAX (column, sheet->active_cell.col);
4130 range.row0 = MIN (row, sheet->active_cell.row);
4131 range.rowi = MAX (row, sheet->active_cell.row);
4133 if (range.row0 != sheet->range.row0 || range.rowi != sheet->range.rowi ||
4134 range.col0 != sheet->range.col0 || range.coli != sheet->range.coli ||
4135 state == PSPPIRE_SHEET_NORMAL)
4136 psppire_sheet_real_select_range (sheet, &range);
4141 psppire_sheet_entry_key_press (GtkWidget *widget,
4145 g_signal_emit_by_name (widget, "key_press_event", key, &focus);
4150 /* Number of rows in a step-increment */
4151 #define ROWS_PER_STEP 1
4155 page_vertical (PsppireSheet *sheet, GtkScrollType dir)
4157 gint old_row = sheet->active_cell.row ;
4158 glong vpixel = psppire_axis_start_pixel (sheet->vaxis, old_row);
4162 vpixel -= psppire_axis_start_pixel (sheet->vaxis,
4163 min_visible_row (sheet));
4167 case GTK_SCROLL_PAGE_DOWN:
4168 gtk_adjustment_set_value (sheet->vadjustment,
4169 sheet->vadjustment->value +
4170 sheet->vadjustment->page_increment);
4172 case GTK_SCROLL_PAGE_UP:
4173 gtk_adjustment_set_value (sheet->vadjustment,
4174 sheet->vadjustment->value -
4175 sheet->vadjustment->page_increment);
4179 g_assert_not_reached ();
4184 vpixel += psppire_axis_start_pixel (sheet->vaxis,
4185 min_visible_row (sheet));
4187 new_row = row_from_ypixel (sheet, vpixel);
4189 change_active_cell (sheet, new_row,
4190 sheet->active_cell.col);
4195 step_sheet (PsppireSheet *sheet, GtkScrollType dir)
4197 gint current_row = sheet->active_cell.row;
4198 gint current_col = sheet->active_cell.col;
4199 PsppireSheetCell new_cell ;
4200 gboolean forbidden = FALSE;
4202 new_cell.row = current_row;
4203 new_cell.col = current_col;
4207 case GTK_SCROLL_STEP_DOWN:
4210 case GTK_SCROLL_STEP_UP:
4213 case GTK_SCROLL_STEP_RIGHT:
4216 case GTK_SCROLL_STEP_LEFT:
4219 case GTK_SCROLL_STEP_FORWARD:
4222 psppire_sheet_model_get_column_count (sheet->model))
4228 case GTK_SCROLL_STEP_BACKWARD:
4230 if (new_cell.col < 0)
4233 psppire_sheet_model_get_column_count (sheet->model) - 1;
4238 g_assert_not_reached ();
4242 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
4243 &sheet->active_cell,
4251 maximize_int (&new_cell.row, 0);
4252 maximize_int (&new_cell.col, 0);
4254 minimize_int (&new_cell.row,
4255 psppire_axis_unit_count (sheet->vaxis) - 1);
4257 minimize_int (&new_cell.col,
4258 psppire_axis_unit_count (sheet->haxis) - 1);
4260 change_active_cell (sheet, new_cell.row, new_cell.col);
4263 if ( new_cell.col > max_fully_visible_column (sheet))
4266 psppire_axis_start_pixel (sheet->haxis,
4268 hpos -= sheet->hadjustment->page_size;
4270 gtk_adjustment_set_value (sheet->hadjustment,
4273 else if ( new_cell.col < min_fully_visible_column (sheet))
4276 psppire_axis_start_pixel (sheet->haxis,
4279 gtk_adjustment_set_value (sheet->hadjustment,
4284 if ( new_cell.row > max_fully_visible_row (sheet))
4287 psppire_axis_start_pixel (sheet->vaxis,
4289 vpos -= sheet->vadjustment->page_size;
4291 gtk_adjustment_set_value (sheet->vadjustment,
4294 else if ( new_cell.row < min_fully_visible_row (sheet))
4297 psppire_axis_start_pixel (sheet->vaxis,
4300 gtk_adjustment_set_value (sheet->vadjustment,
4304 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
4309 psppire_sheet_key_press (GtkWidget *widget,
4312 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
4314 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
4316 switch (key->keyval)
4319 step_sheet (sheet, GTK_SCROLL_STEP_FORWARD);
4322 step_sheet (sheet, GTK_SCROLL_STEP_RIGHT);
4324 case GDK_ISO_Left_Tab:
4325 step_sheet (sheet, GTK_SCROLL_STEP_BACKWARD);
4328 step_sheet (sheet, GTK_SCROLL_STEP_LEFT);
4332 step_sheet (sheet, GTK_SCROLL_STEP_DOWN);
4335 step_sheet (sheet, GTK_SCROLL_STEP_UP);
4339 page_vertical (sheet, GTK_SCROLL_PAGE_DOWN);
4342 page_vertical (sheet, GTK_SCROLL_PAGE_UP);
4346 gtk_adjustment_set_value (sheet->vadjustment,
4347 sheet->vadjustment->lower);
4349 change_active_cell (sheet, 0,
4350 sheet->active_cell.col);
4355 gtk_adjustment_set_value (sheet->vadjustment,
4356 sheet->vadjustment->upper -
4357 sheet->vadjustment->page_size -
4358 sheet->vadjustment->page_increment);
4361 change_active_cellx (sheet,
4362 psppire_axis_unit_count (sheet->vaxis) - 1,
4363 sheet->active_cell.col);
4367 psppire_sheet_real_cell_clear (sheet, sheet->active_cell.row, sheet->active_cell.col);
4378 psppire_sheet_size_request (GtkWidget *widget,
4379 GtkRequisition *requisition)
4381 PsppireSheet *sheet;
4383 g_return_if_fail (widget != NULL);
4384 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
4385 g_return_if_fail (requisition != NULL);
4387 sheet = PSPPIRE_SHEET (widget);
4389 requisition->width = 3 * DEFAULT_COLUMN_WIDTH;
4390 requisition->height = 3 * DEFAULT_ROW_HEIGHT;
4392 /* compute the size of the column title area */
4393 if (sheet->column_titles_visible)
4394 requisition->height += sheet->column_title_area.height;
4396 /* compute the size of the row title area */
4397 if (sheet->row_titles_visible)
4398 requisition->width += sheet->row_title_area.width;
4403 psppire_sheet_size_allocate (GtkWidget *widget,
4404 GtkAllocation *allocation)
4406 PsppireSheet *sheet;
4407 GtkAllocation sheet_allocation;
4410 g_return_if_fail (widget != NULL);
4411 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
4412 g_return_if_fail (allocation != NULL);
4414 sheet = PSPPIRE_SHEET (widget);
4415 widget->allocation = *allocation;
4416 border_width = GTK_CONTAINER (widget)->border_width;
4418 if (GTK_WIDGET_REALIZED (widget))
4419 gdk_window_move_resize (widget->window,
4420 allocation->x + border_width,
4421 allocation->y + border_width,
4422 allocation->width - 2 * border_width,
4423 allocation->height - 2 * border_width);
4425 sheet_allocation.x = 0;
4426 sheet_allocation.y = 0;
4427 sheet_allocation.width = allocation->width - 2 * border_width;
4428 sheet_allocation.height = allocation->height - 2 * border_width;
4430 if (GTK_WIDGET_REALIZED (widget))
4431 gdk_window_move_resize (sheet->sheet_window,
4434 sheet_allocation.width,
4435 sheet_allocation.height);
4437 /* position the window which holds the column title buttons */
4438 sheet->column_title_area.x = 0;
4439 sheet->column_title_area.y = 0;
4440 sheet->column_title_area.width = sheet_allocation.width ;
4443 /* position the window which holds the row title buttons */
4444 sheet->row_title_area.x = 0;
4445 sheet->row_title_area.y = 0;
4446 sheet->row_title_area.height = sheet_allocation.height;
4448 if (sheet->row_titles_visible)
4449 sheet->column_title_area.x += sheet->row_title_area.width;
4451 if (sheet->column_titles_visible)
4452 sheet->row_title_area.y += sheet->column_title_area.height;
4454 if (GTK_WIDGET_REALIZED (widget) && sheet->column_titles_visible)
4455 gdk_window_move_resize (sheet->column_title_window,
4456 sheet->column_title_area.x,
4457 sheet->column_title_area.y,
4458 sheet->column_title_area.width,
4459 sheet->column_title_area.height);
4462 if (GTK_WIDGET_REALIZED (widget) && sheet->row_titles_visible)
4463 gdk_window_move_resize (sheet->row_title_window,
4464 sheet->row_title_area.x,
4465 sheet->row_title_area.y,
4466 sheet->row_title_area.width,
4467 sheet->row_title_area.height);
4469 size_allocate_global_button (sheet);
4473 gint width = sheet->column_title_area.width;
4475 if ( sheet->row_titles_visible)
4476 width -= sheet->row_title_area.width;
4478 g_object_set (sheet->haxis,
4479 "minimum-extent", width,
4486 gint height = sheet->row_title_area.height;
4488 if ( sheet->column_titles_visible)
4489 height -= sheet->column_title_area.height;
4491 g_object_set (sheet->vaxis,
4492 "minimum-extent", height,
4497 /* set the scrollbars adjustments */
4498 adjust_scrollbars (sheet);
4502 draw_column_title_buttons (PsppireSheet *sheet)
4506 if (!sheet->column_titles_visible) return;
4507 if (!GTK_WIDGET_REALIZED (sheet))
4510 gdk_drawable_get_size (sheet->sheet_window, &width, NULL);
4513 if (sheet->row_titles_visible)
4515 x = sheet->row_title_area.width;
4518 if (sheet->column_title_area.width != width || sheet->column_title_area.x != x)
4520 sheet->column_title_area.width = width;
4521 sheet->column_title_area.x = x;
4522 gdk_window_move_resize (sheet->column_title_window,
4523 sheet->column_title_area.x,
4524 sheet->column_title_area.y,
4525 sheet->column_title_area.width,
4526 sheet->column_title_area.height);
4529 if (max_visible_column (sheet) ==
4530 psppire_axis_unit_count (sheet->haxis) - 1)
4531 gdk_window_clear_area (sheet->column_title_window,
4533 sheet->column_title_area.width,
4534 sheet->column_title_area.height);
4536 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
4538 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
4539 max_visible_column (sheet));
4543 draw_row_title_buttons (PsppireSheet *sheet)
4548 if (!sheet->row_titles_visible) return;
4549 if (!GTK_WIDGET_REALIZED (sheet))
4552 gdk_drawable_get_size (sheet->sheet_window, NULL, &height);
4554 if (sheet->column_titles_visible)
4556 y = sheet->column_title_area.height;
4559 if (sheet->row_title_area.height != height || sheet->row_title_area.y != y)
4561 sheet->row_title_area.y = y;
4562 sheet->row_title_area.height = height;
4563 gdk_window_move_resize (sheet->row_title_window,
4564 sheet->row_title_area.x,
4565 sheet->row_title_area.y,
4566 sheet->row_title_area.width,
4567 sheet->row_title_area.height);
4570 if (max_visible_row (sheet) == psppire_axis_unit_count (sheet->vaxis) - 1)
4571 gdk_window_clear_area (sheet->row_title_window,
4573 sheet->row_title_area.width,
4574 sheet->row_title_area.height);
4576 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
4578 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
4579 max_visible_row (sheet));
4584 psppire_sheet_size_allocate_entry (PsppireSheet *sheet)
4586 GtkAllocation entry_alloc;
4587 PsppireSheetCellAttr attributes = { 0 };
4588 GtkEntry *sheet_entry;
4590 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4591 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
4593 sheet_entry = psppire_sheet_get_entry (sheet);
4595 if ( ! psppire_sheet_get_attributes (sheet, sheet->active_cell.row,
4596 sheet->active_cell.col,
4600 if ( GTK_WIDGET_REALIZED (sheet->entry_widget) )
4602 GtkStyle *style = GTK_WIDGET (sheet_entry)->style;
4604 style->bg[GTK_STATE_NORMAL] = attributes.background;
4605 style->fg[GTK_STATE_NORMAL] = attributes.foreground;
4606 style->text[GTK_STATE_NORMAL] = attributes.foreground;
4607 style->bg[GTK_STATE_ACTIVE] = attributes.background;
4608 style->fg[GTK_STATE_ACTIVE] = attributes.foreground;
4609 style->text[GTK_STATE_ACTIVE] = attributes.foreground;
4612 rectangle_from_cell (sheet, sheet->active_cell.row,
4613 sheet->active_cell.col, &entry_alloc);
4615 entry_alloc.x += sheet->cell_padding->left;
4616 entry_alloc.y += sheet->cell_padding->right;
4617 entry_alloc.width -= sheet->cell_padding->left + sheet->cell_padding->right;
4618 entry_alloc.height -= sheet->cell_padding->top + sheet->cell_padding->bottom;
4621 gtk_widget_set_size_request (sheet->entry_widget, entry_alloc.width,
4622 entry_alloc.height);
4623 gtk_widget_size_allocate (sheet->entry_widget, &entry_alloc);
4627 /* Copy the sheet's font to the entry widget */
4629 set_entry_widget_font (PsppireSheet *sheet)
4631 GtkRcStyle *style = gtk_widget_get_modifier_style (sheet->entry_widget);
4633 pango_font_description_free (style->font_desc);
4634 style->font_desc = pango_font_description_copy (GTK_WIDGET (sheet)->style->font_desc);
4636 gtk_widget_modify_style (sheet->entry_widget, style);
4640 create_sheet_entry (PsppireSheet *sheet)
4642 if (sheet->entry_widget)
4644 gtk_widget_unparent (sheet->entry_widget);
4647 sheet->entry_widget = g_object_new (sheet->entry_type, NULL);
4648 g_object_ref_sink (sheet->entry_widget);
4650 gtk_widget_size_request (sheet->entry_widget, NULL);
4652 if ( GTK_IS_ENTRY (sheet->entry_widget))
4654 g_object_set (sheet->entry_widget,
4659 if (GTK_WIDGET_REALIZED (sheet))
4661 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
4662 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
4663 gtk_widget_realize (sheet->entry_widget);
4666 g_signal_connect_swapped (sheet->entry_widget, "key_press_event",
4667 G_CALLBACK (psppire_sheet_entry_key_press),
4670 set_entry_widget_font (sheet);
4672 gtk_widget_show (sheet->entry_widget);
4676 /* Finds the last child widget that happens to be of type GtkEntry */
4678 find_entry (GtkWidget *w, gpointer user_data)
4680 GtkWidget **entry = user_data;
4681 if ( GTK_IS_ENTRY (w))
4689 psppire_sheet_get_entry (PsppireSheet *sheet)
4691 GtkWidget *w = sheet->entry_widget;
4693 g_return_val_if_fail (sheet != NULL, NULL);
4694 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
4695 g_return_val_if_fail (sheet->entry_widget != NULL, NULL);
4697 while (! GTK_IS_ENTRY (w))
4699 GtkWidget *entry = NULL;
4701 if (GTK_IS_CONTAINER (w))
4703 gtk_container_forall (GTK_CONTAINER (w), find_entry, &entry);
4712 return GTK_ENTRY (w);
4717 draw_button (PsppireSheet *sheet, GdkWindow *window,
4718 PsppireSheetButton *button, gboolean is_sensitive,
4719 GdkRectangle allocation)
4721 GtkShadowType shadow_type;
4722 gint text_width = 0, text_height = 0;
4723 PangoAlignment align = PANGO_ALIGN_LEFT;
4729 g_return_if_fail (sheet != NULL);
4730 g_return_if_fail (button != NULL);
4733 rtl = gtk_widget_get_direction (GTK_WIDGET (sheet)) == GTK_TEXT_DIR_RTL;
4735 gdk_window_clear_area (window,
4736 allocation.x, allocation.y,
4737 allocation.width, allocation.height);
4739 gtk_widget_ensure_style (sheet->button);
4741 gtk_paint_box (sheet->button->style, window,
4742 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
4744 GTK_WIDGET (sheet->button),
4746 allocation.x, allocation.y,
4747 allocation.width, allocation.height);
4749 state = button->state;
4750 if (!is_sensitive) state = GTK_STATE_INSENSITIVE;
4752 if (state == GTK_STATE_ACTIVE)
4753 shadow_type = GTK_SHADOW_IN;
4755 shadow_type = GTK_SHADOW_OUT;
4757 if (state != GTK_STATE_NORMAL && state != GTK_STATE_INSENSITIVE)
4758 gtk_paint_box (sheet->button->style, window,
4759 button->state, shadow_type,
4760 &allocation, GTK_WIDGET (sheet->button),
4762 allocation.x, allocation.y,
4763 allocation.width, allocation.height);
4765 if ( button->overstruck)
4767 GdkPoint points[2] = {
4768 {allocation.x, allocation.y},
4769 {allocation.x + allocation.width,
4770 allocation.y + allocation.height}
4773 gtk_paint_polygon (sheet->button->style,
4785 if (button->label_visible)
4787 text_height = DEFAULT_ROW_HEIGHT -
4788 2 * COLUMN_TITLES_HEIGHT;
4790 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4792 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc,
4795 allocation.y += 2 * sheet->button->style->ythickness;
4797 if (button->label && strlen (button->label) > 0)
4799 PangoRectangle rect;
4800 gchar *line = button->label;
4802 PangoLayout *layout = NULL;
4803 gint real_x = allocation.x;
4804 gint real_y = allocation.y;
4806 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), line);
4807 pango_layout_get_extents (layout, NULL, &rect);
4809 text_width = PANGO_PIXELS (rect.width);
4810 switch (button->justification)
4812 case GTK_JUSTIFY_LEFT:
4813 real_x = allocation.x + COLUMN_TITLES_HEIGHT;
4814 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4816 case GTK_JUSTIFY_RIGHT:
4817 real_x = allocation.x + allocation.width - text_width - COLUMN_TITLES_HEIGHT;
4818 align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
4820 case GTK_JUSTIFY_CENTER:
4822 real_x = allocation.x + (allocation.width - text_width)/2;
4823 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4824 pango_layout_set_justify (layout, TRUE);
4826 pango_layout_set_alignment (layout, align);
4827 gtk_paint_layout (GTK_WIDGET (sheet)->style,
4836 g_object_unref (layout);
4839 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4841 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc, NULL);
4845 psppire_sheet_button_free (button);
4849 /* Draw the column title buttons FIRST through to LAST */
4851 draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4855 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4857 if (!sheet->column_titles_visible) return;
4859 g_return_if_fail (first >= min_visible_column (sheet));
4860 g_return_if_fail (last <= max_visible_column (sheet));
4863 rect.height = sheet->column_title_area.height;
4864 rect.x = psppire_axis_start_pixel (sheet->haxis, first) + CELL_SPACING;
4865 rect.width = psppire_axis_start_pixel (sheet->haxis, last) + CELL_SPACING
4866 + psppire_axis_unit_size (sheet->haxis, last);
4868 rect.x -= sheet->hadjustment->value;
4870 minimize_int (&rect.width, sheet->column_title_area.width);
4871 maximize_int (&rect.x, 0);
4873 gdk_window_begin_paint_rect (sheet->column_title_window, &rect);
4875 for (col = first ; col <= last ; ++col)
4877 GdkRectangle allocation;
4878 gboolean is_sensitive = FALSE;
4880 PsppireSheetButton *
4881 button = psppire_sheet_model_get_column_button (sheet->model, col);
4883 allocation.x = psppire_axis_start_pixel (sheet->haxis, col)
4885 allocation.x -= sheet->hadjustment->value;
4887 allocation.height = sheet->column_title_area.height;
4888 allocation.width = psppire_axis_unit_size (sheet->haxis, col);
4889 is_sensitive = psppire_sheet_model_get_column_sensitivity (sheet->model, col);
4891 draw_button (sheet, sheet->column_title_window,
4892 button, is_sensitive, allocation);
4895 gdk_window_end_paint (sheet->column_title_window);
4900 draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4904 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4906 if (!sheet->row_titles_visible) return;
4908 g_return_if_fail (first >= min_visible_row (sheet));
4909 g_return_if_fail (last <= max_visible_row (sheet));
4912 rect.width = sheet->row_title_area.width;
4913 rect.y = psppire_axis_start_pixel (sheet->vaxis, first) + CELL_SPACING;
4914 rect.height = psppire_axis_start_pixel (sheet->vaxis, last) + CELL_SPACING
4915 + psppire_axis_unit_size (sheet->vaxis, last);
4917 rect.y -= sheet->vadjustment->value;
4919 minimize_int (&rect.height, sheet->row_title_area.height);
4920 maximize_int (&rect.y, 0);
4922 gdk_window_begin_paint_rect (sheet->row_title_window, &rect);
4923 for (row = first; row <= last; ++row)
4925 GdkRectangle allocation;
4927 gboolean is_sensitive = FALSE;
4929 PsppireSheetButton *button =
4930 psppire_sheet_model_get_row_button (sheet->model, row);
4932 allocation.y = psppire_axis_start_pixel (sheet->vaxis, row)
4934 allocation.y -= sheet->vadjustment->value;
4936 allocation.width = sheet->row_title_area.width;
4937 allocation.height = psppire_axis_unit_size (sheet->vaxis, row);
4938 is_sensitive = psppire_sheet_model_get_row_sensitivity (sheet->model, row);
4940 draw_button (sheet, sheet->row_title_window,
4941 button, is_sensitive, allocation);
4944 gdk_window_end_paint (sheet->row_title_window);
4951 * vadjustment_value_changed
4952 * hadjustment_value_changed */
4956 update_adjustment (GtkAdjustment *adj, PsppireAxis *axis, gint page_size)
4959 (adj->value + adj->page_size)
4961 (adj->upper - adj->lower);
4963 const glong last_item = psppire_axis_unit_count (axis) - 1;
4965 if (isnan (position) || position < 0)
4969 psppire_axis_start_pixel (axis, last_item)
4971 psppire_axis_unit_size (axis, last_item)
4975 adj->page_size = page_size;
4978 adj->value = position * (adj->upper - adj->lower) - adj->page_size;
4980 if ( adj->value < adj->lower)
4981 adj->value = adj->lower;
4984 gtk_adjustment_changed (adj);
4989 adjust_scrollbars (PsppireSheet *sheet)
4993 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4996 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
4998 if ( sheet->row_titles_visible)
4999 width -= sheet->row_title_area.width;
5001 if (sheet->column_titles_visible)
5002 height -= sheet->column_title_area.height;
5004 if (sheet->vadjustment)
5006 glong last_row = psppire_axis_unit_count (sheet->vaxis) - 1;
5008 sheet->vadjustment->step_increment =
5010 psppire_axis_unit_size (sheet->vaxis, last_row);
5012 sheet->vadjustment->page_increment =
5014 sheet->column_title_area.height -
5015 psppire_axis_unit_size (sheet->vaxis, last_row);
5017 update_adjustment (sheet->vadjustment, sheet->vaxis, height);
5020 if (sheet->hadjustment)
5022 gint last_col = psppire_axis_unit_count (sheet->haxis) - 1;
5023 sheet->hadjustment->step_increment = 1;
5025 sheet->hadjustment->page_increment = width;
5027 sheet->hadjustment->upper =
5028 psppire_axis_start_pixel (sheet->haxis, last_col)
5030 psppire_axis_unit_size (sheet->haxis, last_col)
5033 update_adjustment (sheet->hadjustment, sheet->haxis, width);
5037 /* Subtracts the region of WIDGET from REGION */
5039 subtract_widget_region (GdkRegion *region, GtkWidget *widget)
5042 GdkRectangle intersect;
5045 gdk_region_get_clipbox (region, &rect);
5046 gtk_widget_intersect (widget,
5050 region2 = gdk_region_rectangle (&intersect);
5051 gdk_region_subtract (region, region2);
5052 gdk_region_destroy (region2);
5056 vadjustment_value_changed (GtkAdjustment *adjustment,
5060 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5062 g_return_if_fail (adjustment != NULL);
5064 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
5066 gtk_widget_hide (sheet->entry_widget);
5069 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
5071 subtract_widget_region (region, sheet->button);
5072 gdk_window_begin_paint_region (sheet->sheet_window, region);
5074 draw_sheet_region (sheet, region);
5076 draw_row_title_buttons (sheet);
5077 psppire_sheet_draw_active_cell (sheet);
5079 gdk_window_end_paint (sheet->sheet_window);
5080 gdk_region_destroy (region);
5085 hadjustment_value_changed (GtkAdjustment *adjustment,
5089 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5091 g_return_if_fail (adjustment != NULL);
5093 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
5095 gtk_widget_hide (sheet->entry_widget);
5099 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
5101 subtract_widget_region (region, sheet->button);
5102 gdk_window_begin_paint_region (sheet->sheet_window, region);
5104 draw_sheet_region (sheet, region);
5106 draw_column_title_buttons (sheet);
5108 psppire_sheet_draw_active_cell (sheet);
5110 gdk_window_end_paint (sheet->sheet_window);
5112 gdk_region_destroy (region);
5116 /* COLUMN RESIZING */
5118 draw_xor_vline (PsppireSheet *sheet)
5121 gint xpos = sheet->x_drag;
5122 gdk_drawable_get_size (sheet->sheet_window,
5125 if (sheet->row_titles_visible)
5126 xpos += sheet->row_title_area.width;
5128 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
5130 sheet->column_title_area.height,
5132 height + CELL_SPACING);
5137 draw_xor_hline (PsppireSheet *sheet)
5141 gint ypos = sheet->y_drag;
5143 gdk_drawable_get_size (sheet->sheet_window,
5147 if (sheet->column_titles_visible)
5148 ypos += sheet->column_title_area.height;
5150 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
5151 sheet->row_title_area.width,
5153 width + CELL_SPACING,
5157 /* SELECTED RANGE */
5159 draw_xor_rectangle (PsppireSheet *sheet, PsppireSheetRange range)
5162 GdkRectangle clip_area, area;
5165 area.x = psppire_axis_start_pixel (sheet->haxis, range.col0);
5166 area.y = psppire_axis_start_pixel (sheet->vaxis, range.row0);
5167 area.width = psppire_axis_start_pixel (sheet->haxis, range.coli)- area.x+
5168 psppire_axis_unit_size (sheet->haxis, range.coli);
5169 area.height = psppire_axis_start_pixel (sheet->vaxis, range.rowi)- area.y +
5170 psppire_axis_unit_size (sheet->vaxis, range.rowi);
5172 clip_area.x = sheet->row_title_area.width;
5173 clip_area.y = sheet->column_title_area.height;
5175 gdk_drawable_get_size (sheet->sheet_window,
5176 &clip_area.width, &clip_area.height);
5178 if (!sheet->row_titles_visible) clip_area.x = 0;
5179 if (!sheet->column_titles_visible) clip_area.y = 0;
5183 area.width = area.width + area.x;
5186 if (area.width > clip_area.width) area.width = clip_area.width + 10;
5189 area.height = area.height + area.y;
5192 if (area.height > clip_area.height) area.height = clip_area.height + 10;
5196 clip_area.width += 3;
5197 clip_area.height += 3;
5199 gdk_gc_get_values (sheet->xor_gc, &values);
5201 gdk_gc_set_clip_rectangle (sheet->xor_gc, &clip_area);
5203 gdk_draw_rectangle (sheet->sheet_window,
5206 area.x + i, area.y + i,
5207 area.width - 2 * i, area.height - 2 * i);
5210 gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
5212 gdk_gc_set_foreground (sheet->xor_gc, &values.foreground);
5217 set_column_width (PsppireSheet *sheet,
5221 g_return_if_fail (sheet != NULL);
5222 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
5224 if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis))
5230 psppire_axis_resize (sheet->haxis, column,
5231 width - sheet->cell_padding->left -
5232 sheet->cell_padding->right);
5234 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
5236 draw_column_title_buttons (sheet);
5237 adjust_scrollbars (sheet);
5238 psppire_sheet_size_allocate_entry (sheet);
5239 redraw_range (sheet, NULL);
5244 set_row_height (PsppireSheet *sheet,
5248 g_return_if_fail (sheet != NULL);
5249 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
5251 if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis))
5257 psppire_axis_resize (sheet->vaxis, row,
5258 height - sheet->cell_padding->top -
5259 sheet->cell_padding->bottom);
5261 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) )
5263 draw_row_title_buttons (sheet);
5264 adjust_scrollbars (sheet);
5265 psppire_sheet_size_allocate_entry (sheet);
5266 redraw_range (sheet, NULL);
5271 psppire_sheet_get_attributes (const PsppireSheet *sheet, gint row, gint col,
5272 PsppireSheetCellAttr *attr)
5275 const GtkJustification *j ;
5276 GdkColormap *colormap;
5278 g_return_val_if_fail (sheet != NULL, FALSE);
5279 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), FALSE);
5281 if (row < 0 || col < 0) return FALSE;
5283 attr->foreground = GTK_WIDGET (sheet)->style->black;
5284 attr->background = sheet->color[BG_COLOR];
5286 attr->border.width = 0;
5287 attr->border.line_style = GDK_LINE_SOLID;
5288 attr->border.cap_style = GDK_CAP_NOT_LAST;
5289 attr->border.join_style = GDK_JOIN_MITER;
5290 attr->border.mask = 0;
5291 attr->border.color = GTK_WIDGET (sheet)->style->black;
5293 colormap = gtk_widget_get_colormap (GTK_WIDGET (sheet));
5294 fg = psppire_sheet_model_get_foreground (sheet->model, row, col);
5297 gdk_colormap_alloc_color (colormap, fg, TRUE, TRUE);
5298 attr->foreground = *fg;
5301 bg = psppire_sheet_model_get_background (sheet->model, row, col);
5304 gdk_colormap_alloc_color (colormap, bg, TRUE, TRUE);
5305 attr->background = *bg;
5308 attr->justification =
5309 psppire_sheet_model_get_column_justification (sheet->model, col);
5311 j = psppire_sheet_model_get_justification (sheet->model, row, col);
5313 attr->justification = *j;
5319 psppire_sheet_button_size_request (PsppireSheet *sheet,
5320 const PsppireSheetButton *button,
5321 GtkRequisition *button_requisition)
5323 GtkRequisition requisition;
5324 GtkRequisition label_requisition;
5326 label_requisition.height = DEFAULT_ROW_HEIGHT;
5327 label_requisition.width = COLUMN_MIN_WIDTH;
5329 requisition.height = DEFAULT_ROW_HEIGHT;
5330 requisition.width = COLUMN_MIN_WIDTH;
5333 *button_requisition = requisition;
5334 button_requisition->width = MAX (requisition.width, label_requisition.width);
5335 button_requisition->height = MAX (requisition.height, label_requisition.height);
5340 psppire_sheet_forall (GtkContainer *container,
5341 gboolean include_internals,
5342 GtkCallback callback,
5343 gpointer callback_data)
5345 PsppireSheet *sheet = PSPPIRE_SHEET (container);
5347 g_return_if_fail (callback != NULL);
5349 if (sheet->button && sheet->button->parent)
5350 (* callback) (sheet->button, callback_data);
5352 if (sheet->entry_widget && GTK_IS_CONTAINER (sheet->entry_widget))
5353 (* callback) (sheet->entry_widget, callback_data);
5358 psppire_sheet_get_model (const PsppireSheet *sheet)
5360 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
5362 return sheet->model;
5366 PsppireSheetButton *
5367 psppire_sheet_button_new (void)
5369 PsppireSheetButton *button = g_malloc (sizeof (PsppireSheetButton));
5371 button->state = GTK_STATE_NORMAL;
5372 button->label = NULL;
5373 button->label_visible = TRUE;
5374 button->justification = GTK_JUSTIFY_FILL;
5375 button->overstruck = FALSE;
5382 psppire_sheet_button_free (PsppireSheetButton *button)
5384 if (!button) return ;
5386 g_free (button->label);
5391 append_cell_text (GString *string, const PsppireSheet *sheet, gint r, gint c)
5393 gchar *celltext = psppire_sheet_cell_get_text (sheet, r, c);
5395 if ( NULL == celltext)
5398 g_string_append (string, celltext);
5404 range_to_text (const PsppireSheet *sheet)
5409 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
5412 string = g_string_sized_new (80);
5414 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
5416 for (c = sheet->range.col0; c < sheet->range.coli; ++c)
5418 append_cell_text (string, sheet, r, c);
5419 g_string_append (string, "\t");
5421 append_cell_text (string, sheet, r, c);
5422 if ( r < sheet->range.rowi)
5423 g_string_append (string, "\n");
5430 range_to_html (const PsppireSheet *sheet)
5435 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
5438 string = g_string_sized_new (480);
5440 g_string_append (string, "<html>\n");
5441 g_string_append (string, "<body>\n");
5442 g_string_append (string, "<table>\n");
5443 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
5445 g_string_append (string, "<tr>\n");
5446 for (c = sheet->range.col0; c <= sheet->range.coli; ++c)
5448 g_string_append (string, "<td>");
5449 append_cell_text (string, sheet, r, c);
5450 g_string_append (string, "</td>\n");
5452 g_string_append (string, "</tr>\n");
5454 g_string_append (string, "</table>\n");
5455 g_string_append (string, "</body>\n");
5456 g_string_append (string, "</html>\n");
5468 primary_get_cb (GtkClipboard *clipboard,
5469 GtkSelectionData *selection_data,
5473 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5474 GString *string = NULL;
5478 case SELECT_FMT_TEXT:
5479 string = range_to_text (sheet);
5481 case SELECT_FMT_HTML:
5482 string = range_to_html (sheet);
5485 g_assert_not_reached ();
5488 gtk_selection_data_set (selection_data, selection_data->target,
5490 (const guchar *) string->str, string->len);
5491 g_string_free (string, TRUE);
5495 primary_clear_cb (GtkClipboard *clipboard,
5498 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5499 if ( ! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
5502 psppire_sheet_real_unselect_range (sheet, NULL);
5506 psppire_sheet_update_primary_selection (PsppireSheet *sheet)
5508 static const GtkTargetEntry targets[] = {
5509 { "UTF8_STRING", 0, SELECT_FMT_TEXT },
5510 { "STRING", 0, SELECT_FMT_TEXT },
5511 { "TEXT", 0, SELECT_FMT_TEXT },
5512 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
5513 { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
5514 { "text/plain", 0, SELECT_FMT_TEXT },
5515 { "text/html", 0, SELECT_FMT_HTML }
5518 GtkClipboard *clipboard;
5520 if (!GTK_WIDGET_REALIZED (sheet))
5523 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (sheet),
5524 GDK_SELECTION_PRIMARY);
5526 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
5528 if (!gtk_clipboard_set_with_owner (clipboard, targets,
5529 G_N_ELEMENTS (targets),
5530 primary_get_cb, primary_clear_cb,
5532 primary_clear_cb (clipboard, sheet);
5536 if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (sheet))
5537 gtk_clipboard_clear (clipboard);