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, 2, 2 };
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;
1638 sheet->active_cell.row = row;
1640 g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, row);
1641 psppire_sheet_real_select_range (sheet, NULL);
1646 psppire_sheet_select_column (PsppireSheet *sheet, gint column)
1648 g_return_if_fail (sheet != NULL);
1649 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1651 if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis))
1654 if (sheet->state != PSPPIRE_SHEET_NORMAL)
1655 psppire_sheet_real_unselect_range (sheet, NULL);
1657 sheet->state = PSPPIRE_SHEET_COLUMN_SELECTED;
1658 sheet->range.row0 = 0;
1659 sheet->range.col0 = column;
1660 sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1661 sheet->range.coli = column;
1662 sheet->active_cell.col = column;
1664 g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, column);
1665 psppire_sheet_real_select_range (sheet, NULL);
1672 psppire_sheet_range_isvisible (const PsppireSheet *sheet,
1673 const PsppireSheetRange *range)
1675 g_return_val_if_fail (sheet != NULL, FALSE);
1677 if (range->row0 < 0 || range->row0 >= psppire_axis_unit_count (sheet->vaxis))
1680 if (range->rowi < 0 || range->rowi >= psppire_axis_unit_count (sheet->vaxis))
1683 if (range->col0 < 0 || range->col0 >= psppire_axis_unit_count (sheet->haxis))
1686 if (range->coli < 0 || range->coli >= psppire_axis_unit_count (sheet->haxis))
1689 if (range->rowi < min_visible_row (sheet))
1692 if (range->row0 > max_visible_row (sheet))
1695 if (range->coli < min_visible_column (sheet))
1698 if (range->col0 > max_visible_column (sheet))
1705 psppire_sheet_cell_isvisible (PsppireSheet *sheet,
1706 gint row, gint column)
1708 PsppireSheetRange range;
1711 range.col0 = column;
1713 range.coli = column;
1715 return psppire_sheet_range_isvisible (sheet, &range);
1719 psppire_sheet_get_visible_range (PsppireSheet *sheet, PsppireSheetRange *range)
1721 g_return_if_fail (sheet != NULL);
1722 g_return_if_fail (PSPPIRE_IS_SHEET (sheet)) ;
1723 g_return_if_fail (range != NULL);
1725 range->row0 = min_visible_row (sheet);
1726 range->col0 = min_visible_column (sheet);
1727 range->rowi = max_visible_row (sheet);
1728 range->coli = max_visible_column (sheet);
1733 psppire_sheet_set_scroll_adjustments (PsppireSheet *sheet,
1734 GtkAdjustment *hadjustment,
1735 GtkAdjustment *vadjustment)
1737 if ( sheet->vadjustment != vadjustment )
1739 if (sheet->vadjustment)
1740 g_object_unref (sheet->vadjustment);
1741 sheet->vadjustment = vadjustment;
1745 g_object_ref (vadjustment);
1747 g_signal_connect (sheet->vadjustment, "value_changed",
1748 G_CALLBACK (vadjustment_value_changed),
1753 if ( sheet->hadjustment != hadjustment )
1755 if (sheet->hadjustment)
1756 g_object_unref (sheet->hadjustment);
1758 sheet->hadjustment = hadjustment;
1762 g_object_ref (hadjustment);
1764 g_signal_connect (sheet->hadjustment, "value_changed",
1765 G_CALLBACK (hadjustment_value_changed),
1773 psppire_sheet_finalize (GObject *object)
1775 PsppireSheet *sheet;
1777 g_return_if_fail (object != NULL);
1778 g_return_if_fail (PSPPIRE_IS_SHEET (object));
1780 sheet = PSPPIRE_SHEET (object);
1782 if (G_OBJECT_CLASS (parent_class)->finalize)
1783 (*G_OBJECT_CLASS (parent_class)->finalize) (object);
1787 psppire_sheet_dispose (GObject *object)
1789 PsppireSheet *sheet = PSPPIRE_SHEET (object);
1791 g_return_if_fail (object != NULL);
1792 g_return_if_fail (PSPPIRE_IS_SHEET (object));
1794 if ( sheet->dispose_has_run )
1797 sheet->dispose_has_run = TRUE;
1799 if ( sheet->cell_padding)
1800 g_boxed_free (GTK_TYPE_BORDER, sheet->cell_padding);
1802 if (sheet->model) g_object_unref (sheet->model);
1803 if (sheet->vaxis) g_object_unref (sheet->vaxis);
1804 if (sheet->haxis) g_object_unref (sheet->haxis);
1806 g_object_unref (sheet->button);
1807 sheet->button = NULL;
1809 /* unref adjustments */
1810 if (sheet->hadjustment)
1812 g_signal_handlers_disconnect_matched (sheet->hadjustment,
1813 G_SIGNAL_MATCH_DATA,
1817 g_object_unref (sheet->hadjustment);
1818 sheet->hadjustment = NULL;
1821 if (sheet->vadjustment)
1823 g_signal_handlers_disconnect_matched (sheet->vadjustment,
1824 G_SIGNAL_MATCH_DATA,
1828 g_object_unref (sheet->vadjustment);
1830 sheet->vadjustment = NULL;
1833 if (G_OBJECT_CLASS (parent_class)->dispose)
1834 (*G_OBJECT_CLASS (parent_class)->dispose) (object);
1838 psppire_sheet_style_set (GtkWidget *widget,
1839 GtkStyle *previous_style)
1841 PsppireSheet *sheet;
1843 g_return_if_fail (widget != NULL);
1844 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1846 if (GTK_WIDGET_CLASS (parent_class)->style_set)
1847 (*GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous_style);
1849 sheet = PSPPIRE_SHEET (widget);
1851 if (GTK_WIDGET_REALIZED (widget))
1853 gtk_style_set_background (widget->style, widget->window, widget->state);
1856 set_entry_widget_font (sheet);
1859 #define BORDER_WIDTH 3
1862 psppire_sheet_realize (GtkWidget *widget)
1864 PsppireSheet *sheet;
1865 GdkWindowAttr attributes;
1866 const gint attributes_mask =
1867 GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_CURSOR;
1870 GdkColormap *colormap;
1871 GdkDisplay *display;
1873 g_return_if_fail (widget != NULL);
1874 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1876 sheet = PSPPIRE_SHEET (widget);
1878 colormap = gtk_widget_get_colormap (widget);
1879 display = gtk_widget_get_display (widget);
1881 attributes.window_type = GDK_WINDOW_CHILD;
1882 attributes.x = widget->allocation.x;
1883 attributes.y = widget->allocation.y;
1884 attributes.width = widget->allocation.width;
1885 attributes.height = widget->allocation.height;
1886 attributes.wclass = GDK_INPUT_OUTPUT;
1888 attributes.visual = gtk_widget_get_visual (widget);
1889 attributes.colormap = colormap;
1891 attributes.event_mask = gtk_widget_get_events (widget);
1892 attributes.event_mask |= (GDK_EXPOSURE_MASK |
1893 GDK_BUTTON_PRESS_MASK |
1894 GDK_BUTTON_RELEASE_MASK |
1895 GDK_KEY_PRESS_MASK |
1896 GDK_ENTER_NOTIFY_MASK |
1897 GDK_LEAVE_NOTIFY_MASK |
1898 GDK_POINTER_MOTION_MASK |
1899 GDK_POINTER_MOTION_HINT_MASK);
1901 attributes.cursor = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
1904 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1906 gdk_window_set_user_data (widget->window, sheet);
1908 widget->style = gtk_style_attach (widget->style, widget->window);
1910 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1912 gdk_color_parse ("white", &sheet->color[BG_COLOR]);
1913 gdk_colormap_alloc_color (colormap, &sheet->color[BG_COLOR], FALSE,
1915 gdk_color_parse ("gray", &sheet->color[GRID_COLOR]);
1916 gdk_colormap_alloc_color (colormap, &sheet->color[GRID_COLOR], FALSE,
1921 attributes.width = sheet->column_title_area.width;
1922 attributes.height = sheet->column_title_area.height;
1925 /* column - title window */
1926 sheet->column_title_window =
1927 gdk_window_new (widget->window, &attributes, attributes_mask);
1928 gdk_window_set_user_data (sheet->column_title_window, sheet);
1929 gtk_style_set_background (widget->style, sheet->column_title_window,
1935 attributes.width = sheet->row_title_area.width;
1936 attributes.height = sheet->row_title_area.height;
1938 /* row - title window */
1939 sheet->row_title_window = gdk_window_new (widget->window,
1940 &attributes, attributes_mask);
1941 gdk_window_set_user_data (sheet->row_title_window, sheet);
1942 gtk_style_set_background (widget->style, sheet->row_title_window,
1945 /* sheet - window */
1946 attributes.cursor = gdk_cursor_new_for_display (display, GDK_PLUS);
1951 sheet->sheet_window = gdk_window_new (widget->window,
1952 &attributes, attributes_mask);
1953 gdk_window_set_user_data (sheet->sheet_window, sheet);
1955 gdk_cursor_unref (attributes.cursor);
1957 gdk_window_set_background (sheet->sheet_window, &widget->style->white);
1958 gdk_window_show (sheet->sheet_window);
1961 sheet->fg_gc = gdk_gc_new (widget->window);
1962 sheet->bg_gc = gdk_gc_new (widget->window);
1964 values.foreground = widget->style->white;
1965 values.function = GDK_INVERT;
1966 values.subwindow_mode = GDK_INCLUDE_INFERIORS;
1967 values.line_width = BORDER_WIDTH;
1969 sheet->xor_gc = gdk_gc_new_with_values (widget->window,
1978 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
1979 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
1981 gtk_widget_set_parent_window (sheet->button, sheet->sheet_window);
1982 gtk_widget_set_parent (sheet->button, GTK_WIDGET (sheet));
1984 sheet->button->style = gtk_style_attach (sheet->button->style,
1985 sheet->sheet_window);
1988 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
1990 if (sheet->column_titles_visible)
1991 gdk_window_show (sheet->column_title_window);
1992 if (sheet->row_titles_visible)
1993 gdk_window_show (sheet->row_title_window);
1995 sheet->hover_window = create_hover_window ();
1997 draw_row_title_buttons (sheet);
1998 draw_column_title_buttons (sheet);
2000 psppire_sheet_update_primary_selection (sheet);
2003 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
2007 create_global_button (PsppireSheet *sheet)
2009 sheet->button = gtk_button_new_with_label (" ");
2011 GTK_WIDGET_UNSET_FLAGS(sheet->button, GTK_CAN_FOCUS);
2013 g_object_ref_sink (sheet->button);
2015 g_signal_connect (sheet->button,
2017 G_CALLBACK (global_button_clicked),
2022 size_allocate_global_button (PsppireSheet *sheet)
2024 GtkAllocation allocation;
2026 if (!sheet->column_titles_visible) return;
2027 if (!sheet->row_titles_visible) return;
2029 gtk_widget_size_request (sheet->button, NULL);
2033 allocation.width = sheet->row_title_area.width;
2034 allocation.height = sheet->column_title_area.height;
2036 gtk_widget_size_allocate (sheet->button, &allocation);
2040 global_button_clicked (GtkWidget *widget, gpointer data)
2042 psppire_sheet_click_cell (PSPPIRE_SHEET (data), -1, -1);
2047 psppire_sheet_unrealize (GtkWidget *widget)
2049 PsppireSheet *sheet;
2051 g_return_if_fail (widget != NULL);
2052 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
2054 sheet = PSPPIRE_SHEET (widget);
2056 gdk_cursor_unref (sheet->cursor_drag);
2057 sheet->cursor_drag = NULL;
2059 gdk_colormap_free_colors (gtk_widget_get_colormap (widget),
2060 sheet->color, n_COLORS);
2062 g_object_unref (sheet->xor_gc);
2063 g_object_unref (sheet->fg_gc);
2064 g_object_unref (sheet->bg_gc);
2066 destroy_hover_window (sheet->hover_window);
2068 gdk_window_destroy (sheet->sheet_window);
2069 gdk_window_destroy (sheet->column_title_window);
2070 gdk_window_destroy (sheet->row_title_window);
2072 gtk_widget_unparent (sheet->entry_widget);
2073 if (sheet->button != NULL)
2074 gtk_widget_unparent (sheet->button);
2076 if (GTK_WIDGET_CLASS (parent_class)->unrealize)
2077 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
2081 psppire_sheet_map (GtkWidget *widget)
2083 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2085 g_return_if_fail (widget != NULL);
2086 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
2088 if (!GTK_WIDGET_MAPPED (widget))
2090 GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
2092 gdk_window_show (widget->window);
2093 gdk_window_show (sheet->sheet_window);
2095 if (sheet->column_titles_visible)
2097 draw_column_title_buttons (sheet);
2098 gdk_window_show (sheet->column_title_window);
2100 if (sheet->row_titles_visible)
2102 draw_row_title_buttons (sheet);
2103 gdk_window_show (sheet->row_title_window);
2106 if (!GTK_WIDGET_MAPPED (sheet->entry_widget)
2107 && sheet->active_cell.row >= 0
2108 && sheet->active_cell.col >= 0 )
2110 gtk_widget_show (sheet->entry_widget);
2111 gtk_widget_map (sheet->entry_widget);
2114 if (!GTK_WIDGET_MAPPED (sheet->button))
2116 gtk_widget_show (sheet->button);
2117 gtk_widget_map (sheet->button);
2120 redraw_range (sheet, NULL);
2121 change_active_cell (sheet,
2122 sheet->active_cell.row,
2123 sheet->active_cell.col);
2128 psppire_sheet_unmap (GtkWidget *widget)
2130 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2132 if (!GTK_WIDGET_MAPPED (widget))
2135 GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
2137 gdk_window_hide (sheet->sheet_window);
2138 if (sheet->column_titles_visible)
2139 gdk_window_hide (sheet->column_title_window);
2140 if (sheet->row_titles_visible)
2141 gdk_window_hide (sheet->row_title_window);
2142 gdk_window_hide (widget->window);
2144 gtk_widget_unmap (sheet->entry_widget);
2145 gtk_widget_unmap (sheet->button);
2146 gtk_widget_unmap (sheet->hover_window->window);
2149 /* get cell attributes of the given cell */
2150 /* TRUE means that the cell is currently allocated */
2151 static gboolean psppire_sheet_get_attributes (const PsppireSheet *sheet,
2153 PsppireSheetCellAttr *attributes);
2158 psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint col)
2160 PangoLayout *layout;
2161 PangoRectangle text;
2162 PangoFontDescription *font_desc = GTK_WIDGET (sheet)->style->font_desc;
2167 PsppireSheetCellAttr attributes;
2170 g_return_if_fail (sheet != NULL);
2172 /* bail now if we aren't yet drawable */
2173 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
2176 row >= psppire_axis_unit_count (sheet->vaxis))
2180 col >= psppire_axis_unit_count (sheet->haxis))
2183 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2185 /* select GC for background rectangle */
2186 gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground);
2187 gdk_gc_set_foreground (sheet->bg_gc, &attributes.background);
2189 rectangle_from_cell (sheet, row, col, &area);
2191 gdk_gc_set_line_attributes (sheet->fg_gc, 1, 0, 0, 0);
2193 if (sheet->show_grid)
2195 gdk_gc_set_foreground (sheet->bg_gc, &sheet->color[GRID_COLOR]);
2197 gdk_draw_rectangle (sheet->sheet_window,
2201 area.width, area.height);
2205 label = psppire_sheet_cell_get_text (sheet, row, col);
2210 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), label);
2211 dispose_string (sheet, label);
2214 pango_layout_set_font_description (layout, font_desc);
2216 pango_layout_get_pixel_extents (layout, NULL, &text);
2218 gdk_gc_set_clip_rectangle (sheet->fg_gc, &area);
2220 font_height = pango_font_description_get_size (font_desc);
2221 if ( !pango_font_description_get_size_is_absolute (font_desc))
2222 font_height /= PANGO_SCALE;
2225 if ( sheet->cell_padding )
2227 area.x += sheet->cell_padding->left;
2228 area.width -= sheet->cell_padding->right
2229 + sheet->cell_padding->left;
2231 area.y += sheet->cell_padding->top;
2232 area.height -= sheet->cell_padding->bottom
2234 sheet->cell_padding->top;
2237 /* Centre the text vertically */
2238 area.y += (area.height - font_height) / 2.0;
2240 switch (attributes.justification)
2242 case GTK_JUSTIFY_RIGHT:
2243 area.x += area.width - text.width;
2245 case GTK_JUSTIFY_CENTER:
2246 area.x += (area.width - text.width) / 2.0;
2248 case GTK_JUSTIFY_LEFT:
2252 g_critical ("Unhandled justification %d in column %d\n",
2253 attributes.justification, col);
2257 gdk_draw_layout (sheet->sheet_window, sheet->fg_gc,
2262 gdk_gc_set_clip_rectangle (sheet->fg_gc, NULL);
2263 g_object_unref (layout);
2268 draw_sheet_region (PsppireSheet *sheet, GdkRegion *region)
2270 PsppireSheetRange range;
2275 PsppireSheetRange drawing_range;
2277 gdk_region_get_clipbox (region, &area);
2279 y = area.y + sheet->vadjustment->value;
2280 x = area.x + sheet->hadjustment->value;
2282 if ( sheet->column_titles_visible)
2283 y -= sheet->column_title_area.height;
2285 if ( sheet->row_titles_visible)
2286 x -= sheet->row_title_area.width;
2288 maximize_int (&x, 0);
2289 maximize_int (&y, 0);
2291 range.row0 = row_from_ypixel (sheet, y);
2292 range.rowi = row_from_ypixel (sheet, y + area.height);
2294 range.col0 = column_from_xpixel (sheet, x);
2295 range.coli = column_from_xpixel (sheet, x + area.width);
2297 g_return_if_fail (sheet != NULL);
2298 g_return_if_fail (PSPPIRE_SHEET (sheet));
2300 if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (sheet))) return;
2301 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2302 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
2305 drawing_range.row0 = MAX (range.row0, min_visible_row (sheet));
2306 drawing_range.col0 = MAX (range.col0, min_visible_column (sheet));
2307 drawing_range.rowi = MIN (range.rowi, max_visible_row (sheet));
2308 drawing_range.coli = MIN (range.coli, max_visible_column (sheet));
2310 g_return_if_fail (drawing_range.rowi >= drawing_range.row0);
2311 g_return_if_fail (drawing_range.coli >= drawing_range.col0);
2313 for (i = drawing_range.row0; i <= drawing_range.rowi; i++)
2315 for (j = drawing_range.col0; j <= drawing_range.coli; j++)
2316 psppire_sheet_cell_draw (sheet, i, j);
2319 if (sheet->state != PSPPIRE_SHEET_NORMAL &&
2320 psppire_sheet_range_isvisible (sheet, &sheet->range))
2321 psppire_sheet_range_draw_selection (sheet, drawing_range);
2324 if (sheet->state == GTK_STATE_NORMAL &&
2325 sheet->active_cell.row >= drawing_range.row0 &&
2326 sheet->active_cell.row <= drawing_range.rowi &&
2327 sheet->active_cell.col >= drawing_range.col0 &&
2328 sheet->active_cell.col <= drawing_range.coli)
2329 psppire_sheet_show_entry_widget (sheet);
2334 psppire_sheet_range_draw_selection (PsppireSheet *sheet, PsppireSheetRange range)
2338 PsppireSheetRange aux;
2340 if (range.col0 > sheet->range.coli || range.coli < sheet->range.col0 ||
2341 range.row0 > sheet->range.rowi || range.rowi < sheet->range.row0)
2344 if (!psppire_sheet_range_isvisible (sheet, &range)) return;
2345 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2349 range.col0 = MAX (sheet->range.col0, range.col0);
2350 range.coli = MIN (sheet->range.coli, range.coli);
2351 range.row0 = MAX (sheet->range.row0, range.row0);
2352 range.rowi = MIN (sheet->range.rowi, range.rowi);
2354 range.col0 = MAX (range.col0, min_visible_column (sheet));
2355 range.coli = MIN (range.coli, max_visible_column (sheet));
2356 range.row0 = MAX (range.row0, min_visible_row (sheet));
2357 range.rowi = MIN (range.rowi, max_visible_row (sheet));
2359 for (i = range.row0; i <= range.rowi; i++)
2361 for (j = range.col0; j <= range.coli; j++)
2363 if (psppire_sheet_cell_get_state (sheet, i, j) == GTK_STATE_SELECTED)
2365 rectangle_from_cell (sheet, i, j, &area);
2367 if (i == sheet->range.row0)
2369 area.y = area.y + 2;
2370 area.height = area.height - 2;
2372 if (i == sheet->range.rowi) area.height = area.height - 3;
2373 if (j == sheet->range.col0)
2375 area.x = area.x + 2;
2376 area.width = area.width - 2;
2378 if (j == sheet->range.coli) area.width = area.width - 3;
2380 if (i != sheet->active_cell.row || j != sheet->active_cell.col)
2382 gdk_draw_rectangle (sheet->sheet_window,
2385 area.x + 1, area.y + 1,
2386 area.width, area.height);
2393 psppire_sheet_draw_border (sheet, sheet->range);
2397 safe_strcmp (const gchar *s1, const gchar *s2)
2399 if ( !s1 && !s2) return 0;
2400 if ( !s1) return -1;
2401 if ( !s2) return +1;
2402 return strcmp (s1, s2);
2406 psppire_sheet_set_cell (PsppireSheet *sheet, gint row, gint col,
2407 GtkJustification justification,
2410 PsppireSheetModel *model ;
2413 g_return_if_fail (sheet != NULL);
2414 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2416 if (col >= psppire_axis_unit_count (sheet->haxis)
2417 || row >= psppire_axis_unit_count (sheet->vaxis))
2420 if (col < 0 || row < 0) return;
2422 model = psppire_sheet_get_model (sheet);
2424 old_text = psppire_sheet_model_get_string (model, row, col);
2426 if (0 != safe_strcmp (old_text, text))
2428 g_signal_handler_block (sheet->model, sheet->update_handler_id);
2429 psppire_sheet_model_set_string (model, text, row, col);
2430 g_signal_handler_unblock (sheet->model, sheet->update_handler_id);
2433 if ( psppire_sheet_model_free_strings (model))
2439 psppire_sheet_cell_clear (PsppireSheet *sheet, gint row, gint column)
2441 PsppireSheetRange range;
2443 g_return_if_fail (sheet != NULL);
2444 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2445 if (column >= psppire_axis_unit_count (sheet->haxis) ||
2446 row >= psppire_axis_unit_count (sheet->vaxis)) return;
2448 if (column < 0 || row < 0) return;
2452 range.col0 = min_visible_column (sheet);
2453 range.coli = max_visible_column (sheet);
2455 psppire_sheet_real_cell_clear (sheet, row, column);
2457 redraw_range (sheet, &range);
2461 psppire_sheet_real_cell_clear (PsppireSheet *sheet, gint row, gint column)
2463 PsppireSheetModel *model = psppire_sheet_get_model (sheet);
2465 gchar *old_text = psppire_sheet_cell_get_text (sheet, row, column);
2467 if (old_text && strlen (old_text) > 0 )
2469 psppire_sheet_model_datum_clear (model, row, column);
2472 dispose_string (sheet, old_text);
2476 psppire_sheet_cell_get_text (const PsppireSheet *sheet, gint row, gint col)
2478 PsppireSheetModel *model;
2479 g_return_val_if_fail (sheet != NULL, NULL);
2480 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
2482 if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis))
2484 if (col < 0 || row < 0) return NULL;
2486 model = psppire_sheet_get_model (sheet);
2491 return psppire_sheet_model_get_string (model, row, col);
2496 psppire_sheet_cell_get_state (PsppireSheet *sheet, gint row, gint col)
2499 PsppireSheetRange *range;
2501 g_return_val_if_fail (sheet != NULL, 0);
2502 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2503 if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis)) return 0;
2504 if (col < 0 || row < 0) return 0;
2506 state = sheet->state;
2507 range = &sheet->range;
2511 case PSPPIRE_SHEET_NORMAL:
2512 return GTK_STATE_NORMAL;
2514 case PSPPIRE_SHEET_ROW_SELECTED:
2515 if (row >= range->row0 && row <= range->rowi)
2516 return GTK_STATE_SELECTED;
2518 case PSPPIRE_SHEET_COLUMN_SELECTED:
2519 if (col >= range->col0 && col <= range->coli)
2520 return GTK_STATE_SELECTED;
2522 case PSPPIRE_SHEET_RANGE_SELECTED:
2523 if (row >= range->row0 && row <= range->rowi && \
2524 col >= range->col0 && col <= range->coli)
2525 return GTK_STATE_SELECTED;
2528 return GTK_STATE_NORMAL;
2531 /* Convert X, Y (in pixels) to *ROW, *COLUMN
2532 If the function returns FALSE, then the results will be unreliable.
2535 psppire_sheet_get_pixel_info (PsppireSheet *sheet,
2543 *column = -G_MAXINT;
2545 g_return_val_if_fail (sheet != NULL, 0);
2546 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2548 /* bounds checking, return false if the user clicked
2556 if ( sheet->column_titles_visible)
2557 y -= sheet->column_title_area.height;
2559 y += sheet->vadjustment->value;
2561 if ( y < 0 && sheet->column_titles_visible)
2567 trow = row_from_ypixel (sheet, y);
2568 if (trow > psppire_axis_unit_count (sheet->vaxis))
2574 if ( sheet->row_titles_visible)
2575 x -= sheet->row_title_area.width;
2577 x += sheet->hadjustment->value;
2579 if ( x < 0 && sheet->row_titles_visible)
2585 tcol = column_from_xpixel (sheet, x);
2586 if (tcol > psppire_axis_unit_count (sheet->haxis))
2596 psppire_sheet_get_cell_area (PsppireSheet *sheet,
2601 g_return_val_if_fail (sheet != NULL, 0);
2602 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2604 if (row >= psppire_axis_unit_count (sheet->vaxis) || column >= psppire_axis_unit_count (sheet->haxis))
2607 area->x = (column == -1) ? 0 : psppire_axis_start_pixel (sheet->haxis, column);
2608 area->y = (row == -1) ? 0 : psppire_axis_start_pixel (sheet->vaxis, row);
2610 area->width= (column == -1) ? sheet->row_title_area.width
2611 : psppire_axis_unit_size (sheet->haxis, column);
2613 area->height= (row == -1) ? sheet->column_title_area.height
2614 : psppire_axis_unit_size (sheet->vaxis, row);
2620 psppire_sheet_set_active_cell (PsppireSheet *sheet, gint row, gint col)
2622 g_return_if_fail (sheet != NULL);
2623 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2625 if (row < -1 || col < -1)
2628 if (row >= psppire_axis_unit_count (sheet->vaxis)
2630 col >= psppire_axis_unit_count (sheet->haxis))
2633 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2636 if ( row == -1 || col == -1)
2638 psppire_sheet_hide_entry_widget (sheet);
2642 change_active_cell (sheet, row, col);
2646 psppire_sheet_get_active_cell (PsppireSheet *sheet, gint *row, gint *column)
2648 g_return_if_fail (sheet != NULL);
2649 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2651 if ( row ) *row = sheet->active_cell.row;
2652 if (column) *column = sheet->active_cell.col;
2656 entry_load_text (PsppireSheet *sheet)
2660 GtkJustification justification;
2661 PsppireSheetCellAttr attributes;
2663 if (!GTK_WIDGET_VISIBLE (sheet->entry_widget)) return;
2664 if (sheet->state != GTK_STATE_NORMAL) return;
2666 row = sheet->active_cell.row;
2667 col = sheet->active_cell.col;
2669 if (row < 0 || col < 0) return;
2671 text = gtk_entry_get_text (psppire_sheet_get_entry (sheet));
2673 if (text && strlen (text) > 0)
2675 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2676 justification = attributes.justification;
2677 psppire_sheet_set_cell (sheet, row, col, justification, text);
2683 psppire_sheet_hide_entry_widget (PsppireSheet *sheet)
2685 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2688 if (sheet->active_cell.row < 0 ||
2689 sheet->active_cell.col < 0) return;
2691 gtk_widget_hide (sheet->entry_widget);
2692 gtk_widget_unmap (sheet->entry_widget);
2694 GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2698 change_active_cell (PsppireSheet *sheet, gint row, gint col)
2700 gint old_row, old_col;
2702 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2704 if (row < 0 || col < 0)
2707 if ( row > psppire_axis_unit_count (sheet->vaxis)
2708 || col > psppire_axis_unit_count (sheet->haxis))
2711 if (sheet->state != PSPPIRE_SHEET_NORMAL)
2713 sheet->state = PSPPIRE_SHEET_NORMAL;
2714 psppire_sheet_real_unselect_range (sheet, NULL);
2717 old_row = sheet->active_cell.row;
2718 old_col = sheet->active_cell.col;
2720 /* Erase the old cell */
2721 psppire_sheet_draw_active_cell (sheet);
2723 entry_load_text (sheet);
2725 sheet->range.row0 = row;
2726 sheet->range.col0 = col;
2727 sheet->range.rowi = row;
2728 sheet->range.coli = col;
2729 sheet->active_cell.row = row;
2730 sheet->active_cell.col = col;
2731 sheet->selection_cell.row = row;
2732 sheet->selection_cell.col = col;
2734 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
2736 GTK_WIDGET_UNSET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2738 psppire_sheet_draw_active_cell (sheet);
2739 psppire_sheet_show_entry_widget (sheet);
2741 GTK_WIDGET_SET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2743 g_signal_emit (sheet, sheet_signals [ACTIVATE], 0,
2744 row, col, old_row, old_col);
2749 psppire_sheet_show_entry_widget (PsppireSheet *sheet)
2751 GtkEntry *sheet_entry;
2752 PsppireSheetCellAttr attributes;
2756 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2758 row = sheet->active_cell.row;
2759 col = sheet->active_cell.col;
2761 /* Don't show the active cell, if there is no active cell: */
2762 if (! (row >= 0 && col >= 0)) /* e.g row or coll == -1. */
2765 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2766 if (sheet->state != PSPPIRE_SHEET_NORMAL) return;
2767 if (PSPPIRE_SHEET_IN_SELECTION (sheet)) return;
2769 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2771 sheet_entry = psppire_sheet_get_entry (sheet);
2773 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2775 if (GTK_IS_ENTRY (sheet_entry))
2777 gchar *text = psppire_sheet_cell_get_text (sheet, row, col);
2778 const gchar *old_text = gtk_entry_get_text (GTK_ENTRY (sheet_entry));
2781 text = g_strdup ("");
2783 if (strcmp (old_text, text) != 0)
2784 gtk_entry_set_text (sheet_entry, text);
2786 dispose_string (sheet, text);
2789 switch (attributes.justification)
2791 case GTK_JUSTIFY_RIGHT:
2792 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 1.0);
2794 case GTK_JUSTIFY_CENTER:
2795 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.5);
2797 case GTK_JUSTIFY_LEFT:
2799 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.0);
2805 psppire_sheet_size_allocate_entry (sheet);
2807 gtk_widget_set_sensitive (GTK_WIDGET (sheet_entry),
2808 psppire_sheet_model_is_editable (sheet->model,
2810 gtk_widget_map (sheet->entry_widget);
2814 psppire_sheet_draw_active_cell (PsppireSheet *sheet)
2817 PsppireSheetRange range;
2819 row = sheet->active_cell.row;
2820 col = sheet->active_cell.col;
2822 if (row < 0 || col < 0) return FALSE;
2824 if (!psppire_sheet_cell_isvisible (sheet, row, col))
2827 range.col0 = range.coli = col;
2828 range.row0 = range.rowi = row;
2830 psppire_sheet_draw_border (sheet, range);
2838 psppire_sheet_new_selection (PsppireSheet *sheet, PsppireSheetRange *range)
2840 gint i, j, mask1, mask2;
2841 gint state, selected;
2842 gint x, y, width, height;
2843 PsppireSheetRange new_range, aux_range;
2845 g_return_if_fail (sheet != NULL);
2847 if (range == NULL) range=&sheet->range;
2851 range->row0 = MIN (range->row0, sheet->range.row0);
2852 range->rowi = MAX (range->rowi, sheet->range.rowi);
2853 range->col0 = MIN (range->col0, sheet->range.col0);
2854 range->coli = MAX (range->coli, sheet->range.coli);
2856 range->row0 = MAX (range->row0, min_visible_row (sheet));
2857 range->rowi = MIN (range->rowi, max_visible_row (sheet));
2858 range->col0 = MAX (range->col0, min_visible_column (sheet));
2859 range->coli = MIN (range->coli, max_visible_column (sheet));
2861 aux_range.row0 = MAX (new_range.row0, min_visible_row (sheet));
2862 aux_range.rowi = MIN (new_range.rowi, max_visible_row (sheet));
2863 aux_range.col0 = MAX (new_range.col0, min_visible_column (sheet));
2864 aux_range.coli = MIN (new_range.coli, max_visible_column (sheet));
2866 for (i = range->row0; i <= range->rowi; i++)
2868 for (j = range->col0; j <= range->coli; j++)
2871 state = psppire_sheet_cell_get_state (sheet, i, j);
2872 selected= (i <= new_range.rowi && i >= new_range.row0 &&
2873 j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
2875 if (state == GTK_STATE_SELECTED && selected &&
2876 (i == sheet->range.row0 || i == sheet->range.rowi ||
2877 j == sheet->range.col0 || j == sheet->range.coli ||
2878 i == new_range.row0 || i == new_range.rowi ||
2879 j == new_range.col0 || j == new_range.coli))
2882 mask1 = i == sheet->range.row0 ? 1 : 0;
2883 mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1;
2884 mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1;
2885 mask1 = j == sheet->range.coli ? mask1 + 8 : mask1;
2887 mask2 = i == new_range.row0 ? 1 : 0;
2888 mask2 = i == new_range.rowi ? mask2 + 2 : mask2;
2889 mask2 = j == new_range.col0 ? mask2 + 4 : mask2;
2890 mask2 = j == new_range.coli ? mask2 + 8 : mask2;
2894 x = psppire_axis_start_pixel (sheet->haxis, j);
2895 y = psppire_axis_start_pixel (sheet->vaxis, i);
2896 width = psppire_axis_start_pixel (sheet->haxis, j)- x+
2897 psppire_axis_unit_size (sheet->haxis, j);
2898 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
2900 if (i == sheet->range.row0)
2903 height = height + 3;
2905 if (i == sheet->range.rowi) height = height + 3;
2906 if (j == sheet->range.col0)
2911 if (j == sheet->range.coli) width = width + 3;
2913 if (i != sheet->active_cell.row || j != sheet->active_cell.col)
2915 x = psppire_axis_start_pixel (sheet->haxis, j);
2916 y = psppire_axis_start_pixel (sheet->vaxis, i);
2917 width = psppire_axis_start_pixel (sheet->haxis, j)- x+
2918 psppire_axis_unit_size (sheet->haxis, j);
2920 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
2922 if (i == new_range.row0)
2925 height = height - 2;
2927 if (i == new_range.rowi) height = height - 3;
2928 if (j == new_range.col0)
2933 if (j == new_range.coli) width = width - 3;
2935 gdk_draw_rectangle (sheet->sheet_window,
2946 for (i = range->row0; i <= range->rowi; i++)
2948 for (j = range->col0; j <= range->coli; j++)
2951 state = psppire_sheet_cell_get_state (sheet, i, j);
2952 selected= (i <= new_range.rowi && i >= new_range.row0 &&
2953 j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
2955 if (state == GTK_STATE_SELECTED && !selected)
2958 x = psppire_axis_start_pixel (sheet->haxis, j);
2959 y = psppire_axis_start_pixel (sheet->vaxis, i);
2960 width = psppire_axis_start_pixel (sheet->haxis, j) - x + psppire_axis_unit_size (sheet->haxis, j);
2961 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
2963 if (i == sheet->range.row0)
2966 height = height + 3;
2968 if (i == sheet->range.rowi) height = height + 3;
2969 if (j == sheet->range.col0)
2974 if (j == sheet->range.coli) width = width + 3;
2980 for (i = range->row0; i <= range->rowi; i++)
2982 for (j = range->col0; j <= range->coli; j++)
2985 state = psppire_sheet_cell_get_state (sheet, i, j);
2986 selected= (i <= new_range.rowi && i >= new_range.row0 &&
2987 j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
2989 if (state != GTK_STATE_SELECTED && selected &&
2990 (i != sheet->active_cell.row || j != sheet->active_cell.col))
2993 x = psppire_axis_start_pixel (sheet->haxis, j);
2994 y = psppire_axis_start_pixel (sheet->vaxis, i);
2995 width = psppire_axis_start_pixel (sheet->haxis, j) - x + psppire_axis_unit_size (sheet->haxis, j);
2996 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
2998 if (i == new_range.row0)
3001 height = height - 2;
3003 if (i == new_range.rowi) height = height - 3;
3004 if (j == new_range.col0)
3009 if (j == new_range.coli) width = width - 3;
3011 gdk_draw_rectangle (sheet->sheet_window,
3022 for (i = aux_range.row0; i <= aux_range.rowi; i++)
3024 for (j = aux_range.col0; j <= aux_range.coli; j++)
3026 state = psppire_sheet_cell_get_state (sheet, i, j);
3028 mask1 = i == sheet->range.row0 ? 1 : 0;
3029 mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1;
3030 mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1;
3031 mask1 = j == sheet->range.coli ? mask1 + 8 : mask1;
3033 mask2 = i == new_range.row0 ? 1 : 0;
3034 mask2 = i == new_range.rowi ? mask2 + 2 : mask2;
3035 mask2 = j == new_range.col0 ? mask2 + 4 : mask2;
3036 mask2 = j == new_range.coli ? mask2 + 8 : mask2;
3037 if (mask2 != mask1 || (mask2 == mask1 && state != GTK_STATE_SELECTED))
3039 x = psppire_axis_start_pixel (sheet->haxis, j);
3040 y = psppire_axis_start_pixel (sheet->vaxis, i);
3041 width = psppire_axis_unit_size (sheet->haxis, j);
3042 height = psppire_axis_unit_size (sheet->vaxis, i);
3044 gdk_draw_rectangle (sheet->sheet_window,
3052 gdk_draw_rectangle (sheet->sheet_window,
3055 x + 1, y + height - 1,
3059 gdk_draw_rectangle (sheet->sheet_window,
3067 gdk_draw_rectangle (sheet->sheet_window,
3070 x + width - 1, y + 1,
3082 psppire_sheet_draw_border (PsppireSheet *sheet, PsppireSheetRange new_range)
3086 rectangle_from_range (sheet, &new_range, &area);
3088 gdk_draw_rectangle (sheet->sheet_window,
3092 area.width, area.height);
3097 psppire_sheet_real_select_range (PsppireSheet *sheet,
3098 const PsppireSheetRange *range)
3102 g_return_if_fail (sheet != NULL);
3104 if (range == NULL) range = &sheet->range;
3106 memcpy (&sheet->range, range, sizeof (*range));
3108 if (range->row0 < 0 || range->rowi < 0) return;
3109 if (range->col0 < 0 || range->coli < 0) return;
3111 state = sheet->state;
3114 if (range->coli != sheet->range.coli || range->col0 != sheet->range.col0 ||
3115 range->rowi != sheet->range.rowi || range->row0 != sheet->range.row0)
3117 psppire_sheet_new_selection (sheet, &sheet->range);
3121 psppire_sheet_range_draw_selection (sheet, sheet->range);
3125 psppire_sheet_update_primary_selection (sheet);
3127 g_signal_emit (sheet, sheet_signals[SELECT_RANGE], 0, &sheet->range);
3132 psppire_sheet_get_selected_range (PsppireSheet *sheet, PsppireSheetRange *range)
3134 g_return_if_fail (sheet != NULL);
3135 *range = sheet->range;
3140 psppire_sheet_select_range (PsppireSheet *sheet, const PsppireSheetRange *range)
3142 g_return_if_fail (sheet != NULL);
3144 if (range == NULL) range=&sheet->range;
3146 if (range->row0 < 0 || range->rowi < 0) return;
3147 if (range->col0 < 0 || range->coli < 0) return;
3150 if (sheet->state != PSPPIRE_SHEET_NORMAL)
3151 psppire_sheet_real_unselect_range (sheet, NULL);
3153 sheet->range.row0 = range->row0;
3154 sheet->range.rowi = range->rowi;
3155 sheet->range.col0 = range->col0;
3156 sheet->range.coli = range->coli;
3157 sheet->active_cell.row = range->row0;
3158 sheet->active_cell.col = range->col0;
3159 sheet->selection_cell.row = range->rowi;
3160 sheet->selection_cell.col = range->coli;
3162 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
3163 psppire_sheet_real_select_range (sheet, NULL);
3167 psppire_sheet_unselect_range (PsppireSheet *sheet)
3169 if (! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
3172 psppire_sheet_real_unselect_range (sheet, NULL);
3173 sheet->state = GTK_STATE_NORMAL;
3175 change_active_cell (sheet,
3176 sheet->active_cell.row, sheet->active_cell.col);
3181 psppire_sheet_real_unselect_range (PsppireSheet *sheet,
3182 const PsppireSheetRange *range)
3184 g_return_if_fail (sheet != NULL);
3185 g_return_if_fail (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)));
3188 range = &sheet->range;
3190 if (range->row0 < 0 || range->rowi < 0) return;
3191 if (range->col0 < 0 || range->coli < 0) return;
3193 g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, -1);
3194 g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, -1);
3196 sheet->range.row0 = -1;
3197 sheet->range.rowi = -1;
3198 sheet->range.col0 = -1;
3199 sheet->range.coli = -1;
3204 psppire_sheet_expose (GtkWidget *widget,
3205 GdkEventExpose *event)
3207 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3209 g_return_val_if_fail (event != NULL, FALSE);
3211 if (!GTK_WIDGET_DRAWABLE (widget))
3214 /* exposure events on the sheet */
3215 if (event->window == sheet->row_title_window &&
3216 sheet->row_titles_visible)
3218 draw_row_title_buttons_range (sheet,
3219 min_visible_row (sheet),
3220 max_visible_row (sheet));
3223 if (event->window == sheet->column_title_window &&
3224 sheet->column_titles_visible)
3226 draw_column_title_buttons_range (sheet,
3227 min_visible_column (sheet),
3228 max_visible_column (sheet));
3231 if (event->window == sheet->sheet_window)
3233 draw_sheet_region (sheet, event->region);
3236 if (sheet->state != PSPPIRE_SHEET_NORMAL)
3238 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
3239 psppire_sheet_range_draw (sheet, &sheet->range);
3241 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
3242 psppire_sheet_range_draw (sheet, &sheet->drag_range);
3244 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
3245 psppire_sheet_range_draw_selection (sheet, sheet->range);
3246 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
3247 draw_xor_rectangle (sheet, sheet->drag_range);
3251 if ((!PSPPIRE_SHEET_IN_XDRAG (sheet)) && (!PSPPIRE_SHEET_IN_YDRAG (sheet)))
3254 PsppireSheetRange range;
3255 range.row0 = range.rowi = sheet->active_cell.row;
3256 range.col0 = range.coli = sheet->active_cell.col;
3258 rectangle_from_range (sheet, &range, &rect);
3260 if (GDK_OVERLAP_RECTANGLE_OUT !=
3261 gdk_region_rect_in (event->region, &rect))
3263 psppire_sheet_draw_active_cell (sheet);
3269 (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
3276 psppire_sheet_button_press (GtkWidget *widget,
3277 GdkEventButton *event)
3279 PsppireSheet *sheet;
3280 GdkModifierType mods;
3285 g_return_val_if_fail (widget != NULL, FALSE);
3286 g_return_val_if_fail (PSPPIRE_IS_SHEET (widget), FALSE);
3287 g_return_val_if_fail (event != NULL, FALSE);
3289 sheet = PSPPIRE_SHEET (widget);
3291 /* Cancel any pending tooltips */
3292 if (sheet->motion_timer)
3294 g_source_remove (sheet->motion_timer);
3295 sheet->motion_timer = 0;
3298 gtk_widget_get_pointer (widget, &x, &y);
3299 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
3302 if (event->window == sheet->column_title_window)
3304 sheet->x_drag = event->x;
3305 g_signal_emit (sheet,
3306 sheet_signals[BUTTON_EVENT_COLUMN], 0,
3309 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
3311 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
3312 g_signal_emit (sheet,
3313 sheet_signals[DOUBLE_CLICK_COLUMN], 0, column);
3316 else if (event->window == sheet->row_title_window)
3318 g_signal_emit (sheet,
3319 sheet_signals[BUTTON_EVENT_ROW], 0,
3322 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
3324 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
3325 g_signal_emit (sheet,
3326 sheet_signals[DOUBLE_CLICK_ROW], 0, row);
3330 gdk_window_get_pointer (widget->window, NULL, NULL, &mods);
3332 if (! (mods & GDK_BUTTON1_MASK)) return TRUE;
3335 /* press on resize windows */
3336 if (event->window == sheet->column_title_window)
3338 sheet->x_drag = event->x;
3340 if (on_column_boundary (sheet, sheet->x_drag, &sheet->drag_cell.col))
3342 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
3343 gdk_pointer_grab (sheet->column_title_window, FALSE,
3344 GDK_POINTER_MOTION_HINT_MASK |
3345 GDK_BUTTON1_MOTION_MASK |
3346 GDK_BUTTON_RELEASE_MASK,
3347 NULL, NULL, event->time);
3349 draw_xor_vline (sheet);
3354 if (event->window == sheet->row_title_window)
3356 sheet->y_drag = event->y;
3358 if (on_row_boundary (sheet, sheet->y_drag, &sheet->drag_cell.row))
3360 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
3361 gdk_pointer_grab (sheet->row_title_window, FALSE,
3362 GDK_POINTER_MOTION_HINT_MASK |
3363 GDK_BUTTON1_MOTION_MASK |
3364 GDK_BUTTON_RELEASE_MASK,
3365 NULL, NULL, event->time);
3367 draw_xor_hline (sheet);
3372 /* the sheet itself does not handle other than single click events */
3373 if (event->type != GDK_BUTTON_PRESS) return FALSE;
3375 /* selections on the sheet */
3376 if (event->window == sheet->sheet_window)
3378 gtk_widget_get_pointer (widget, &x, &y);
3379 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
3380 gdk_pointer_grab (sheet->sheet_window, FALSE,
3381 GDK_POINTER_MOTION_HINT_MASK |
3382 GDK_BUTTON1_MOTION_MASK |
3383 GDK_BUTTON_RELEASE_MASK,
3384 NULL, NULL, event->time);
3385 gtk_grab_add (GTK_WIDGET (sheet));
3387 if (sheet->selection_mode != GTK_SELECTION_SINGLE &&
3388 sheet->selection_mode != GTK_SELECTION_NONE &&
3389 sheet->cursor_drag->type == GDK_SIZING &&
3390 !PSPPIRE_SHEET_IN_SELECTION (sheet) && !PSPPIRE_SHEET_IN_RESIZE (sheet))
3392 if (sheet->state == GTK_STATE_NORMAL)
3394 row = sheet->active_cell.row;
3395 column = sheet->active_cell.col;
3396 sheet->active_cell.row = row;
3397 sheet->active_cell.col = column;
3398 sheet->drag_range = sheet->range;
3399 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
3400 psppire_sheet_select_range (sheet, &sheet->drag_range);
3404 if (row > sheet->range.rowi) row--;
3405 if (column > sheet->range.coli) column--;
3406 sheet->drag_cell.row = row;
3407 sheet->drag_cell.col = column;
3408 sheet->drag_range = sheet->range;
3409 draw_xor_rectangle (sheet, sheet->drag_range);
3410 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_RESIZE);
3412 else if (sheet->cursor_drag->type == GDK_TOP_LEFT_ARROW &&
3413 !PSPPIRE_SHEET_IN_SELECTION (sheet)
3414 && ! PSPPIRE_SHEET_IN_DRAG (sheet)
3415 && sheet->active_cell.row >= 0
3416 && sheet->active_cell.col >= 0
3419 if (sheet->state == GTK_STATE_NORMAL)
3421 row = sheet->active_cell.row;
3422 column = sheet->active_cell.col;
3423 sheet->active_cell.row = row;
3424 sheet->active_cell.col = column;
3425 sheet->drag_range = sheet->range;
3426 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
3427 psppire_sheet_select_range (sheet, &sheet->drag_range);
3431 if (row < sheet->range.row0) row++;
3432 if (row > sheet->range.rowi) row--;
3433 if (column < sheet->range.col0) column++;
3434 if (column > sheet->range.coli) column--;
3435 sheet->drag_cell.row = row;
3436 sheet->drag_cell.col = column;
3437 sheet->drag_range = sheet->range;
3438 draw_xor_rectangle (sheet, sheet->drag_range);
3439 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_DRAG);
3443 veto = psppire_sheet_click_cell (sheet, row, column);
3444 if (veto) PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3448 if (event->window == sheet->column_title_window)
3450 gtk_widget_get_pointer (widget, &x, &y);
3451 if ( sheet->row_titles_visible)
3452 x -= sheet->row_title_area.width;
3454 x += sheet->hadjustment->value;
3456 column = column_from_xpixel (sheet, x);
3458 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
3460 veto = psppire_sheet_click_cell (sheet, -1, column);
3461 gtk_grab_add (GTK_WIDGET (sheet));
3462 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3466 if (event->window == sheet->row_title_window)
3468 gtk_widget_get_pointer (widget, &x, &y);
3469 if ( sheet->column_titles_visible)
3470 y -= sheet->column_title_area.height;
3472 y += sheet->vadjustment->value;
3474 row = row_from_ypixel (sheet, y);
3475 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
3477 veto = psppire_sheet_click_cell (sheet, row, -1);
3478 gtk_grab_add (GTK_WIDGET (sheet));
3479 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3487 psppire_sheet_click_cell (PsppireSheet *sheet, gint row, gint column)
3489 PsppireSheetCell cell;
3490 gboolean forbid_move;
3495 if (row >= psppire_axis_unit_count (sheet->vaxis)
3496 || column >= psppire_axis_unit_count (sheet->haxis))
3501 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
3502 &sheet->active_cell,
3508 if (sheet->state == GTK_STATE_NORMAL)
3511 row = sheet->active_cell.row;
3512 column = sheet->active_cell.col;
3514 change_active_cell (sheet, row, column);
3518 if (row == -1 && column >= 0)
3520 psppire_sheet_select_column (sheet, column);
3524 if (column == -1 && row >= 0)
3526 psppire_sheet_select_row (sheet, row);
3530 if (row == -1 && column == -1)
3532 sheet->range.row0 = 0;
3533 sheet->range.col0 = 0;
3534 sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
3536 psppire_axis_unit_count (sheet->haxis) - 1;
3537 sheet->active_cell.row = 0;
3538 sheet->active_cell.col = 0;
3539 psppire_sheet_select_range (sheet, NULL);
3543 if (sheet->state != PSPPIRE_SHEET_NORMAL)
3545 sheet->state = PSPPIRE_SHEET_NORMAL;
3546 psppire_sheet_real_unselect_range (sheet, NULL);
3550 change_active_cell (sheet, row, column);
3553 sheet->active_cell.row = row;
3554 sheet->active_cell.col = column;
3555 sheet->selection_cell.row = row;
3556 sheet->selection_cell.col = column;
3557 sheet->range.row0 = row;
3558 sheet->range.col0 = column;
3559 sheet->range.rowi = row;
3560 sheet->range.coli = column;
3561 sheet->state = PSPPIRE_SHEET_NORMAL;
3562 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3564 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
3570 psppire_sheet_button_release (GtkWidget *widget,
3571 GdkEventButton *event)
3573 GdkDisplay *display = gtk_widget_get_display (widget);
3575 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3577 /* release on resize windows */
3578 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3581 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
3582 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3584 gdk_display_pointer_ungrab (display, event->time);
3585 draw_xor_vline (sheet);
3588 psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)
3589 + sheet->hadjustment->value;
3591 set_column_width (sheet, sheet->drag_cell.col, width);
3596 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3599 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
3600 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3602 gdk_display_pointer_ungrab (display, event->time);
3603 draw_xor_hline (sheet);
3606 psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row) +
3607 sheet->vadjustment->value;
3609 set_row_height (sheet, sheet->drag_cell.row, height);
3614 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3616 PsppireSheetRange old_range;
3617 draw_xor_rectangle (sheet, sheet->drag_range);
3618 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_DRAG);
3619 gdk_display_pointer_ungrab (display, event->time);
3621 psppire_sheet_real_unselect_range (sheet, NULL);
3623 sheet->active_cell.row = sheet->active_cell.row +
3624 (sheet->drag_range.row0 - sheet->range.row0);
3625 sheet->active_cell.col = sheet->active_cell.col +
3626 (sheet->drag_range.col0 - sheet->range.col0);
3627 sheet->selection_cell.row = sheet->selection_cell.row +
3628 (sheet->drag_range.row0 - sheet->range.row0);
3629 sheet->selection_cell.col = sheet->selection_cell.col +
3630 (sheet->drag_range.col0 - sheet->range.col0);
3631 old_range = sheet->range;
3632 sheet->range = sheet->drag_range;
3633 sheet->drag_range = old_range;
3634 g_signal_emit (sheet, sheet_signals[MOVE_RANGE], 0,
3635 &sheet->drag_range, &sheet->range);
3636 psppire_sheet_select_range (sheet, &sheet->range);
3639 if (PSPPIRE_SHEET_IN_RESIZE (sheet))
3641 PsppireSheetRange old_range;
3642 draw_xor_rectangle (sheet, sheet->drag_range);
3643 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_RESIZE);
3644 gdk_display_pointer_ungrab (display, event->time);
3646 psppire_sheet_real_unselect_range (sheet, NULL);
3648 sheet->active_cell.row = sheet->active_cell.row +
3649 (sheet->drag_range.row0 - sheet->range.row0);
3650 sheet->active_cell.col = sheet->active_cell.col +
3651 (sheet->drag_range.col0 - sheet->range.col0);
3652 if (sheet->drag_range.row0 < sheet->range.row0)
3653 sheet->selection_cell.row = sheet->drag_range.row0;
3654 if (sheet->drag_range.rowi >= sheet->range.rowi)
3655 sheet->selection_cell.row = sheet->drag_range.rowi;
3656 if (sheet->drag_range.col0 < sheet->range.col0)
3657 sheet->selection_cell.col = sheet->drag_range.col0;
3658 if (sheet->drag_range.coli >= sheet->range.coli)
3659 sheet->selection_cell.col = sheet->drag_range.coli;
3660 old_range = sheet->range;
3661 sheet->range = sheet->drag_range;
3662 sheet->drag_range = old_range;
3664 if (sheet->state == GTK_STATE_NORMAL) sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
3665 g_signal_emit (sheet, sheet_signals[RESIZE_RANGE], 0,
3666 &sheet->drag_range, &sheet->range);
3667 psppire_sheet_select_range (sheet, &sheet->range);
3670 if (sheet->state == PSPPIRE_SHEET_NORMAL && PSPPIRE_SHEET_IN_SELECTION (sheet))
3672 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3673 gdk_display_pointer_ungrab (display, event->time);
3674 change_active_cell (sheet, sheet->active_cell.row,
3675 sheet->active_cell.col);
3678 if (PSPPIRE_SHEET_IN_SELECTION)
3679 gdk_display_pointer_ungrab (display, event->time);
3680 gtk_grab_remove (GTK_WIDGET (sheet));
3682 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3691 /* Shamelessly lifted from gtktooltips */
3693 psppire_sheet_subtitle_paint_window (GtkWidget *tip_window)
3697 gtk_widget_size_request (tip_window, &req);
3698 gtk_paint_flat_box (tip_window->style, tip_window->window,
3699 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
3700 NULL, GTK_WIDGET(tip_window), "tooltip",
3701 0, 0, req.width, req.height);
3707 destroy_hover_window (PsppireSheetHoverTitle *h)
3709 gtk_widget_destroy (h->window);
3713 static PsppireSheetHoverTitle *
3714 create_hover_window (void)
3716 PsppireSheetHoverTitle *hw = g_malloc (sizeof (*hw));
3718 hw->window = gtk_window_new (GTK_WINDOW_POPUP);
3720 #if GTK_CHECK_VERSION (2, 9, 0)
3721 gtk_window_set_type_hint (GTK_WINDOW (hw->window),
3722 GDK_WINDOW_TYPE_HINT_TOOLTIP);
3725 gtk_widget_set_app_paintable (hw->window, TRUE);
3726 gtk_window_set_resizable (GTK_WINDOW (hw->window), FALSE);
3727 gtk_widget_set_name (hw->window, "gtk-tooltips");
3728 gtk_container_set_border_width (GTK_CONTAINER (hw->window), 4);
3730 g_signal_connect (hw->window,
3732 G_CALLBACK (psppire_sheet_subtitle_paint_window),
3735 hw->label = gtk_label_new (NULL);
3738 gtk_label_set_line_wrap (GTK_LABEL (hw->label), TRUE);
3739 gtk_misc_set_alignment (GTK_MISC (hw->label), 0.5, 0.5);
3741 gtk_container_add (GTK_CONTAINER (hw->window), hw->label);
3743 gtk_widget_show (hw->label);
3745 g_signal_connect (hw->window,
3747 G_CALLBACK (gtk_widget_destroyed),
3753 #define HOVER_WINDOW_Y_OFFSET 2
3756 show_subtitle (PsppireSheet *sheet, gint row, gint column,
3757 const gchar *subtitle)
3766 gtk_label_set_text (GTK_LABEL (sheet->hover_window->label),
3770 sheet->hover_window->row = row;
3771 sheet->hover_window->column = column;
3773 gdk_window_get_origin (GTK_WIDGET (sheet)->window, &x, &y);
3775 gtk_widget_get_pointer (GTK_WIDGET (sheet), &px, &py);
3777 gtk_widget_show (sheet->hover_window->window);
3779 width = GTK_WIDGET (sheet->hover_window->label)->allocation.width;
3785 y += sheet->column_title_area.y;
3786 y += sheet->column_title_area.height;
3787 y += HOVER_WINDOW_Y_OFFSET;
3793 x += sheet->row_title_area.x;
3794 x += sheet->row_title_area.width * 2 / 3.0;
3797 gtk_window_move (GTK_WINDOW (sheet->hover_window->window),
3802 motion_timeout_callback (gpointer data)
3804 PsppireSheet *sheet = PSPPIRE_SHEET (data);
3808 gdk_threads_enter ();
3809 gtk_widget_get_pointer (GTK_WIDGET (sheet), &x, &y);
3811 if ( psppire_sheet_get_pixel_info (sheet, x, y, &row, &column) )
3813 if (sheet->row_title_under && row >= 0)
3815 gchar *text = psppire_sheet_model_get_row_subtitle (sheet->model, row);
3817 show_subtitle (sheet, row, -1, text);
3821 if (sheet->column_title_under && column >= 0)
3823 gchar *text = psppire_sheet_model_get_column_subtitle (sheet->model,
3826 show_subtitle (sheet, -1, column, text);
3832 gdk_threads_leave ();
3837 psppire_sheet_motion (GtkWidget *widget, GdkEventMotion *event)
3839 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3840 GdkModifierType mods;
3841 GdkCursorType new_cursor;
3844 GdkDisplay *display;
3846 g_return_val_if_fail (event != NULL, FALSE);
3848 display = gtk_widget_get_display (widget);
3850 /* selections on the sheet */
3854 if (!GTK_WIDGET_VISIBLE (sheet->hover_window->window))
3856 if ( sheet->motion_timer > 0 )
3857 g_source_remove (sheet->motion_timer);
3858 sheet->motion_timer =
3859 g_timeout_add (TIMEOUT_HOVER, motion_timeout_callback, sheet);
3865 gtk_widget_get_pointer (widget, &wx, &wy);
3867 if ( psppire_sheet_get_pixel_info (sheet, wx, wy, &row, &column) )
3869 if ( row != sheet->hover_window->row ||
3870 column != sheet->hover_window->column)
3872 gtk_widget_hide (sheet->hover_window->window);
3877 if (event->window == sheet->column_title_window)
3879 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3880 on_column_boundary (sheet, x, &column))
3882 new_cursor = GDK_SB_H_DOUBLE_ARROW;
3883 if (new_cursor != sheet->cursor_drag->type)
3885 gdk_cursor_unref (sheet->cursor_drag);
3886 sheet->cursor_drag =
3887 gdk_cursor_new_for_display (display, new_cursor);
3889 gdk_window_set_cursor (sheet->column_title_window,
3890 sheet->cursor_drag);
3895 new_cursor = GDK_TOP_LEFT_ARROW;
3896 if (!PSPPIRE_SHEET_IN_XDRAG (sheet) &&
3897 new_cursor != sheet->cursor_drag->type)
3899 gdk_cursor_unref (sheet->cursor_drag);
3900 sheet->cursor_drag =
3901 gdk_cursor_new_for_display (display, new_cursor);
3902 gdk_window_set_cursor (sheet->column_title_window,
3903 sheet->cursor_drag);
3907 else if (event->window == sheet->row_title_window)
3909 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3910 on_row_boundary (sheet, y, &row))
3912 new_cursor = GDK_SB_V_DOUBLE_ARROW;
3913 if (new_cursor != sheet->cursor_drag->type)
3915 gdk_cursor_unref (sheet->cursor_drag);
3916 sheet->cursor_drag =
3917 gdk_cursor_new_for_display (display, new_cursor);
3918 gdk_window_set_cursor (sheet->row_title_window,
3919 sheet->cursor_drag);
3924 new_cursor = GDK_TOP_LEFT_ARROW;
3925 if (!PSPPIRE_SHEET_IN_YDRAG (sheet) &&
3926 new_cursor != sheet->cursor_drag->type)
3928 gdk_cursor_unref (sheet->cursor_drag);
3929 sheet->cursor_drag =
3930 gdk_cursor_new_for_display (display, new_cursor);
3931 gdk_window_set_cursor (sheet->row_title_window,
3932 sheet->cursor_drag);
3937 new_cursor = GDK_PLUS;
3938 if ( event->window == sheet->sheet_window &&
3939 !POSSIBLE_DRAG (sheet, x, y, &row, &column) &&
3940 !PSPPIRE_SHEET_IN_DRAG (sheet) &&
3941 !POSSIBLE_RESIZE (sheet, x, y, &row, &column) &&
3942 !PSPPIRE_SHEET_IN_RESIZE (sheet) &&
3943 new_cursor != sheet->cursor_drag->type)
3945 gdk_cursor_unref (sheet->cursor_drag);
3946 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
3947 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3950 new_cursor = GDK_TOP_LEFT_ARROW;
3951 if ( event->window == sheet->sheet_window &&
3952 ! (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
3953 PSPPIRE_SHEET_IN_RESIZE (sheet)) &&
3954 (POSSIBLE_DRAG (sheet, x, y, &row, &column) ||
3955 PSPPIRE_SHEET_IN_DRAG (sheet)) &&
3956 new_cursor != sheet->cursor_drag->type)
3958 gdk_cursor_unref (sheet->cursor_drag);
3959 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
3960 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3963 new_cursor = GDK_SIZING;
3964 if ( event->window == sheet->sheet_window &&
3965 sheet->selection_mode != GTK_SELECTION_NONE &&
3966 !PSPPIRE_SHEET_IN_DRAG (sheet) &&
3967 (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
3968 PSPPIRE_SHEET_IN_RESIZE (sheet)) &&
3969 new_cursor != sheet->cursor_drag->type)
3971 gdk_cursor_unref (sheet->cursor_drag);
3972 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_SIZING);
3973 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3977 gdk_window_get_pointer (widget->window, &x, &y, &mods);
3978 if (! (mods & GDK_BUTTON1_MASK)) return FALSE;
3980 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3982 if (event->x != sheet->x_drag)
3984 draw_xor_vline (sheet);
3985 sheet->x_drag = event->x;
3986 draw_xor_vline (sheet);
3992 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3994 if (event->y != sheet->y_drag)
3996 draw_xor_hline (sheet);
3997 sheet->y_drag = event->y;
3998 draw_xor_hline (sheet);
4004 if (PSPPIRE_SHEET_IN_DRAG (sheet))
4006 PsppireSheetRange aux;
4007 column = column_from_xpixel (sheet, x)- sheet->drag_cell.col;
4008 row = row_from_ypixel (sheet, y) - sheet->drag_cell.row;
4009 if (sheet->state == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
4010 if (sheet->state == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
4014 if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
4015 aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
4017 aux = sheet->drag_range;
4018 sheet->drag_range.row0 = sheet->range.row0 + row;
4019 sheet->drag_range.col0 = sheet->range.col0 + column;
4020 sheet->drag_range.rowi = sheet->range.rowi + row;
4021 sheet->drag_range.coli = sheet->range.coli + column;
4022 if (aux.row0 != sheet->drag_range.row0 ||
4023 aux.col0 != sheet->drag_range.col0)
4025 draw_xor_rectangle (sheet, aux);
4026 draw_xor_rectangle (sheet, sheet->drag_range);
4032 if (PSPPIRE_SHEET_IN_RESIZE (sheet))
4034 PsppireSheetRange aux;
4035 gint v_h, current_col, current_row, col_threshold, row_threshold;
4037 if (abs (x - psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)) >
4038 abs (y - psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row))) v_h = 2;
4040 current_col = column_from_xpixel (sheet, x);
4041 current_row = row_from_ypixel (sheet, y);
4042 column = current_col - sheet->drag_cell.col;
4043 row = current_row - sheet->drag_cell.row;
4045 /*use half of column width resp. row height as threshold to
4047 col_threshold = psppire_axis_start_pixel (sheet->haxis, current_col) +
4048 psppire_axis_unit_size (sheet->haxis, current_col) / 2;
4051 if (x < col_threshold)
4054 else if (column < 0)
4056 if (x > col_threshold)
4059 row_threshold = psppire_axis_start_pixel (sheet->vaxis, current_row) +
4060 psppire_axis_unit_size (sheet->vaxis, current_row)/2;
4063 if (y < row_threshold)
4068 if (y > row_threshold)
4072 if (sheet->state == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
4073 if (sheet->state == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
4083 if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
4084 aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
4086 aux = sheet->drag_range;
4087 sheet->drag_range = sheet->range;
4089 if (row < 0) sheet->drag_range.row0 = sheet->range.row0 + row;
4090 if (row > 0) sheet->drag_range.rowi = sheet->range.rowi + row;
4091 if (column < 0) sheet->drag_range.col0 = sheet->range.col0 + column;
4092 if (column > 0) sheet->drag_range.coli = sheet->range.coli + column;
4094 if (aux.row0 != sheet->drag_range.row0 ||
4095 aux.rowi != sheet->drag_range.rowi ||
4096 aux.col0 != sheet->drag_range.col0 ||
4097 aux.coli != sheet->drag_range.coli)
4099 draw_xor_rectangle (sheet, aux);
4100 draw_xor_rectangle (sheet, sheet->drag_range);
4106 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
4108 if (sheet->state == PSPPIRE_SHEET_NORMAL && row == sheet->active_cell.row &&
4109 column == sheet->active_cell.col) return TRUE;
4111 if (PSPPIRE_SHEET_IN_SELECTION (sheet) && mods&GDK_BUTTON1_MASK)
4112 psppire_sheet_extend_selection (sheet, row, column);
4118 psppire_sheet_crossing_notify (GtkWidget *widget,
4119 GdkEventCrossing *event)
4121 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
4123 if (event->window == sheet->column_title_window)
4124 sheet->column_title_under = event->type == GDK_ENTER_NOTIFY;
4125 else if (event->window == sheet->row_title_window)
4126 sheet->row_title_under = event->type == GDK_ENTER_NOTIFY;
4128 if (event->type == GDK_LEAVE_NOTIFY)
4129 gtk_widget_hide (sheet->hover_window->window);
4136 psppire_sheet_focus_in (GtkWidget *w,
4137 GdkEventFocus *event)
4139 PsppireSheet *sheet = PSPPIRE_SHEET (w);
4141 gtk_widget_grab_focus (sheet->entry_widget);
4148 psppire_sheet_extend_selection (PsppireSheet *sheet, gint row, gint column)
4150 PsppireSheetRange range;
4154 if (row == sheet->selection_cell.row && column == sheet->selection_cell.col)
4157 if (sheet->selection_mode == GTK_SELECTION_SINGLE) return;
4159 gtk_widget_grab_focus (GTK_WIDGET (sheet));
4161 if (PSPPIRE_SHEET_IN_DRAG (sheet)) return;
4163 state = sheet->state;
4165 switch (sheet->state)
4167 case PSPPIRE_SHEET_ROW_SELECTED:
4168 column = psppire_axis_unit_count (sheet->haxis) - 1;
4170 case PSPPIRE_SHEET_COLUMN_SELECTED:
4171 row = psppire_axis_unit_count (sheet->vaxis) - 1;
4173 case PSPPIRE_SHEET_NORMAL:
4174 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
4175 r = sheet->active_cell.row;
4176 c = sheet->active_cell.col;
4177 sheet->range.col0 = c;
4178 sheet->range.row0 = r;
4179 sheet->range.coli = c;
4180 sheet->range.rowi = r;
4181 psppire_sheet_range_draw_selection (sheet, sheet->range);
4182 case PSPPIRE_SHEET_RANGE_SELECTED:
4183 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
4186 sheet->selection_cell.row = row;
4187 sheet->selection_cell.col = column;
4189 range.col0 = MIN (column, sheet->active_cell.col);
4190 range.coli = MAX (column, sheet->active_cell.col);
4191 range.row0 = MIN (row, sheet->active_cell.row);
4192 range.rowi = MAX (row, sheet->active_cell.row);
4194 if (range.row0 != sheet->range.row0 || range.rowi != sheet->range.rowi ||
4195 range.col0 != sheet->range.col0 || range.coli != sheet->range.coli ||
4196 state == PSPPIRE_SHEET_NORMAL)
4197 psppire_sheet_real_select_range (sheet, &range);
4202 psppire_sheet_entry_key_press (GtkWidget *widget,
4206 g_signal_emit_by_name (widget, "key_press_event", key, &focus);
4211 /* Number of rows in a step-increment */
4212 #define ROWS_PER_STEP 1
4216 page_vertical (PsppireSheet *sheet, GtkScrollType dir)
4218 gint old_row = sheet->active_cell.row ;
4219 glong vpixel = psppire_axis_start_pixel (sheet->vaxis, old_row);
4223 vpixel -= psppire_axis_start_pixel (sheet->vaxis,
4224 min_visible_row (sheet));
4228 case GTK_SCROLL_PAGE_DOWN:
4229 gtk_adjustment_set_value (sheet->vadjustment,
4230 sheet->vadjustment->value +
4231 sheet->vadjustment->page_increment);
4233 case GTK_SCROLL_PAGE_UP:
4234 gtk_adjustment_set_value (sheet->vadjustment,
4235 sheet->vadjustment->value -
4236 sheet->vadjustment->page_increment);
4240 g_assert_not_reached ();
4245 vpixel += psppire_axis_start_pixel (sheet->vaxis,
4246 min_visible_row (sheet));
4248 new_row = row_from_ypixel (sheet, vpixel);
4250 change_active_cell (sheet, new_row,
4251 sheet->active_cell.col);
4256 step_sheet (PsppireSheet *sheet, GtkScrollType dir)
4258 gint current_row = sheet->active_cell.row;
4259 gint current_col = sheet->active_cell.col;
4260 PsppireSheetCell new_cell ;
4261 gboolean forbidden = FALSE;
4263 new_cell.row = current_row;
4264 new_cell.col = current_col;
4268 case GTK_SCROLL_STEP_DOWN:
4271 case GTK_SCROLL_STEP_UP:
4274 case GTK_SCROLL_STEP_RIGHT:
4277 case GTK_SCROLL_STEP_LEFT:
4280 case GTK_SCROLL_STEP_FORWARD:
4283 psppire_sheet_model_get_column_count (sheet->model))
4289 case GTK_SCROLL_STEP_BACKWARD:
4291 if (new_cell.col < 0)
4294 psppire_sheet_model_get_column_count (sheet->model) - 1;
4299 g_assert_not_reached ();
4303 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
4304 &sheet->active_cell,
4312 maximize_int (&new_cell.row, 0);
4313 maximize_int (&new_cell.col, 0);
4315 minimize_int (&new_cell.row,
4316 psppire_axis_unit_count (sheet->vaxis) - 1);
4318 minimize_int (&new_cell.col,
4319 psppire_axis_unit_count (sheet->haxis) - 1);
4321 change_active_cell (sheet, new_cell.row, new_cell.col);
4324 if ( new_cell.col > max_fully_visible_column (sheet))
4327 psppire_axis_start_pixel (sheet->haxis,
4329 hpos -= sheet->hadjustment->page_size;
4331 gtk_adjustment_set_value (sheet->hadjustment,
4334 else if ( new_cell.col < min_fully_visible_column (sheet))
4337 psppire_axis_start_pixel (sheet->haxis,
4340 gtk_adjustment_set_value (sheet->hadjustment,
4345 if ( new_cell.row > max_fully_visible_row (sheet))
4348 psppire_axis_start_pixel (sheet->vaxis,
4350 vpos -= sheet->vadjustment->page_size;
4352 gtk_adjustment_set_value (sheet->vadjustment,
4355 else if ( new_cell.row < min_fully_visible_row (sheet))
4358 psppire_axis_start_pixel (sheet->vaxis,
4361 gtk_adjustment_set_value (sheet->vadjustment,
4365 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
4370 psppire_sheet_key_press (GtkWidget *widget,
4373 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
4375 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
4377 switch (key->keyval)
4380 step_sheet (sheet, GTK_SCROLL_STEP_FORWARD);
4383 step_sheet (sheet, GTK_SCROLL_STEP_RIGHT);
4385 case GDK_ISO_Left_Tab:
4386 step_sheet (sheet, GTK_SCROLL_STEP_BACKWARD);
4389 step_sheet (sheet, GTK_SCROLL_STEP_LEFT);
4393 step_sheet (sheet, GTK_SCROLL_STEP_DOWN);
4396 step_sheet (sheet, GTK_SCROLL_STEP_UP);
4400 page_vertical (sheet, GTK_SCROLL_PAGE_DOWN);
4403 page_vertical (sheet, GTK_SCROLL_PAGE_UP);
4407 gtk_adjustment_set_value (sheet->vadjustment,
4408 sheet->vadjustment->lower);
4410 change_active_cell (sheet, 0,
4411 sheet->active_cell.col);
4416 gtk_adjustment_set_value (sheet->vadjustment,
4417 sheet->vadjustment->upper -
4418 sheet->vadjustment->page_size -
4419 sheet->vadjustment->page_increment);
4422 change_active_cellx (sheet,
4423 psppire_axis_unit_count (sheet->vaxis) - 1,
4424 sheet->active_cell.col);
4428 psppire_sheet_real_cell_clear (sheet, sheet->active_cell.row, sheet->active_cell.col);
4439 psppire_sheet_size_request (GtkWidget *widget,
4440 GtkRequisition *requisition)
4442 PsppireSheet *sheet;
4444 g_return_if_fail (widget != NULL);
4445 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
4446 g_return_if_fail (requisition != NULL);
4448 sheet = PSPPIRE_SHEET (widget);
4450 requisition->width = 3 * DEFAULT_COLUMN_WIDTH;
4451 requisition->height = 3 * DEFAULT_ROW_HEIGHT;
4453 /* compute the size of the column title area */
4454 if (sheet->column_titles_visible)
4455 requisition->height += sheet->column_title_area.height;
4457 /* compute the size of the row title area */
4458 if (sheet->row_titles_visible)
4459 requisition->width += sheet->row_title_area.width;
4464 psppire_sheet_size_allocate (GtkWidget *widget,
4465 GtkAllocation *allocation)
4467 PsppireSheet *sheet;
4468 GtkAllocation sheet_allocation;
4471 g_return_if_fail (widget != NULL);
4472 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
4473 g_return_if_fail (allocation != NULL);
4475 sheet = PSPPIRE_SHEET (widget);
4476 widget->allocation = *allocation;
4477 border_width = GTK_CONTAINER (widget)->border_width;
4479 if (GTK_WIDGET_REALIZED (widget))
4480 gdk_window_move_resize (widget->window,
4481 allocation->x + border_width,
4482 allocation->y + border_width,
4483 allocation->width - 2 * border_width,
4484 allocation->height - 2 * border_width);
4486 sheet_allocation.x = 0;
4487 sheet_allocation.y = 0;
4488 sheet_allocation.width = allocation->width - 2 * border_width;
4489 sheet_allocation.height = allocation->height - 2 * border_width;
4491 if (GTK_WIDGET_REALIZED (widget))
4492 gdk_window_move_resize (sheet->sheet_window,
4495 sheet_allocation.width,
4496 sheet_allocation.height);
4498 /* position the window which holds the column title buttons */
4499 sheet->column_title_area.x = 0;
4500 sheet->column_title_area.y = 0;
4501 sheet->column_title_area.width = sheet_allocation.width ;
4504 /* position the window which holds the row title buttons */
4505 sheet->row_title_area.x = 0;
4506 sheet->row_title_area.y = 0;
4507 sheet->row_title_area.height = sheet_allocation.height;
4509 if (sheet->row_titles_visible)
4510 sheet->column_title_area.x += sheet->row_title_area.width;
4512 if (sheet->column_titles_visible)
4513 sheet->row_title_area.y += sheet->column_title_area.height;
4515 if (GTK_WIDGET_REALIZED (widget) && sheet->column_titles_visible)
4516 gdk_window_move_resize (sheet->column_title_window,
4517 sheet->column_title_area.x,
4518 sheet->column_title_area.y,
4519 sheet->column_title_area.width,
4520 sheet->column_title_area.height);
4523 if (GTK_WIDGET_REALIZED (widget) && sheet->row_titles_visible)
4524 gdk_window_move_resize (sheet->row_title_window,
4525 sheet->row_title_area.x,
4526 sheet->row_title_area.y,
4527 sheet->row_title_area.width,
4528 sheet->row_title_area.height);
4530 size_allocate_global_button (sheet);
4534 gint width = sheet->column_title_area.width;
4536 if ( sheet->row_titles_visible)
4537 width -= sheet->row_title_area.width;
4539 g_object_set (sheet->haxis,
4540 "minimum-extent", width,
4547 gint height = sheet->row_title_area.height;
4549 if ( sheet->column_titles_visible)
4550 height -= sheet->column_title_area.height;
4552 g_object_set (sheet->vaxis,
4553 "minimum-extent", height,
4558 /* set the scrollbars adjustments */
4559 adjust_scrollbars (sheet);
4563 draw_column_title_buttons (PsppireSheet *sheet)
4567 if (!sheet->column_titles_visible) return;
4568 if (!GTK_WIDGET_REALIZED (sheet))
4571 gdk_drawable_get_size (sheet->sheet_window, &width, NULL);
4574 if (sheet->row_titles_visible)
4576 x = sheet->row_title_area.width;
4579 if (sheet->column_title_area.width != width || sheet->column_title_area.x != x)
4581 sheet->column_title_area.width = width;
4582 sheet->column_title_area.x = x;
4583 gdk_window_move_resize (sheet->column_title_window,
4584 sheet->column_title_area.x,
4585 sheet->column_title_area.y,
4586 sheet->column_title_area.width,
4587 sheet->column_title_area.height);
4590 if (max_visible_column (sheet) ==
4591 psppire_axis_unit_count (sheet->haxis) - 1)
4592 gdk_window_clear_area (sheet->column_title_window,
4594 sheet->column_title_area.width,
4595 sheet->column_title_area.height);
4597 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
4599 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
4600 max_visible_column (sheet));
4604 draw_row_title_buttons (PsppireSheet *sheet)
4609 if (!sheet->row_titles_visible) return;
4610 if (!GTK_WIDGET_REALIZED (sheet))
4613 gdk_drawable_get_size (sheet->sheet_window, NULL, &height);
4615 if (sheet->column_titles_visible)
4617 y = sheet->column_title_area.height;
4620 if (sheet->row_title_area.height != height || sheet->row_title_area.y != y)
4622 sheet->row_title_area.y = y;
4623 sheet->row_title_area.height = height;
4624 gdk_window_move_resize (sheet->row_title_window,
4625 sheet->row_title_area.x,
4626 sheet->row_title_area.y,
4627 sheet->row_title_area.width,
4628 sheet->row_title_area.height);
4631 if (max_visible_row (sheet) == psppire_axis_unit_count (sheet->vaxis) - 1)
4632 gdk_window_clear_area (sheet->row_title_window,
4634 sheet->row_title_area.width,
4635 sheet->row_title_area.height);
4637 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
4639 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
4640 max_visible_row (sheet));
4645 psppire_sheet_size_allocate_entry (PsppireSheet *sheet)
4647 GtkAllocation entry_alloc;
4648 PsppireSheetCellAttr attributes = { 0 };
4649 GtkEntry *sheet_entry;
4651 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4652 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
4654 sheet_entry = psppire_sheet_get_entry (sheet);
4656 if ( ! psppire_sheet_get_attributes (sheet, sheet->active_cell.row,
4657 sheet->active_cell.col,
4661 if ( GTK_WIDGET_REALIZED (sheet->entry_widget) )
4663 GtkStyle *style = GTK_WIDGET (sheet_entry)->style;
4665 style->bg[GTK_STATE_NORMAL] = attributes.background;
4666 style->fg[GTK_STATE_NORMAL] = attributes.foreground;
4667 style->text[GTK_STATE_NORMAL] = attributes.foreground;
4668 style->bg[GTK_STATE_ACTIVE] = attributes.background;
4669 style->fg[GTK_STATE_ACTIVE] = attributes.foreground;
4670 style->text[GTK_STATE_ACTIVE] = attributes.foreground;
4673 rectangle_from_cell (sheet, sheet->active_cell.row,
4674 sheet->active_cell.col, &entry_alloc);
4676 entry_alloc.width -= BORDER_WIDTH ;
4677 entry_alloc.height -= BORDER_WIDTH ;
4678 entry_alloc.x += DIV_RND_UP (BORDER_WIDTH, 2);
4679 entry_alloc.y += DIV_RND_UP (BORDER_WIDTH, 2);
4682 gtk_widget_set_size_request (sheet->entry_widget, entry_alloc.width,
4683 entry_alloc.height);
4684 gtk_widget_size_allocate (sheet->entry_widget, &entry_alloc);
4688 /* Copy the sheet's font to the entry widget */
4690 set_entry_widget_font (PsppireSheet *sheet)
4692 GtkRcStyle *style = gtk_widget_get_modifier_style (sheet->entry_widget);
4694 pango_font_description_free (style->font_desc);
4695 style->font_desc = pango_font_description_copy (GTK_WIDGET (sheet)->style->font_desc);
4697 gtk_widget_modify_style (sheet->entry_widget, style);
4701 create_sheet_entry (PsppireSheet *sheet)
4703 if (sheet->entry_widget)
4705 gtk_widget_unparent (sheet->entry_widget);
4708 sheet->entry_widget = g_object_new (sheet->entry_type, NULL);
4709 g_object_ref_sink (sheet->entry_widget);
4711 gtk_widget_size_request (sheet->entry_widget, NULL);
4713 if ( GTK_IS_ENTRY (sheet->entry_widget))
4715 g_object_set (sheet->entry_widget,
4720 if (GTK_WIDGET_REALIZED (sheet))
4722 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
4723 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
4724 gtk_widget_realize (sheet->entry_widget);
4727 g_signal_connect_swapped (sheet->entry_widget, "key_press_event",
4728 G_CALLBACK (psppire_sheet_entry_key_press),
4731 set_entry_widget_font (sheet);
4733 gtk_widget_show (sheet->entry_widget);
4737 /* Finds the last child widget that happens to be of type GtkEntry */
4739 find_entry (GtkWidget *w, gpointer user_data)
4741 GtkWidget **entry = user_data;
4742 if ( GTK_IS_ENTRY (w))
4750 psppire_sheet_get_entry (PsppireSheet *sheet)
4752 GtkWidget *w = sheet->entry_widget;
4754 g_return_val_if_fail (sheet != NULL, NULL);
4755 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
4756 g_return_val_if_fail (sheet->entry_widget != NULL, NULL);
4758 while (! GTK_IS_ENTRY (w))
4760 GtkWidget *entry = NULL;
4762 if (GTK_IS_CONTAINER (w))
4764 gtk_container_forall (GTK_CONTAINER (w), find_entry, &entry);
4773 return GTK_ENTRY (w);
4778 draw_button (PsppireSheet *sheet, GdkWindow *window,
4779 PsppireSheetButton *button, gboolean is_sensitive,
4780 GdkRectangle allocation)
4782 GtkShadowType shadow_type;
4783 gint text_width = 0, text_height = 0;
4784 PangoAlignment align = PANGO_ALIGN_LEFT;
4790 g_return_if_fail (sheet != NULL);
4791 g_return_if_fail (button != NULL);
4794 rtl = gtk_widget_get_direction (GTK_WIDGET (sheet)) == GTK_TEXT_DIR_RTL;
4796 gdk_window_clear_area (window,
4797 allocation.x, allocation.y,
4798 allocation.width, allocation.height);
4800 gtk_widget_ensure_style (sheet->button);
4802 gtk_paint_box (sheet->button->style, window,
4803 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
4805 GTK_WIDGET (sheet->button),
4807 allocation.x, allocation.y,
4808 allocation.width, allocation.height);
4810 state = button->state;
4811 if (!is_sensitive) state = GTK_STATE_INSENSITIVE;
4813 if (state == GTK_STATE_ACTIVE)
4814 shadow_type = GTK_SHADOW_IN;
4816 shadow_type = GTK_SHADOW_OUT;
4818 if (state != GTK_STATE_NORMAL && state != GTK_STATE_INSENSITIVE)
4819 gtk_paint_box (sheet->button->style, window,
4820 button->state, shadow_type,
4821 &allocation, GTK_WIDGET (sheet->button),
4823 allocation.x, allocation.y,
4824 allocation.width, allocation.height);
4826 if ( button->overstruck)
4828 GdkPoint points[2] = {
4829 {allocation.x, allocation.y},
4830 {allocation.x + allocation.width,
4831 allocation.y + allocation.height}
4834 gtk_paint_polygon (sheet->button->style,
4846 if (button->label_visible)
4848 text_height = DEFAULT_ROW_HEIGHT -
4849 2 * COLUMN_TITLES_HEIGHT;
4851 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4853 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc,
4856 allocation.y += 2 * sheet->button->style->ythickness;
4858 if (button->label && strlen (button->label) > 0)
4860 PangoRectangle rect;
4861 gchar *line = button->label;
4863 PangoLayout *layout = NULL;
4864 gint real_x = allocation.x;
4865 gint real_y = allocation.y;
4867 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), line);
4868 pango_layout_get_extents (layout, NULL, &rect);
4870 text_width = PANGO_PIXELS (rect.width);
4871 switch (button->justification)
4873 case GTK_JUSTIFY_LEFT:
4874 real_x = allocation.x + COLUMN_TITLES_HEIGHT;
4875 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4877 case GTK_JUSTIFY_RIGHT:
4878 real_x = allocation.x + allocation.width - text_width - COLUMN_TITLES_HEIGHT;
4879 align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
4881 case GTK_JUSTIFY_CENTER:
4883 real_x = allocation.x + (allocation.width - text_width)/2;
4884 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4885 pango_layout_set_justify (layout, TRUE);
4887 pango_layout_set_alignment (layout, align);
4888 gtk_paint_layout (GTK_WIDGET (sheet)->style,
4897 g_object_unref (layout);
4900 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4902 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc, NULL);
4906 psppire_sheet_button_free (button);
4910 /* Draw the column title buttons FIRST through to LAST */
4912 draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4916 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4918 if (!sheet->column_titles_visible) return;
4920 g_return_if_fail (first >= min_visible_column (sheet));
4921 g_return_if_fail (last <= max_visible_column (sheet));
4924 rect.height = sheet->column_title_area.height;
4925 rect.x = psppire_axis_start_pixel (sheet->haxis, first) + CELL_SPACING;
4926 rect.width = psppire_axis_start_pixel (sheet->haxis, last) + CELL_SPACING
4927 + psppire_axis_unit_size (sheet->haxis, last);
4929 rect.x -= sheet->hadjustment->value;
4931 minimize_int (&rect.width, sheet->column_title_area.width);
4932 maximize_int (&rect.x, 0);
4934 gdk_window_begin_paint_rect (sheet->column_title_window, &rect);
4936 for (col = first ; col <= last ; ++col)
4938 GdkRectangle allocation;
4939 gboolean is_sensitive = FALSE;
4941 PsppireSheetButton *
4942 button = psppire_sheet_model_get_column_button (sheet->model, col);
4944 allocation.x = psppire_axis_start_pixel (sheet->haxis, col)
4946 allocation.x -= sheet->hadjustment->value;
4948 allocation.height = sheet->column_title_area.height;
4949 allocation.width = psppire_axis_unit_size (sheet->haxis, col);
4950 is_sensitive = psppire_sheet_model_get_column_sensitivity (sheet->model, col);
4952 draw_button (sheet, sheet->column_title_window,
4953 button, is_sensitive, allocation);
4956 gdk_window_end_paint (sheet->column_title_window);
4961 draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4965 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4967 if (!sheet->row_titles_visible) return;
4969 g_return_if_fail (first >= min_visible_row (sheet));
4970 g_return_if_fail (last <= max_visible_row (sheet));
4973 rect.width = sheet->row_title_area.width;
4974 rect.y = psppire_axis_start_pixel (sheet->vaxis, first) + CELL_SPACING;
4975 rect.height = psppire_axis_start_pixel (sheet->vaxis, last) + CELL_SPACING
4976 + psppire_axis_unit_size (sheet->vaxis, last);
4978 rect.y -= sheet->vadjustment->value;
4980 minimize_int (&rect.height, sheet->row_title_area.height);
4981 maximize_int (&rect.y, 0);
4983 gdk_window_begin_paint_rect (sheet->row_title_window, &rect);
4984 for (row = first; row <= last; ++row)
4986 GdkRectangle allocation;
4988 gboolean is_sensitive = FALSE;
4990 PsppireSheetButton *button =
4991 psppire_sheet_model_get_row_button (sheet->model, row);
4993 allocation.y = psppire_axis_start_pixel (sheet->vaxis, row)
4995 allocation.y -= sheet->vadjustment->value;
4997 allocation.width = sheet->row_title_area.width;
4998 allocation.height = psppire_axis_unit_size (sheet->vaxis, row);
4999 is_sensitive = psppire_sheet_model_get_row_sensitivity (sheet->model, row);
5001 draw_button (sheet, sheet->row_title_window,
5002 button, is_sensitive, allocation);
5005 gdk_window_end_paint (sheet->row_title_window);
5012 * vadjustment_value_changed
5013 * hadjustment_value_changed */
5017 update_adjustment (GtkAdjustment *adj, PsppireAxis *axis, gint page_size)
5020 (adj->value + adj->page_size)
5022 (adj->upper - adj->lower);
5024 const glong last_item = psppire_axis_unit_count (axis) - 1;
5026 if (isnan (position) || position < 0)
5030 psppire_axis_start_pixel (axis, last_item)
5032 psppire_axis_unit_size (axis, last_item)
5036 adj->page_size = page_size;
5039 adj->value = position * (adj->upper - adj->lower) - adj->page_size;
5041 if ( adj->value < adj->lower)
5042 adj->value = adj->lower;
5045 gtk_adjustment_changed (adj);
5050 adjust_scrollbars (PsppireSheet *sheet)
5054 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
5057 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
5059 if ( sheet->row_titles_visible)
5060 width -= sheet->row_title_area.width;
5062 if (sheet->column_titles_visible)
5063 height -= sheet->column_title_area.height;
5065 if (sheet->vadjustment)
5067 glong last_row = psppire_axis_unit_count (sheet->vaxis) - 1;
5069 sheet->vadjustment->step_increment =
5071 psppire_axis_unit_size (sheet->vaxis, last_row);
5073 sheet->vadjustment->page_increment =
5075 sheet->column_title_area.height -
5076 psppire_axis_unit_size (sheet->vaxis, last_row);
5078 update_adjustment (sheet->vadjustment, sheet->vaxis, height);
5081 if (sheet->hadjustment)
5083 gint last_col = psppire_axis_unit_count (sheet->haxis) - 1;
5084 sheet->hadjustment->step_increment = 1;
5086 sheet->hadjustment->page_increment = width;
5088 sheet->hadjustment->upper =
5089 psppire_axis_start_pixel (sheet->haxis, last_col)
5091 psppire_axis_unit_size (sheet->haxis, last_col)
5094 update_adjustment (sheet->hadjustment, sheet->haxis, width);
5098 /* Subtracts the region of WIDGET from REGION */
5100 subtract_widget_region (GdkRegion *region, GtkWidget *widget)
5103 GdkRectangle intersect;
5106 gdk_region_get_clipbox (region, &rect);
5107 gtk_widget_intersect (widget,
5111 region2 = gdk_region_rectangle (&intersect);
5112 gdk_region_subtract (region, region2);
5113 gdk_region_destroy (region2);
5117 vadjustment_value_changed (GtkAdjustment *adjustment,
5121 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5123 g_return_if_fail (adjustment != NULL);
5125 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
5127 gtk_widget_hide (sheet->entry_widget);
5130 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
5132 subtract_widget_region (region, sheet->button);
5133 gdk_window_begin_paint_region (sheet->sheet_window, region);
5135 draw_sheet_region (sheet, region);
5137 draw_row_title_buttons (sheet);
5138 psppire_sheet_draw_active_cell (sheet);
5140 gdk_window_end_paint (sheet->sheet_window);
5141 gdk_region_destroy (region);
5146 hadjustment_value_changed (GtkAdjustment *adjustment,
5150 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5152 g_return_if_fail (adjustment != NULL);
5154 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
5156 gtk_widget_hide (sheet->entry_widget);
5160 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
5162 subtract_widget_region (region, sheet->button);
5163 gdk_window_begin_paint_region (sheet->sheet_window, region);
5165 draw_sheet_region (sheet, region);
5167 draw_column_title_buttons (sheet);
5169 psppire_sheet_draw_active_cell (sheet);
5171 gdk_window_end_paint (sheet->sheet_window);
5173 gdk_region_destroy (region);
5177 /* COLUMN RESIZING */
5179 draw_xor_vline (PsppireSheet *sheet)
5182 gint xpos = sheet->x_drag;
5183 gdk_drawable_get_size (sheet->sheet_window,
5186 if (sheet->row_titles_visible)
5187 xpos += sheet->row_title_area.width;
5189 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
5191 sheet->column_title_area.height,
5193 height + CELL_SPACING);
5198 draw_xor_hline (PsppireSheet *sheet)
5202 gint ypos = sheet->y_drag;
5204 gdk_drawable_get_size (sheet->sheet_window,
5208 if (sheet->column_titles_visible)
5209 ypos += sheet->column_title_area.height;
5211 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
5212 sheet->row_title_area.width,
5214 width + CELL_SPACING,
5218 /* SELECTED RANGE */
5220 draw_xor_rectangle (PsppireSheet *sheet, PsppireSheetRange range)
5223 GdkRectangle clip_area, area;
5226 area.x = psppire_axis_start_pixel (sheet->haxis, range.col0);
5227 area.y = psppire_axis_start_pixel (sheet->vaxis, range.row0);
5228 area.width = psppire_axis_start_pixel (sheet->haxis, range.coli)- area.x+
5229 psppire_axis_unit_size (sheet->haxis, range.coli);
5230 area.height = psppire_axis_start_pixel (sheet->vaxis, range.rowi)- area.y +
5231 psppire_axis_unit_size (sheet->vaxis, range.rowi);
5233 clip_area.x = sheet->row_title_area.width;
5234 clip_area.y = sheet->column_title_area.height;
5236 gdk_drawable_get_size (sheet->sheet_window,
5237 &clip_area.width, &clip_area.height);
5239 if (!sheet->row_titles_visible) clip_area.x = 0;
5240 if (!sheet->column_titles_visible) clip_area.y = 0;
5244 area.width = area.width + area.x;
5247 if (area.width > clip_area.width) area.width = clip_area.width + 10;
5250 area.height = area.height + area.y;
5253 if (area.height > clip_area.height) area.height = clip_area.height + 10;
5257 clip_area.width += 3;
5258 clip_area.height += 3;
5260 gdk_gc_get_values (sheet->xor_gc, &values);
5262 gdk_gc_set_clip_rectangle (sheet->xor_gc, &clip_area);
5264 gdk_draw_rectangle (sheet->sheet_window,
5267 area.x + i, area.y + i,
5268 area.width - 2 * i, area.height - 2 * i);
5271 gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
5273 gdk_gc_set_foreground (sheet->xor_gc, &values.foreground);
5278 set_column_width (PsppireSheet *sheet,
5282 g_return_if_fail (sheet != NULL);
5283 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
5285 if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis))
5291 psppire_axis_resize (sheet->haxis, column,
5292 width - sheet->cell_padding->left -
5293 sheet->cell_padding->right);
5295 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
5297 draw_column_title_buttons (sheet);
5298 adjust_scrollbars (sheet);
5299 psppire_sheet_size_allocate_entry (sheet);
5300 redraw_range (sheet, NULL);
5305 set_row_height (PsppireSheet *sheet,
5309 g_return_if_fail (sheet != NULL);
5310 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
5312 if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis))
5318 psppire_axis_resize (sheet->vaxis, row,
5319 height - sheet->cell_padding->top -
5320 sheet->cell_padding->bottom);
5322 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) )
5324 draw_row_title_buttons (sheet);
5325 adjust_scrollbars (sheet);
5326 psppire_sheet_size_allocate_entry (sheet);
5327 redraw_range (sheet, NULL);
5332 psppire_sheet_get_attributes (const PsppireSheet *sheet, gint row, gint col,
5333 PsppireSheetCellAttr *attr)
5336 const GtkJustification *j ;
5337 GdkColormap *colormap;
5339 g_return_val_if_fail (sheet != NULL, FALSE);
5340 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), FALSE);
5342 if (row < 0 || col < 0) return FALSE;
5344 attr->foreground = GTK_WIDGET (sheet)->style->black;
5345 attr->background = sheet->color[BG_COLOR];
5347 attr->border.width = 0;
5348 attr->border.line_style = GDK_LINE_SOLID;
5349 attr->border.cap_style = GDK_CAP_NOT_LAST;
5350 attr->border.join_style = GDK_JOIN_MITER;
5351 attr->border.mask = 0;
5352 attr->border.color = GTK_WIDGET (sheet)->style->black;
5354 colormap = gtk_widget_get_colormap (GTK_WIDGET (sheet));
5355 fg = psppire_sheet_model_get_foreground (sheet->model, row, col);
5358 gdk_colormap_alloc_color (colormap, fg, TRUE, TRUE);
5359 attr->foreground = *fg;
5362 bg = psppire_sheet_model_get_background (sheet->model, row, col);
5365 gdk_colormap_alloc_color (colormap, bg, TRUE, TRUE);
5366 attr->background = *bg;
5369 attr->justification =
5370 psppire_sheet_model_get_column_justification (sheet->model, col);
5372 j = psppire_sheet_model_get_justification (sheet->model, row, col);
5374 attr->justification = *j;
5380 psppire_sheet_button_size_request (PsppireSheet *sheet,
5381 const PsppireSheetButton *button,
5382 GtkRequisition *button_requisition)
5384 GtkRequisition requisition;
5385 GtkRequisition label_requisition;
5387 label_requisition.height = DEFAULT_ROW_HEIGHT;
5388 label_requisition.width = COLUMN_MIN_WIDTH;
5390 requisition.height = DEFAULT_ROW_HEIGHT;
5391 requisition.width = COLUMN_MIN_WIDTH;
5394 *button_requisition = requisition;
5395 button_requisition->width = MAX (requisition.width, label_requisition.width);
5396 button_requisition->height = MAX (requisition.height, label_requisition.height);
5401 psppire_sheet_forall (GtkContainer *container,
5402 gboolean include_internals,
5403 GtkCallback callback,
5404 gpointer callback_data)
5406 PsppireSheet *sheet = PSPPIRE_SHEET (container);
5408 g_return_if_fail (callback != NULL);
5410 if (sheet->button && sheet->button->parent)
5411 (* callback) (sheet->button, callback_data);
5413 if (sheet->entry_widget && GTK_IS_CONTAINER (sheet->entry_widget))
5414 (* callback) (sheet->entry_widget, callback_data);
5419 psppire_sheet_get_model (const PsppireSheet *sheet)
5421 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
5423 return sheet->model;
5427 PsppireSheetButton *
5428 psppire_sheet_button_new (void)
5430 PsppireSheetButton *button = g_malloc (sizeof (PsppireSheetButton));
5432 button->state = GTK_STATE_NORMAL;
5433 button->label = NULL;
5434 button->label_visible = TRUE;
5435 button->justification = GTK_JUSTIFY_FILL;
5436 button->overstruck = FALSE;
5443 psppire_sheet_button_free (PsppireSheetButton *button)
5445 if (!button) return ;
5447 g_free (button->label);
5452 append_cell_text (GString *string, const PsppireSheet *sheet, gint r, gint c)
5454 gchar *celltext = psppire_sheet_cell_get_text (sheet, r, c);
5456 if ( NULL == celltext)
5459 g_string_append (string, celltext);
5465 range_to_text (const PsppireSheet *sheet)
5470 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
5473 string = g_string_sized_new (80);
5475 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
5477 for (c = sheet->range.col0; c < sheet->range.coli; ++c)
5479 append_cell_text (string, sheet, r, c);
5480 g_string_append (string, "\t");
5482 append_cell_text (string, sheet, r, c);
5483 if ( r < sheet->range.rowi)
5484 g_string_append (string, "\n");
5491 range_to_html (const PsppireSheet *sheet)
5496 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
5499 string = g_string_sized_new (480);
5501 g_string_append (string, "<html>\n");
5502 g_string_append (string, "<body>\n");
5503 g_string_append (string, "<table>\n");
5504 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
5506 g_string_append (string, "<tr>\n");
5507 for (c = sheet->range.col0; c <= sheet->range.coli; ++c)
5509 g_string_append (string, "<td>");
5510 append_cell_text (string, sheet, r, c);
5511 g_string_append (string, "</td>\n");
5513 g_string_append (string, "</tr>\n");
5515 g_string_append (string, "</table>\n");
5516 g_string_append (string, "</body>\n");
5517 g_string_append (string, "</html>\n");
5529 primary_get_cb (GtkClipboard *clipboard,
5530 GtkSelectionData *selection_data,
5534 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5535 GString *string = NULL;
5539 case SELECT_FMT_TEXT:
5540 string = range_to_text (sheet);
5542 case SELECT_FMT_HTML:
5543 string = range_to_html (sheet);
5546 g_assert_not_reached ();
5549 gtk_selection_data_set (selection_data, selection_data->target,
5551 (const guchar *) string->str, string->len);
5552 g_string_free (string, TRUE);
5556 primary_clear_cb (GtkClipboard *clipboard,
5559 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5560 if ( ! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
5563 psppire_sheet_real_unselect_range (sheet, NULL);
5567 psppire_sheet_update_primary_selection (PsppireSheet *sheet)
5569 static const GtkTargetEntry targets[] = {
5570 { "UTF8_STRING", 0, SELECT_FMT_TEXT },
5571 { "STRING", 0, SELECT_FMT_TEXT },
5572 { "TEXT", 0, SELECT_FMT_TEXT },
5573 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
5574 { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
5575 { "text/plain", 0, SELECT_FMT_TEXT },
5576 { "text/html", 0, SELECT_FMT_HTML }
5579 GtkClipboard *clipboard;
5581 if (!GTK_WIDGET_REALIZED (sheet))
5584 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (sheet),
5585 GDK_SELECTION_PRIMARY);
5587 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
5589 if (!gtk_clipboard_set_with_owner (clipboard, targets,
5590 G_N_ELEMENTS (targets),
5591 primary_get_cb, primary_clear_cb,
5593 primary_clear_cb (clipboard, sheet);
5597 if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (sheet))
5598 gtk_clipboard_clear (clipboard);