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));
1985 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
1987 if (sheet->column_titles_visible)
1988 gdk_window_show (sheet->column_title_window);
1989 if (sheet->row_titles_visible)
1990 gdk_window_show (sheet->row_title_window);
1992 sheet->hover_window = create_hover_window ();
1994 draw_row_title_buttons (sheet);
1995 draw_column_title_buttons (sheet);
1997 psppire_sheet_update_primary_selection (sheet);
2000 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
2004 create_global_button (PsppireSheet *sheet)
2006 sheet->button = gtk_button_new_with_label (" ");
2008 GTK_WIDGET_UNSET_FLAGS(sheet->button, GTK_CAN_FOCUS);
2010 g_object_ref_sink (sheet->button);
2012 g_signal_connect (sheet->button,
2014 G_CALLBACK (global_button_clicked),
2019 size_allocate_global_button (PsppireSheet *sheet)
2021 GtkAllocation allocation;
2023 if (!sheet->column_titles_visible) return;
2024 if (!sheet->row_titles_visible) return;
2026 gtk_widget_size_request (sheet->button, NULL);
2030 allocation.width = sheet->row_title_area.width;
2031 allocation.height = sheet->column_title_area.height;
2033 gtk_widget_size_allocate (sheet->button, &allocation);
2037 global_button_clicked (GtkWidget *widget, gpointer data)
2039 psppire_sheet_click_cell (PSPPIRE_SHEET (data), -1, -1);
2044 psppire_sheet_unrealize (GtkWidget *widget)
2046 PsppireSheet *sheet;
2048 g_return_if_fail (widget != NULL);
2049 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
2051 sheet = PSPPIRE_SHEET (widget);
2053 gdk_cursor_unref (sheet->cursor_drag);
2054 sheet->cursor_drag = NULL;
2056 gdk_colormap_free_colors (gtk_widget_get_colormap (widget),
2057 sheet->color, n_COLORS);
2059 g_object_unref (sheet->xor_gc);
2060 g_object_unref (sheet->fg_gc);
2061 g_object_unref (sheet->bg_gc);
2063 destroy_hover_window (sheet->hover_window);
2065 gdk_window_destroy (sheet->sheet_window);
2066 gdk_window_destroy (sheet->column_title_window);
2067 gdk_window_destroy (sheet->row_title_window);
2069 gtk_widget_unparent (sheet->entry_widget);
2070 if (sheet->button != NULL)
2071 gtk_widget_unparent (sheet->button);
2073 if (GTK_WIDGET_CLASS (parent_class)->unrealize)
2074 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
2078 psppire_sheet_map (GtkWidget *widget)
2080 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2082 g_return_if_fail (widget != NULL);
2083 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
2085 if (!GTK_WIDGET_MAPPED (widget))
2087 GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
2089 gdk_window_show (widget->window);
2090 gdk_window_show (sheet->sheet_window);
2092 if (sheet->column_titles_visible)
2094 draw_column_title_buttons (sheet);
2095 gdk_window_show (sheet->column_title_window);
2097 if (sheet->row_titles_visible)
2099 draw_row_title_buttons (sheet);
2100 gdk_window_show (sheet->row_title_window);
2103 if (!GTK_WIDGET_MAPPED (sheet->entry_widget)
2104 && sheet->active_cell.row >= 0
2105 && sheet->active_cell.col >= 0 )
2107 gtk_widget_show (sheet->entry_widget);
2108 gtk_widget_map (sheet->entry_widget);
2111 if (!GTK_WIDGET_MAPPED (sheet->button))
2113 gtk_widget_show (sheet->button);
2114 gtk_widget_map (sheet->button);
2117 redraw_range (sheet, NULL);
2118 change_active_cell (sheet,
2119 sheet->active_cell.row,
2120 sheet->active_cell.col);
2125 psppire_sheet_unmap (GtkWidget *widget)
2127 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2129 if (!GTK_WIDGET_MAPPED (widget))
2132 GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
2134 gdk_window_hide (sheet->sheet_window);
2135 if (sheet->column_titles_visible)
2136 gdk_window_hide (sheet->column_title_window);
2137 if (sheet->row_titles_visible)
2138 gdk_window_hide (sheet->row_title_window);
2139 gdk_window_hide (widget->window);
2141 if (GTK_WIDGET_MAPPED (sheet->entry_widget))
2142 gtk_widget_unmap (sheet->entry_widget);
2144 if (GTK_WIDGET_MAPPED (sheet->button))
2145 gtk_widget_unmap (sheet->button);
2148 /* get cell attributes of the given cell */
2149 /* TRUE means that the cell is currently allocated */
2150 static gboolean psppire_sheet_get_attributes (const PsppireSheet *sheet,
2152 PsppireSheetCellAttr *attributes);
2157 psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint col)
2159 PangoLayout *layout;
2160 PangoRectangle text;
2161 PangoFontDescription *font_desc = GTK_WIDGET (sheet)->style->font_desc;
2166 PsppireSheetCellAttr attributes;
2169 g_return_if_fail (sheet != NULL);
2171 /* bail now if we aren't yet drawable */
2172 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
2175 row >= psppire_axis_unit_count (sheet->vaxis))
2179 col >= psppire_axis_unit_count (sheet->haxis))
2182 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2184 /* select GC for background rectangle */
2185 gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground);
2186 gdk_gc_set_foreground (sheet->bg_gc, &attributes.background);
2188 rectangle_from_cell (sheet, row, col, &area);
2190 gdk_gc_set_line_attributes (sheet->fg_gc, 1, 0, 0, 0);
2192 if (sheet->show_grid)
2194 gdk_gc_set_foreground (sheet->bg_gc, &sheet->color[GRID_COLOR]);
2196 gdk_draw_rectangle (sheet->sheet_window,
2200 area.width, area.height);
2204 label = psppire_sheet_cell_get_text (sheet, row, col);
2209 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), label);
2210 dispose_string (sheet, label);
2213 pango_layout_set_font_description (layout, font_desc);
2215 pango_layout_get_pixel_extents (layout, NULL, &text);
2217 gdk_gc_set_clip_rectangle (sheet->fg_gc, &area);
2219 font_height = pango_font_description_get_size (font_desc);
2220 if ( !pango_font_description_get_size_is_absolute (font_desc))
2221 font_height /= PANGO_SCALE;
2224 if ( sheet->cell_padding )
2226 area.x += sheet->cell_padding->left;
2227 area.width -= sheet->cell_padding->right
2228 + sheet->cell_padding->left;
2230 area.y += sheet->cell_padding->top;
2231 area.height -= sheet->cell_padding->bottom
2233 sheet->cell_padding->top;
2236 /* Centre the text vertically */
2237 area.y += (area.height - font_height) / 2.0;
2239 switch (attributes.justification)
2241 case GTK_JUSTIFY_RIGHT:
2242 area.x += area.width - text.width;
2244 case GTK_JUSTIFY_CENTER:
2245 area.x += (area.width - text.width) / 2.0;
2247 case GTK_JUSTIFY_LEFT:
2251 g_critical ("Unhandled justification %d in column %d\n",
2252 attributes.justification, col);
2256 gdk_draw_layout (sheet->sheet_window, sheet->fg_gc,
2261 gdk_gc_set_clip_rectangle (sheet->fg_gc, NULL);
2262 g_object_unref (layout);
2267 draw_sheet_region (PsppireSheet *sheet, GdkRegion *region)
2269 PsppireSheetRange range;
2274 PsppireSheetRange drawing_range;
2276 gdk_region_get_clipbox (region, &area);
2278 y = area.y + sheet->vadjustment->value;
2279 x = area.x + sheet->hadjustment->value;
2281 if ( sheet->column_titles_visible)
2282 y -= sheet->column_title_area.height;
2284 if ( sheet->row_titles_visible)
2285 x -= sheet->row_title_area.width;
2287 maximize_int (&x, 0);
2288 maximize_int (&y, 0);
2290 range.row0 = row_from_ypixel (sheet, y);
2291 range.rowi = row_from_ypixel (sheet, y + area.height);
2293 range.col0 = column_from_xpixel (sheet, x);
2294 range.coli = column_from_xpixel (sheet, x + area.width);
2296 g_return_if_fail (sheet != NULL);
2297 g_return_if_fail (PSPPIRE_SHEET (sheet));
2299 if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (sheet))) return;
2300 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2301 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
2304 drawing_range.row0 = MAX (range.row0, min_visible_row (sheet));
2305 drawing_range.col0 = MAX (range.col0, min_visible_column (sheet));
2306 drawing_range.rowi = MIN (range.rowi, max_visible_row (sheet));
2307 drawing_range.coli = MIN (range.coli, max_visible_column (sheet));
2309 g_return_if_fail (drawing_range.rowi >= drawing_range.row0);
2310 g_return_if_fail (drawing_range.coli >= drawing_range.col0);
2312 for (i = drawing_range.row0; i <= drawing_range.rowi; i++)
2314 for (j = drawing_range.col0; j <= drawing_range.coli; j++)
2315 psppire_sheet_cell_draw (sheet, i, j);
2318 if (sheet->state != PSPPIRE_SHEET_NORMAL &&
2319 psppire_sheet_range_isvisible (sheet, &sheet->range))
2320 psppire_sheet_range_draw_selection (sheet, drawing_range);
2323 if (sheet->state == GTK_STATE_NORMAL &&
2324 sheet->active_cell.row >= drawing_range.row0 &&
2325 sheet->active_cell.row <= drawing_range.rowi &&
2326 sheet->active_cell.col >= drawing_range.col0 &&
2327 sheet->active_cell.col <= drawing_range.coli)
2328 psppire_sheet_show_entry_widget (sheet);
2333 psppire_sheet_range_draw_selection (PsppireSheet *sheet, PsppireSheetRange range)
2337 PsppireSheetRange aux;
2339 if (range.col0 > sheet->range.coli || range.coli < sheet->range.col0 ||
2340 range.row0 > sheet->range.rowi || range.rowi < sheet->range.row0)
2343 if (!psppire_sheet_range_isvisible (sheet, &range)) return;
2344 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2348 range.col0 = MAX (sheet->range.col0, range.col0);
2349 range.coli = MIN (sheet->range.coli, range.coli);
2350 range.row0 = MAX (sheet->range.row0, range.row0);
2351 range.rowi = MIN (sheet->range.rowi, range.rowi);
2353 range.col0 = MAX (range.col0, min_visible_column (sheet));
2354 range.coli = MIN (range.coli, max_visible_column (sheet));
2355 range.row0 = MAX (range.row0, min_visible_row (sheet));
2356 range.rowi = MIN (range.rowi, max_visible_row (sheet));
2358 for (i = range.row0; i <= range.rowi; i++)
2360 for (j = range.col0; j <= range.coli; j++)
2362 if (psppire_sheet_cell_get_state (sheet, i, j) == GTK_STATE_SELECTED)
2364 rectangle_from_cell (sheet, i, j, &area);
2366 if (i == sheet->range.row0)
2368 area.y = area.y + 2;
2369 area.height = area.height - 2;
2371 if (i == sheet->range.rowi) area.height = area.height - 3;
2372 if (j == sheet->range.col0)
2374 area.x = area.x + 2;
2375 area.width = area.width - 2;
2377 if (j == sheet->range.coli) area.width = area.width - 3;
2379 if (i != sheet->active_cell.row || j != sheet->active_cell.col)
2381 gdk_draw_rectangle (sheet->sheet_window,
2384 area.x + 1, area.y + 1,
2385 area.width, area.height);
2392 psppire_sheet_draw_border (sheet, sheet->range);
2396 safe_strcmp (const gchar *s1, const gchar *s2)
2398 if ( !s1 && !s2) return 0;
2399 if ( !s1) return -1;
2400 if ( !s2) return +1;
2401 return strcmp (s1, s2);
2405 psppire_sheet_set_cell (PsppireSheet *sheet, gint row, gint col,
2406 GtkJustification justification,
2409 PsppireSheetModel *model ;
2412 g_return_if_fail (sheet != NULL);
2413 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2415 if (col >= psppire_axis_unit_count (sheet->haxis)
2416 || row >= psppire_axis_unit_count (sheet->vaxis))
2419 if (col < 0 || row < 0) return;
2421 model = psppire_sheet_get_model (sheet);
2423 old_text = psppire_sheet_model_get_string (model, row, col);
2425 if (0 != safe_strcmp (old_text, text))
2427 g_signal_handler_block (sheet->model, sheet->update_handler_id);
2428 psppire_sheet_model_set_string (model, text, row, col);
2429 g_signal_handler_unblock (sheet->model, sheet->update_handler_id);
2432 if ( psppire_sheet_model_free_strings (model))
2438 psppire_sheet_cell_clear (PsppireSheet *sheet, gint row, gint column)
2440 PsppireSheetRange range;
2442 g_return_if_fail (sheet != NULL);
2443 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2444 if (column >= psppire_axis_unit_count (sheet->haxis) ||
2445 row >= psppire_axis_unit_count (sheet->vaxis)) return;
2447 if (column < 0 || row < 0) return;
2451 range.col0 = min_visible_column (sheet);
2452 range.coli = max_visible_column (sheet);
2454 psppire_sheet_real_cell_clear (sheet, row, column);
2456 redraw_range (sheet, &range);
2460 psppire_sheet_real_cell_clear (PsppireSheet *sheet, gint row, gint column)
2462 PsppireSheetModel *model = psppire_sheet_get_model (sheet);
2464 gchar *old_text = psppire_sheet_cell_get_text (sheet, row, column);
2466 if (old_text && strlen (old_text) > 0 )
2468 psppire_sheet_model_datum_clear (model, row, column);
2471 dispose_string (sheet, old_text);
2475 psppire_sheet_cell_get_text (const PsppireSheet *sheet, gint row, gint col)
2477 PsppireSheetModel *model;
2478 g_return_val_if_fail (sheet != NULL, NULL);
2479 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
2481 if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis))
2483 if (col < 0 || row < 0) return NULL;
2485 model = psppire_sheet_get_model (sheet);
2490 return psppire_sheet_model_get_string (model, row, col);
2495 psppire_sheet_cell_get_state (PsppireSheet *sheet, gint row, gint col)
2498 PsppireSheetRange *range;
2500 g_return_val_if_fail (sheet != NULL, 0);
2501 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2502 if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis)) return 0;
2503 if (col < 0 || row < 0) return 0;
2505 state = sheet->state;
2506 range = &sheet->range;
2510 case PSPPIRE_SHEET_NORMAL:
2511 return GTK_STATE_NORMAL;
2513 case PSPPIRE_SHEET_ROW_SELECTED:
2514 if (row >= range->row0 && row <= range->rowi)
2515 return GTK_STATE_SELECTED;
2517 case PSPPIRE_SHEET_COLUMN_SELECTED:
2518 if (col >= range->col0 && col <= range->coli)
2519 return GTK_STATE_SELECTED;
2521 case PSPPIRE_SHEET_RANGE_SELECTED:
2522 if (row >= range->row0 && row <= range->rowi && \
2523 col >= range->col0 && col <= range->coli)
2524 return GTK_STATE_SELECTED;
2527 return GTK_STATE_NORMAL;
2530 /* Convert X, Y (in pixels) to *ROW, *COLUMN
2531 If the function returns FALSE, then the results will be unreliable.
2534 psppire_sheet_get_pixel_info (PsppireSheet *sheet,
2542 *column = -G_MAXINT;
2544 g_return_val_if_fail (sheet != NULL, 0);
2545 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2547 /* bounds checking, return false if the user clicked
2555 if ( sheet->column_titles_visible)
2556 y -= sheet->column_title_area.height;
2558 y += sheet->vadjustment->value;
2560 if ( y < 0 && sheet->column_titles_visible)
2566 trow = row_from_ypixel (sheet, y);
2567 if (trow > psppire_axis_unit_count (sheet->vaxis))
2573 if ( sheet->row_titles_visible)
2574 x -= sheet->row_title_area.width;
2576 x += sheet->hadjustment->value;
2578 if ( x < 0 && sheet->row_titles_visible)
2584 tcol = column_from_xpixel (sheet, x);
2585 if (tcol > psppire_axis_unit_count (sheet->haxis))
2595 psppire_sheet_get_cell_area (PsppireSheet *sheet,
2600 g_return_val_if_fail (sheet != NULL, 0);
2601 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2603 if (row >= psppire_axis_unit_count (sheet->vaxis) || column >= psppire_axis_unit_count (sheet->haxis))
2606 area->x = (column == -1) ? 0 : psppire_axis_start_pixel (sheet->haxis, column);
2607 area->y = (row == -1) ? 0 : psppire_axis_start_pixel (sheet->vaxis, row);
2609 area->width= (column == -1) ? sheet->row_title_area.width
2610 : psppire_axis_unit_size (sheet->haxis, column);
2612 area->height= (row == -1) ? sheet->column_title_area.height
2613 : psppire_axis_unit_size (sheet->vaxis, row);
2619 psppire_sheet_set_active_cell (PsppireSheet *sheet, gint row, gint col)
2621 g_return_if_fail (sheet != NULL);
2622 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2624 if (row < -1 || col < -1)
2627 if (row >= psppire_axis_unit_count (sheet->vaxis)
2629 col >= psppire_axis_unit_count (sheet->haxis))
2632 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2635 if ( row == -1 || col == -1)
2637 psppire_sheet_hide_entry_widget (sheet);
2641 change_active_cell (sheet, row, col);
2645 psppire_sheet_get_active_cell (PsppireSheet *sheet, gint *row, gint *column)
2647 g_return_if_fail (sheet != NULL);
2648 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2650 if ( row ) *row = sheet->active_cell.row;
2651 if (column) *column = sheet->active_cell.col;
2655 entry_load_text (PsppireSheet *sheet)
2659 GtkJustification justification;
2660 PsppireSheetCellAttr attributes;
2662 if (!GTK_WIDGET_VISIBLE (sheet->entry_widget)) return;
2663 if (sheet->state != GTK_STATE_NORMAL) return;
2665 row = sheet->active_cell.row;
2666 col = sheet->active_cell.col;
2668 if (row < 0 || col < 0) return;
2670 text = gtk_entry_get_text (psppire_sheet_get_entry (sheet));
2672 if (text && strlen (text) > 0)
2674 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2675 justification = attributes.justification;
2676 psppire_sheet_set_cell (sheet, row, col, justification, text);
2682 psppire_sheet_hide_entry_widget (PsppireSheet *sheet)
2684 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2687 if (sheet->active_cell.row < 0 ||
2688 sheet->active_cell.col < 0) return;
2690 gtk_widget_hide (sheet->entry_widget);
2691 gtk_widget_unmap (sheet->entry_widget);
2693 GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2697 change_active_cell (PsppireSheet *sheet, gint row, gint col)
2699 gint old_row, old_col;
2701 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2703 if (row < 0 || col < 0)
2706 if ( row > psppire_axis_unit_count (sheet->vaxis)
2707 || col > psppire_axis_unit_count (sheet->haxis))
2710 if (sheet->state != PSPPIRE_SHEET_NORMAL)
2712 sheet->state = PSPPIRE_SHEET_NORMAL;
2713 psppire_sheet_real_unselect_range (sheet, NULL);
2716 old_row = sheet->active_cell.row;
2717 old_col = sheet->active_cell.col;
2719 /* Erase the old cell */
2720 psppire_sheet_draw_active_cell (sheet);
2722 entry_load_text (sheet);
2724 sheet->range.row0 = row;
2725 sheet->range.col0 = col;
2726 sheet->range.rowi = row;
2727 sheet->range.coli = col;
2728 sheet->active_cell.row = row;
2729 sheet->active_cell.col = col;
2730 sheet->selection_cell.row = row;
2731 sheet->selection_cell.col = col;
2733 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
2735 GTK_WIDGET_UNSET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2737 psppire_sheet_draw_active_cell (sheet);
2738 psppire_sheet_show_entry_widget (sheet);
2740 GTK_WIDGET_SET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2742 g_signal_emit (sheet, sheet_signals [ACTIVATE], 0,
2743 row, col, old_row, old_col);
2748 psppire_sheet_show_entry_widget (PsppireSheet *sheet)
2750 GtkEntry *sheet_entry;
2751 PsppireSheetCellAttr attributes;
2755 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2757 row = sheet->active_cell.row;
2758 col = sheet->active_cell.col;
2760 /* Don't show the active cell, if there is no active cell: */
2761 if (! (row >= 0 && col >= 0)) /* e.g row or coll == -1. */
2764 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2765 if (sheet->state != PSPPIRE_SHEET_NORMAL) return;
2766 if (PSPPIRE_SHEET_IN_SELECTION (sheet)) return;
2768 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2770 sheet_entry = psppire_sheet_get_entry (sheet);
2772 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2774 if (GTK_IS_ENTRY (sheet_entry))
2776 gchar *text = psppire_sheet_cell_get_text (sheet, row, col);
2777 const gchar *old_text = gtk_entry_get_text (GTK_ENTRY (sheet_entry));
2780 text = g_strdup ("");
2782 if (strcmp (old_text, text) != 0)
2783 gtk_entry_set_text (sheet_entry, text);
2785 dispose_string (sheet, text);
2788 switch (attributes.justification)
2790 case GTK_JUSTIFY_RIGHT:
2791 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 1.0);
2793 case GTK_JUSTIFY_CENTER:
2794 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.5);
2796 case GTK_JUSTIFY_LEFT:
2798 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.0);
2804 psppire_sheet_size_allocate_entry (sheet);
2806 gtk_widget_set_sensitive (GTK_WIDGET (sheet_entry),
2807 psppire_sheet_model_is_editable (sheet->model,
2809 gtk_widget_map (sheet->entry_widget);
2813 psppire_sheet_draw_active_cell (PsppireSheet *sheet)
2816 PsppireSheetRange range;
2818 row = sheet->active_cell.row;
2819 col = sheet->active_cell.col;
2821 if (row < 0 || col < 0) return FALSE;
2823 if (!psppire_sheet_cell_isvisible (sheet, row, col))
2826 range.col0 = range.coli = col;
2827 range.row0 = range.rowi = row;
2829 psppire_sheet_draw_border (sheet, range);
2837 psppire_sheet_new_selection (PsppireSheet *sheet, PsppireSheetRange *range)
2839 gint i, j, mask1, mask2;
2840 gint state, selected;
2841 gint x, y, width, height;
2842 PsppireSheetRange new_range, aux_range;
2844 g_return_if_fail (sheet != NULL);
2846 if (range == NULL) range=&sheet->range;
2850 range->row0 = MIN (range->row0, sheet->range.row0);
2851 range->rowi = MAX (range->rowi, sheet->range.rowi);
2852 range->col0 = MIN (range->col0, sheet->range.col0);
2853 range->coli = MAX (range->coli, sheet->range.coli);
2855 range->row0 = MAX (range->row0, min_visible_row (sheet));
2856 range->rowi = MIN (range->rowi, max_visible_row (sheet));
2857 range->col0 = MAX (range->col0, min_visible_column (sheet));
2858 range->coli = MIN (range->coli, max_visible_column (sheet));
2860 aux_range.row0 = MAX (new_range.row0, min_visible_row (sheet));
2861 aux_range.rowi = MIN (new_range.rowi, max_visible_row (sheet));
2862 aux_range.col0 = MAX (new_range.col0, min_visible_column (sheet));
2863 aux_range.coli = MIN (new_range.coli, max_visible_column (sheet));
2865 for (i = range->row0; i <= range->rowi; i++)
2867 for (j = range->col0; j <= range->coli; j++)
2870 state = psppire_sheet_cell_get_state (sheet, i, j);
2871 selected= (i <= new_range.rowi && i >= new_range.row0 &&
2872 j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
2874 if (state == GTK_STATE_SELECTED && selected &&
2875 (i == sheet->range.row0 || i == sheet->range.rowi ||
2876 j == sheet->range.col0 || j == sheet->range.coli ||
2877 i == new_range.row0 || i == new_range.rowi ||
2878 j == new_range.col0 || j == new_range.coli))
2881 mask1 = i == sheet->range.row0 ? 1 : 0;
2882 mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1;
2883 mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1;
2884 mask1 = j == sheet->range.coli ? mask1 + 8 : mask1;
2886 mask2 = i == new_range.row0 ? 1 : 0;
2887 mask2 = i == new_range.rowi ? mask2 + 2 : mask2;
2888 mask2 = j == new_range.col0 ? mask2 + 4 : mask2;
2889 mask2 = j == new_range.coli ? mask2 + 8 : mask2;
2893 x = psppire_axis_start_pixel (sheet->haxis, j);
2894 y = psppire_axis_start_pixel (sheet->vaxis, i);
2895 width = psppire_axis_start_pixel (sheet->haxis, j)- x+
2896 psppire_axis_unit_size (sheet->haxis, j);
2897 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
2899 if (i == sheet->range.row0)
2902 height = height + 3;
2904 if (i == sheet->range.rowi) height = height + 3;
2905 if (j == sheet->range.col0)
2910 if (j == sheet->range.coli) width = width + 3;
2912 if (i != sheet->active_cell.row || j != sheet->active_cell.col)
2914 x = psppire_axis_start_pixel (sheet->haxis, j);
2915 y = psppire_axis_start_pixel (sheet->vaxis, i);
2916 width = psppire_axis_start_pixel (sheet->haxis, j)- x+
2917 psppire_axis_unit_size (sheet->haxis, j);
2919 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
2921 if (i == new_range.row0)
2924 height = height - 2;
2926 if (i == new_range.rowi) height = height - 3;
2927 if (j == new_range.col0)
2932 if (j == new_range.coli) width = width - 3;
2934 gdk_draw_rectangle (sheet->sheet_window,
2945 for (i = range->row0; i <= range->rowi; i++)
2947 for (j = range->col0; j <= range->coli; j++)
2950 state = psppire_sheet_cell_get_state (sheet, i, j);
2951 selected= (i <= new_range.rowi && i >= new_range.row0 &&
2952 j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
2954 if (state == GTK_STATE_SELECTED && !selected)
2957 x = psppire_axis_start_pixel (sheet->haxis, j);
2958 y = psppire_axis_start_pixel (sheet->vaxis, i);
2959 width = psppire_axis_start_pixel (sheet->haxis, j) - x + psppire_axis_unit_size (sheet->haxis, j);
2960 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
2962 if (i == sheet->range.row0)
2965 height = height + 3;
2967 if (i == sheet->range.rowi) height = height + 3;
2968 if (j == sheet->range.col0)
2973 if (j == sheet->range.coli) width = width + 3;
2979 for (i = range->row0; i <= range->rowi; i++)
2981 for (j = range->col0; j <= range->coli; j++)
2984 state = psppire_sheet_cell_get_state (sheet, i, j);
2985 selected= (i <= new_range.rowi && i >= new_range.row0 &&
2986 j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
2988 if (state != GTK_STATE_SELECTED && selected &&
2989 (i != sheet->active_cell.row || j != sheet->active_cell.col))
2992 x = psppire_axis_start_pixel (sheet->haxis, j);
2993 y = psppire_axis_start_pixel (sheet->vaxis, i);
2994 width = psppire_axis_start_pixel (sheet->haxis, j) - x + psppire_axis_unit_size (sheet->haxis, j);
2995 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
2997 if (i == new_range.row0)
3000 height = height - 2;
3002 if (i == new_range.rowi) height = height - 3;
3003 if (j == new_range.col0)
3008 if (j == new_range.coli) width = width - 3;
3010 gdk_draw_rectangle (sheet->sheet_window,
3021 for (i = aux_range.row0; i <= aux_range.rowi; i++)
3023 for (j = aux_range.col0; j <= aux_range.coli; j++)
3025 state = psppire_sheet_cell_get_state (sheet, i, j);
3027 mask1 = i == sheet->range.row0 ? 1 : 0;
3028 mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1;
3029 mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1;
3030 mask1 = j == sheet->range.coli ? mask1 + 8 : mask1;
3032 mask2 = i == new_range.row0 ? 1 : 0;
3033 mask2 = i == new_range.rowi ? mask2 + 2 : mask2;
3034 mask2 = j == new_range.col0 ? mask2 + 4 : mask2;
3035 mask2 = j == new_range.coli ? mask2 + 8 : mask2;
3036 if (mask2 != mask1 || (mask2 == mask1 && state != GTK_STATE_SELECTED))
3038 x = psppire_axis_start_pixel (sheet->haxis, j);
3039 y = psppire_axis_start_pixel (sheet->vaxis, i);
3040 width = psppire_axis_unit_size (sheet->haxis, j);
3041 height = psppire_axis_unit_size (sheet->vaxis, i);
3043 gdk_draw_rectangle (sheet->sheet_window,
3051 gdk_draw_rectangle (sheet->sheet_window,
3054 x + 1, y + height - 1,
3058 gdk_draw_rectangle (sheet->sheet_window,
3066 gdk_draw_rectangle (sheet->sheet_window,
3069 x + width - 1, y + 1,
3081 psppire_sheet_draw_border (PsppireSheet *sheet, PsppireSheetRange new_range)
3085 rectangle_from_range (sheet, &new_range, &area);
3087 gdk_draw_rectangle (sheet->sheet_window,
3091 area.width, area.height);
3096 psppire_sheet_real_select_range (PsppireSheet *sheet,
3097 const PsppireSheetRange *range)
3101 g_return_if_fail (sheet != NULL);
3103 if (range == NULL) range = &sheet->range;
3105 memcpy (&sheet->range, range, sizeof (*range));
3107 if (range->row0 < 0 || range->rowi < 0) return;
3108 if (range->col0 < 0 || range->coli < 0) return;
3110 state = sheet->state;
3113 if (range->coli != sheet->range.coli || range->col0 != sheet->range.col0 ||
3114 range->rowi != sheet->range.rowi || range->row0 != sheet->range.row0)
3116 psppire_sheet_new_selection (sheet, &sheet->range);
3120 psppire_sheet_range_draw_selection (sheet, sheet->range);
3124 psppire_sheet_update_primary_selection (sheet);
3126 g_signal_emit (sheet, sheet_signals[SELECT_RANGE], 0, &sheet->range);
3131 psppire_sheet_get_selected_range (PsppireSheet *sheet, PsppireSheetRange *range)
3133 g_return_if_fail (sheet != NULL);
3134 *range = sheet->range;
3139 psppire_sheet_select_range (PsppireSheet *sheet, const PsppireSheetRange *range)
3141 g_return_if_fail (sheet != NULL);
3143 if (range == NULL) range=&sheet->range;
3145 if (range->row0 < 0 || range->rowi < 0) return;
3146 if (range->col0 < 0 || range->coli < 0) return;
3149 if (sheet->state != PSPPIRE_SHEET_NORMAL)
3150 psppire_sheet_real_unselect_range (sheet, NULL);
3152 sheet->range.row0 = range->row0;
3153 sheet->range.rowi = range->rowi;
3154 sheet->range.col0 = range->col0;
3155 sheet->range.coli = range->coli;
3156 sheet->active_cell.row = range->row0;
3157 sheet->active_cell.col = range->col0;
3158 sheet->selection_cell.row = range->rowi;
3159 sheet->selection_cell.col = range->coli;
3161 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
3162 psppire_sheet_real_select_range (sheet, NULL);
3166 psppire_sheet_unselect_range (PsppireSheet *sheet)
3168 if (! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
3171 psppire_sheet_real_unselect_range (sheet, NULL);
3172 sheet->state = GTK_STATE_NORMAL;
3174 change_active_cell (sheet,
3175 sheet->active_cell.row, sheet->active_cell.col);
3180 psppire_sheet_real_unselect_range (PsppireSheet *sheet,
3181 const PsppireSheetRange *range)
3183 g_return_if_fail (sheet != NULL);
3184 g_return_if_fail (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)));
3187 range = &sheet->range;
3189 if (range->row0 < 0 || range->rowi < 0) return;
3190 if (range->col0 < 0 || range->coli < 0) return;
3192 g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, -1);
3193 g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, -1);
3195 sheet->range.row0 = -1;
3196 sheet->range.rowi = -1;
3197 sheet->range.col0 = -1;
3198 sheet->range.coli = -1;
3203 psppire_sheet_expose (GtkWidget *widget,
3204 GdkEventExpose *event)
3206 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3208 g_return_val_if_fail (event != NULL, FALSE);
3210 if (!GTK_WIDGET_DRAWABLE (widget))
3213 /* exposure events on the sheet */
3214 if (event->window == sheet->row_title_window &&
3215 sheet->row_titles_visible)
3217 draw_row_title_buttons_range (sheet,
3218 min_visible_row (sheet),
3219 max_visible_row (sheet));
3222 if (event->window == sheet->column_title_window &&
3223 sheet->column_titles_visible)
3225 draw_column_title_buttons_range (sheet,
3226 min_visible_column (sheet),
3227 max_visible_column (sheet));
3230 if (event->window == sheet->sheet_window)
3232 draw_sheet_region (sheet, event->region);
3235 if (sheet->state != PSPPIRE_SHEET_NORMAL)
3237 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
3238 psppire_sheet_range_draw (sheet, &sheet->range);
3240 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
3241 psppire_sheet_range_draw (sheet, &sheet->drag_range);
3243 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
3244 psppire_sheet_range_draw_selection (sheet, sheet->range);
3245 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
3246 draw_xor_rectangle (sheet, sheet->drag_range);
3250 if ((!PSPPIRE_SHEET_IN_XDRAG (sheet)) && (!PSPPIRE_SHEET_IN_YDRAG (sheet)))
3253 PsppireSheetRange range;
3254 range.row0 = range.rowi = sheet->active_cell.row;
3255 range.col0 = range.coli = sheet->active_cell.col;
3257 rectangle_from_range (sheet, &range, &rect);
3259 if (GDK_OVERLAP_RECTANGLE_OUT !=
3260 gdk_region_rect_in (event->region, &rect))
3262 psppire_sheet_draw_active_cell (sheet);
3268 (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
3275 psppire_sheet_button_press (GtkWidget *widget,
3276 GdkEventButton *event)
3278 PsppireSheet *sheet;
3279 GdkModifierType mods;
3284 g_return_val_if_fail (widget != NULL, FALSE);
3285 g_return_val_if_fail (PSPPIRE_IS_SHEET (widget), FALSE);
3286 g_return_val_if_fail (event != NULL, FALSE);
3288 sheet = PSPPIRE_SHEET (widget);
3290 /* Cancel any pending tooltips */
3291 if (sheet->motion_timer)
3293 g_source_remove (sheet->motion_timer);
3294 sheet->motion_timer = 0;
3297 gtk_widget_get_pointer (widget, &x, &y);
3298 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
3301 if (event->window == sheet->column_title_window)
3303 sheet->x_drag = event->x;
3304 g_signal_emit (sheet,
3305 sheet_signals[BUTTON_EVENT_COLUMN], 0,
3308 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
3310 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
3311 g_signal_emit (sheet,
3312 sheet_signals[DOUBLE_CLICK_COLUMN], 0, column);
3315 else if (event->window == sheet->row_title_window)
3317 g_signal_emit (sheet,
3318 sheet_signals[BUTTON_EVENT_ROW], 0,
3321 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
3323 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
3324 g_signal_emit (sheet,
3325 sheet_signals[DOUBLE_CLICK_ROW], 0, row);
3329 gdk_window_get_pointer (widget->window, NULL, NULL, &mods);
3331 if (! (mods & GDK_BUTTON1_MASK)) return TRUE;
3334 /* press on resize windows */
3335 if (event->window == sheet->column_title_window)
3337 sheet->x_drag = event->x;
3339 if (on_column_boundary (sheet, sheet->x_drag, &sheet->drag_cell.col))
3341 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
3342 gdk_pointer_grab (sheet->column_title_window, FALSE,
3343 GDK_POINTER_MOTION_HINT_MASK |
3344 GDK_BUTTON1_MOTION_MASK |
3345 GDK_BUTTON_RELEASE_MASK,
3346 NULL, NULL, event->time);
3348 draw_xor_vline (sheet);
3353 if (event->window == sheet->row_title_window)
3355 sheet->y_drag = event->y;
3357 if (on_row_boundary (sheet, sheet->y_drag, &sheet->drag_cell.row))
3359 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
3360 gdk_pointer_grab (sheet->row_title_window, FALSE,
3361 GDK_POINTER_MOTION_HINT_MASK |
3362 GDK_BUTTON1_MOTION_MASK |
3363 GDK_BUTTON_RELEASE_MASK,
3364 NULL, NULL, event->time);
3366 draw_xor_hline (sheet);
3371 /* the sheet itself does not handle other than single click events */
3372 if (event->type != GDK_BUTTON_PRESS) return FALSE;
3374 /* selections on the sheet */
3375 if (event->window == sheet->sheet_window)
3377 gtk_widget_get_pointer (widget, &x, &y);
3378 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
3379 gdk_pointer_grab (sheet->sheet_window, FALSE,
3380 GDK_POINTER_MOTION_HINT_MASK |
3381 GDK_BUTTON1_MOTION_MASK |
3382 GDK_BUTTON_RELEASE_MASK,
3383 NULL, NULL, event->time);
3384 gtk_grab_add (GTK_WIDGET (sheet));
3386 if (sheet->selection_mode != GTK_SELECTION_SINGLE &&
3387 sheet->selection_mode != GTK_SELECTION_NONE &&
3388 sheet->cursor_drag->type == GDK_SIZING &&
3389 !PSPPIRE_SHEET_IN_SELECTION (sheet) && !PSPPIRE_SHEET_IN_RESIZE (sheet))
3391 if (sheet->state == GTK_STATE_NORMAL)
3393 row = sheet->active_cell.row;
3394 column = sheet->active_cell.col;
3395 sheet->active_cell.row = row;
3396 sheet->active_cell.col = column;
3397 sheet->drag_range = sheet->range;
3398 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
3399 psppire_sheet_select_range (sheet, &sheet->drag_range);
3403 if (row > sheet->range.rowi) row--;
3404 if (column > sheet->range.coli) column--;
3405 sheet->drag_cell.row = row;
3406 sheet->drag_cell.col = column;
3407 sheet->drag_range = sheet->range;
3408 draw_xor_rectangle (sheet, sheet->drag_range);
3409 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_RESIZE);
3411 else if (sheet->cursor_drag->type == GDK_TOP_LEFT_ARROW &&
3412 !PSPPIRE_SHEET_IN_SELECTION (sheet)
3413 && ! PSPPIRE_SHEET_IN_DRAG (sheet)
3414 && sheet->active_cell.row >= 0
3415 && sheet->active_cell.col >= 0
3418 if (sheet->state == GTK_STATE_NORMAL)
3420 row = sheet->active_cell.row;
3421 column = sheet->active_cell.col;
3422 sheet->active_cell.row = row;
3423 sheet->active_cell.col = column;
3424 sheet->drag_range = sheet->range;
3425 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
3426 psppire_sheet_select_range (sheet, &sheet->drag_range);
3430 if (row < sheet->range.row0) row++;
3431 if (row > sheet->range.rowi) row--;
3432 if (column < sheet->range.col0) column++;
3433 if (column > sheet->range.coli) column--;
3434 sheet->drag_cell.row = row;
3435 sheet->drag_cell.col = column;
3436 sheet->drag_range = sheet->range;
3437 draw_xor_rectangle (sheet, sheet->drag_range);
3438 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_DRAG);
3442 veto = psppire_sheet_click_cell (sheet, row, column);
3443 if (veto) PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3447 if (event->window == sheet->column_title_window)
3449 gtk_widget_get_pointer (widget, &x, &y);
3450 if ( sheet->row_titles_visible)
3451 x -= sheet->row_title_area.width;
3453 x += sheet->hadjustment->value;
3455 column = column_from_xpixel (sheet, x);
3457 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
3459 veto = psppire_sheet_click_cell (sheet, -1, column);
3460 gtk_grab_add (GTK_WIDGET (sheet));
3461 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3465 if (event->window == sheet->row_title_window)
3467 gtk_widget_get_pointer (widget, &x, &y);
3468 if ( sheet->column_titles_visible)
3469 y -= sheet->column_title_area.height;
3471 y += sheet->vadjustment->value;
3473 row = row_from_ypixel (sheet, y);
3474 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
3476 veto = psppire_sheet_click_cell (sheet, row, -1);
3477 gtk_grab_add (GTK_WIDGET (sheet));
3478 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3486 psppire_sheet_click_cell (PsppireSheet *sheet, gint row, gint column)
3488 PsppireSheetCell cell;
3489 gboolean forbid_move;
3494 if (row >= psppire_axis_unit_count (sheet->vaxis)
3495 || column >= psppire_axis_unit_count (sheet->haxis))
3500 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
3501 &sheet->active_cell,
3507 if (sheet->state == GTK_STATE_NORMAL)
3510 row = sheet->active_cell.row;
3511 column = sheet->active_cell.col;
3513 change_active_cell (sheet, row, column);
3517 if (row == -1 && column >= 0)
3519 psppire_sheet_select_column (sheet, column);
3523 if (column == -1 && row >= 0)
3525 psppire_sheet_select_row (sheet, row);
3529 if (row == -1 && column == -1)
3531 sheet->range.row0 = 0;
3532 sheet->range.col0 = 0;
3533 sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
3535 psppire_axis_unit_count (sheet->haxis) - 1;
3536 sheet->active_cell.row = 0;
3537 sheet->active_cell.col = 0;
3538 psppire_sheet_select_range (sheet, NULL);
3542 if (sheet->state != PSPPIRE_SHEET_NORMAL)
3544 sheet->state = PSPPIRE_SHEET_NORMAL;
3545 psppire_sheet_real_unselect_range (sheet, NULL);
3549 change_active_cell (sheet, row, column);
3552 sheet->active_cell.row = row;
3553 sheet->active_cell.col = column;
3554 sheet->selection_cell.row = row;
3555 sheet->selection_cell.col = column;
3556 sheet->range.row0 = row;
3557 sheet->range.col0 = column;
3558 sheet->range.rowi = row;
3559 sheet->range.coli = column;
3560 sheet->state = PSPPIRE_SHEET_NORMAL;
3561 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3563 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
3569 psppire_sheet_button_release (GtkWidget *widget,
3570 GdkEventButton *event)
3572 GdkDisplay *display = gtk_widget_get_display (widget);
3574 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3576 /* release on resize windows */
3577 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3580 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
3581 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3583 gdk_display_pointer_ungrab (display, event->time);
3584 draw_xor_vline (sheet);
3587 psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)
3588 + sheet->hadjustment->value;
3590 set_column_width (sheet, sheet->drag_cell.col, width);
3595 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3598 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
3599 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3601 gdk_display_pointer_ungrab (display, event->time);
3602 draw_xor_hline (sheet);
3605 psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row) +
3606 sheet->vadjustment->value;
3608 set_row_height (sheet, sheet->drag_cell.row, height);
3613 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3615 PsppireSheetRange old_range;
3616 draw_xor_rectangle (sheet, sheet->drag_range);
3617 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_DRAG);
3618 gdk_display_pointer_ungrab (display, event->time);
3620 psppire_sheet_real_unselect_range (sheet, NULL);
3622 sheet->active_cell.row = sheet->active_cell.row +
3623 (sheet->drag_range.row0 - sheet->range.row0);
3624 sheet->active_cell.col = sheet->active_cell.col +
3625 (sheet->drag_range.col0 - sheet->range.col0);
3626 sheet->selection_cell.row = sheet->selection_cell.row +
3627 (sheet->drag_range.row0 - sheet->range.row0);
3628 sheet->selection_cell.col = sheet->selection_cell.col +
3629 (sheet->drag_range.col0 - sheet->range.col0);
3630 old_range = sheet->range;
3631 sheet->range = sheet->drag_range;
3632 sheet->drag_range = old_range;
3633 g_signal_emit (sheet, sheet_signals[MOVE_RANGE], 0,
3634 &sheet->drag_range, &sheet->range);
3635 psppire_sheet_select_range (sheet, &sheet->range);
3638 if (PSPPIRE_SHEET_IN_RESIZE (sheet))
3640 PsppireSheetRange old_range;
3641 draw_xor_rectangle (sheet, sheet->drag_range);
3642 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_RESIZE);
3643 gdk_display_pointer_ungrab (display, event->time);
3645 psppire_sheet_real_unselect_range (sheet, NULL);
3647 sheet->active_cell.row = sheet->active_cell.row +
3648 (sheet->drag_range.row0 - sheet->range.row0);
3649 sheet->active_cell.col = sheet->active_cell.col +
3650 (sheet->drag_range.col0 - sheet->range.col0);
3651 if (sheet->drag_range.row0 < sheet->range.row0)
3652 sheet->selection_cell.row = sheet->drag_range.row0;
3653 if (sheet->drag_range.rowi >= sheet->range.rowi)
3654 sheet->selection_cell.row = sheet->drag_range.rowi;
3655 if (sheet->drag_range.col0 < sheet->range.col0)
3656 sheet->selection_cell.col = sheet->drag_range.col0;
3657 if (sheet->drag_range.coli >= sheet->range.coli)
3658 sheet->selection_cell.col = sheet->drag_range.coli;
3659 old_range = sheet->range;
3660 sheet->range = sheet->drag_range;
3661 sheet->drag_range = old_range;
3663 if (sheet->state == GTK_STATE_NORMAL) sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
3664 g_signal_emit (sheet, sheet_signals[RESIZE_RANGE], 0,
3665 &sheet->drag_range, &sheet->range);
3666 psppire_sheet_select_range (sheet, &sheet->range);
3669 if (sheet->state == PSPPIRE_SHEET_NORMAL && PSPPIRE_SHEET_IN_SELECTION (sheet))
3671 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3672 gdk_display_pointer_ungrab (display, event->time);
3673 change_active_cell (sheet, sheet->active_cell.row,
3674 sheet->active_cell.col);
3677 if (PSPPIRE_SHEET_IN_SELECTION)
3678 gdk_display_pointer_ungrab (display, event->time);
3679 gtk_grab_remove (GTK_WIDGET (sheet));
3681 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3690 /* Shamelessly lifted from gtktooltips */
3692 psppire_sheet_subtitle_paint_window (GtkWidget *tip_window)
3696 gtk_widget_size_request (tip_window, &req);
3697 gtk_paint_flat_box (tip_window->style, tip_window->window,
3698 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
3699 NULL, GTK_WIDGET(tip_window), "tooltip",
3700 0, 0, req.width, req.height);
3706 destroy_hover_window (PsppireSheetHoverTitle *h)
3708 gtk_widget_destroy (h->window);
3712 static PsppireSheetHoverTitle *
3713 create_hover_window (void)
3715 PsppireSheetHoverTitle *hw = g_malloc (sizeof (*hw));
3717 hw->window = gtk_window_new (GTK_WINDOW_POPUP);
3719 #if GTK_CHECK_VERSION (2, 9, 0)
3720 gtk_window_set_type_hint (GTK_WINDOW (hw->window),
3721 GDK_WINDOW_TYPE_HINT_TOOLTIP);
3724 gtk_widget_set_app_paintable (hw->window, TRUE);
3725 gtk_window_set_resizable (GTK_WINDOW (hw->window), FALSE);
3726 gtk_widget_set_name (hw->window, "gtk-tooltips");
3727 gtk_container_set_border_width (GTK_CONTAINER (hw->window), 4);
3729 g_signal_connect (hw->window,
3731 G_CALLBACK (psppire_sheet_subtitle_paint_window),
3734 hw->label = gtk_label_new (NULL);
3737 gtk_label_set_line_wrap (GTK_LABEL (hw->label), TRUE);
3738 gtk_misc_set_alignment (GTK_MISC (hw->label), 0.5, 0.5);
3740 gtk_container_add (GTK_CONTAINER (hw->window), hw->label);
3742 gtk_widget_show (hw->label);
3744 g_signal_connect (hw->window,
3746 G_CALLBACK (gtk_widget_destroyed),
3752 #define HOVER_WINDOW_Y_OFFSET 2
3755 show_subtitle (PsppireSheet *sheet, gint row, gint column, const gchar *subtitle)
3764 gtk_label_set_text (GTK_LABEL (sheet->hover_window->label),
3768 sheet->hover_window->row = row;
3769 sheet->hover_window->column = column;
3771 gdk_window_get_origin (GTK_WIDGET (sheet)->window, &x, &y);
3773 gtk_widget_get_pointer (GTK_WIDGET (sheet), &px, &py);
3775 gtk_widget_show (sheet->hover_window->window);
3777 width = GTK_WIDGET (sheet->hover_window->label)->allocation.width;
3783 y += sheet->column_title_area.y;
3784 y += sheet->column_title_area.height;
3785 y += HOVER_WINDOW_Y_OFFSET;
3791 x += sheet->row_title_area.x;
3792 x += sheet->row_title_area.width * 2 / 3.0;
3795 gtk_window_move (GTK_WINDOW (sheet->hover_window->window),
3800 motion_timeout_callback (gpointer data)
3802 PsppireSheet *sheet = PSPPIRE_SHEET (data);
3806 gdk_threads_enter ();
3807 gtk_widget_get_pointer (GTK_WIDGET (sheet), &x, &y);
3809 if ( psppire_sheet_get_pixel_info (sheet, x, y, &row, &column) )
3811 if (sheet->row_title_under && row >= 0)
3813 gchar *text = psppire_sheet_model_get_row_subtitle (sheet->model, row);
3815 show_subtitle (sheet, row, -1, text);
3819 if (sheet->column_title_under && column >= 0)
3821 gchar *text = psppire_sheet_model_get_column_subtitle (sheet->model,
3824 show_subtitle (sheet, -1, column, text);
3830 gdk_threads_leave ();
3835 psppire_sheet_motion (GtkWidget *widget, GdkEventMotion *event)
3837 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3838 GdkModifierType mods;
3839 GdkCursorType new_cursor;
3842 GdkDisplay *display;
3844 g_return_val_if_fail (event != NULL, FALSE);
3846 display = gtk_widget_get_display (widget);
3848 /* selections on the sheet */
3852 if (!GTK_WIDGET_VISIBLE (sheet->hover_window->window))
3854 if ( sheet->motion_timer > 0 )
3855 g_source_remove (sheet->motion_timer);
3856 sheet->motion_timer =
3857 g_timeout_add (TIMEOUT_HOVER, motion_timeout_callback, sheet);
3863 gtk_widget_get_pointer (widget, &wx, &wy);
3865 if ( psppire_sheet_get_pixel_info (sheet, wx, wy, &row, &column) )
3867 if ( row != sheet->hover_window->row ||
3868 column != sheet->hover_window->column)
3870 gtk_widget_hide (sheet->hover_window->window);
3875 if (event->window == sheet->column_title_window)
3877 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3878 on_column_boundary (sheet, x, &column))
3880 new_cursor = GDK_SB_H_DOUBLE_ARROW;
3881 if (new_cursor != sheet->cursor_drag->type)
3883 gdk_cursor_unref (sheet->cursor_drag);
3884 sheet->cursor_drag =
3885 gdk_cursor_new_for_display (display, new_cursor);
3887 gdk_window_set_cursor (sheet->column_title_window,
3888 sheet->cursor_drag);
3893 new_cursor = GDK_TOP_LEFT_ARROW;
3894 if (!PSPPIRE_SHEET_IN_XDRAG (sheet) &&
3895 new_cursor != sheet->cursor_drag->type)
3897 gdk_cursor_unref (sheet->cursor_drag);
3898 sheet->cursor_drag =
3899 gdk_cursor_new_for_display (display, new_cursor);
3900 gdk_window_set_cursor (sheet->column_title_window,
3901 sheet->cursor_drag);
3905 else if (event->window == sheet->row_title_window)
3907 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3908 on_row_boundary (sheet, y, &row))
3910 new_cursor = GDK_SB_V_DOUBLE_ARROW;
3911 if (new_cursor != sheet->cursor_drag->type)
3913 gdk_cursor_unref (sheet->cursor_drag);
3914 sheet->cursor_drag =
3915 gdk_cursor_new_for_display (display, new_cursor);
3916 gdk_window_set_cursor (sheet->row_title_window,
3917 sheet->cursor_drag);
3922 new_cursor = GDK_TOP_LEFT_ARROW;
3923 if (!PSPPIRE_SHEET_IN_YDRAG (sheet) &&
3924 new_cursor != sheet->cursor_drag->type)
3926 gdk_cursor_unref (sheet->cursor_drag);
3927 sheet->cursor_drag =
3928 gdk_cursor_new_for_display (display, new_cursor);
3929 gdk_window_set_cursor (sheet->row_title_window,
3930 sheet->cursor_drag);
3935 new_cursor = GDK_PLUS;
3936 if ( event->window == sheet->sheet_window &&
3937 !POSSIBLE_DRAG (sheet, x, y, &row, &column) &&
3938 !PSPPIRE_SHEET_IN_DRAG (sheet) &&
3939 !POSSIBLE_RESIZE (sheet, x, y, &row, &column) &&
3940 !PSPPIRE_SHEET_IN_RESIZE (sheet) &&
3941 new_cursor != sheet->cursor_drag->type)
3943 gdk_cursor_unref (sheet->cursor_drag);
3944 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
3945 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3948 new_cursor = GDK_TOP_LEFT_ARROW;
3949 if ( event->window == sheet->sheet_window &&
3950 ! (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
3951 PSPPIRE_SHEET_IN_RESIZE (sheet)) &&
3952 (POSSIBLE_DRAG (sheet, x, y, &row, &column) ||
3953 PSPPIRE_SHEET_IN_DRAG (sheet)) &&
3954 new_cursor != sheet->cursor_drag->type)
3956 gdk_cursor_unref (sheet->cursor_drag);
3957 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
3958 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3961 new_cursor = GDK_SIZING;
3962 if ( event->window == sheet->sheet_window &&
3963 sheet->selection_mode != GTK_SELECTION_NONE &&
3964 !PSPPIRE_SHEET_IN_DRAG (sheet) &&
3965 (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
3966 PSPPIRE_SHEET_IN_RESIZE (sheet)) &&
3967 new_cursor != sheet->cursor_drag->type)
3969 gdk_cursor_unref (sheet->cursor_drag);
3970 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_SIZING);
3971 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3975 gdk_window_get_pointer (widget->window, &x, &y, &mods);
3976 if (! (mods & GDK_BUTTON1_MASK)) return FALSE;
3978 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3980 if (event->x != sheet->x_drag)
3982 draw_xor_vline (sheet);
3983 sheet->x_drag = event->x;
3984 draw_xor_vline (sheet);
3990 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3992 if (event->y != sheet->y_drag)
3994 draw_xor_hline (sheet);
3995 sheet->y_drag = event->y;
3996 draw_xor_hline (sheet);
4002 if (PSPPIRE_SHEET_IN_DRAG (sheet))
4004 PsppireSheetRange aux;
4005 column = column_from_xpixel (sheet, x)- sheet->drag_cell.col;
4006 row = row_from_ypixel (sheet, y) - sheet->drag_cell.row;
4007 if (sheet->state == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
4008 if (sheet->state == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
4012 if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
4013 aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
4015 aux = sheet->drag_range;
4016 sheet->drag_range.row0 = sheet->range.row0 + row;
4017 sheet->drag_range.col0 = sheet->range.col0 + column;
4018 sheet->drag_range.rowi = sheet->range.rowi + row;
4019 sheet->drag_range.coli = sheet->range.coli + column;
4020 if (aux.row0 != sheet->drag_range.row0 ||
4021 aux.col0 != sheet->drag_range.col0)
4023 draw_xor_rectangle (sheet, aux);
4024 draw_xor_rectangle (sheet, sheet->drag_range);
4030 if (PSPPIRE_SHEET_IN_RESIZE (sheet))
4032 PsppireSheetRange aux;
4033 gint v_h, current_col, current_row, col_threshold, row_threshold;
4035 if (abs (x - psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)) >
4036 abs (y - psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row))) v_h = 2;
4038 current_col = column_from_xpixel (sheet, x);
4039 current_row = row_from_ypixel (sheet, y);
4040 column = current_col - sheet->drag_cell.col;
4041 row = current_row - sheet->drag_cell.row;
4043 /*use half of column width resp. row height as threshold to
4045 col_threshold = psppire_axis_start_pixel (sheet->haxis, current_col) +
4046 psppire_axis_unit_size (sheet->haxis, current_col) / 2;
4049 if (x < col_threshold)
4052 else if (column < 0)
4054 if (x > col_threshold)
4057 row_threshold = psppire_axis_start_pixel (sheet->vaxis, current_row) +
4058 psppire_axis_unit_size (sheet->vaxis, current_row)/2;
4061 if (y < row_threshold)
4066 if (y > row_threshold)
4070 if (sheet->state == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
4071 if (sheet->state == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
4081 if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
4082 aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
4084 aux = sheet->drag_range;
4085 sheet->drag_range = sheet->range;
4087 if (row < 0) sheet->drag_range.row0 = sheet->range.row0 + row;
4088 if (row > 0) sheet->drag_range.rowi = sheet->range.rowi + row;
4089 if (column < 0) sheet->drag_range.col0 = sheet->range.col0 + column;
4090 if (column > 0) sheet->drag_range.coli = sheet->range.coli + column;
4092 if (aux.row0 != sheet->drag_range.row0 ||
4093 aux.rowi != sheet->drag_range.rowi ||
4094 aux.col0 != sheet->drag_range.col0 ||
4095 aux.coli != sheet->drag_range.coli)
4097 draw_xor_rectangle (sheet, aux);
4098 draw_xor_rectangle (sheet, sheet->drag_range);
4104 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
4106 if (sheet->state == PSPPIRE_SHEET_NORMAL && row == sheet->active_cell.row &&
4107 column == sheet->active_cell.col) return TRUE;
4109 if (PSPPIRE_SHEET_IN_SELECTION (sheet) && mods&GDK_BUTTON1_MASK)
4110 psppire_sheet_extend_selection (sheet, row, column);
4116 psppire_sheet_crossing_notify (GtkWidget *widget,
4117 GdkEventCrossing *event)
4119 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
4121 if (event->window == sheet->column_title_window)
4122 sheet->column_title_under = event->type == GDK_ENTER_NOTIFY;
4123 else if (event->window == sheet->row_title_window)
4124 sheet->row_title_under = event->type == GDK_ENTER_NOTIFY;
4131 psppire_sheet_focus_in (GtkWidget *w,
4132 GdkEventFocus *event)
4134 PsppireSheet *sheet = PSPPIRE_SHEET (w);
4136 gtk_widget_grab_focus (sheet->entry_widget);
4143 psppire_sheet_extend_selection (PsppireSheet *sheet, gint row, gint column)
4145 PsppireSheetRange range;
4149 if (row == sheet->selection_cell.row && column == sheet->selection_cell.col)
4152 if (sheet->selection_mode == GTK_SELECTION_SINGLE) return;
4154 gtk_widget_grab_focus (GTK_WIDGET (sheet));
4156 if (PSPPIRE_SHEET_IN_DRAG (sheet)) return;
4158 state = sheet->state;
4160 switch (sheet->state)
4162 case PSPPIRE_SHEET_ROW_SELECTED:
4163 column = psppire_axis_unit_count (sheet->haxis) - 1;
4165 case PSPPIRE_SHEET_COLUMN_SELECTED:
4166 row = psppire_axis_unit_count (sheet->vaxis) - 1;
4168 case PSPPIRE_SHEET_NORMAL:
4169 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
4170 r = sheet->active_cell.row;
4171 c = sheet->active_cell.col;
4172 sheet->range.col0 = c;
4173 sheet->range.row0 = r;
4174 sheet->range.coli = c;
4175 sheet->range.rowi = r;
4176 psppire_sheet_range_draw_selection (sheet, sheet->range);
4177 case PSPPIRE_SHEET_RANGE_SELECTED:
4178 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
4181 sheet->selection_cell.row = row;
4182 sheet->selection_cell.col = column;
4184 range.col0 = MIN (column, sheet->active_cell.col);
4185 range.coli = MAX (column, sheet->active_cell.col);
4186 range.row0 = MIN (row, sheet->active_cell.row);
4187 range.rowi = MAX (row, sheet->active_cell.row);
4189 if (range.row0 != sheet->range.row0 || range.rowi != sheet->range.rowi ||
4190 range.col0 != sheet->range.col0 || range.coli != sheet->range.coli ||
4191 state == PSPPIRE_SHEET_NORMAL)
4192 psppire_sheet_real_select_range (sheet, &range);
4197 psppire_sheet_entry_key_press (GtkWidget *widget,
4201 g_signal_emit_by_name (widget, "key_press_event", key, &focus);
4206 /* Number of rows in a step-increment */
4207 #define ROWS_PER_STEP 1
4211 page_vertical (PsppireSheet *sheet, GtkScrollType dir)
4213 gint old_row = sheet->active_cell.row ;
4214 glong vpixel = psppire_axis_start_pixel (sheet->vaxis, old_row);
4218 vpixel -= psppire_axis_start_pixel (sheet->vaxis,
4219 min_visible_row (sheet));
4223 case GTK_SCROLL_PAGE_DOWN:
4224 gtk_adjustment_set_value (sheet->vadjustment,
4225 sheet->vadjustment->value +
4226 sheet->vadjustment->page_increment);
4228 case GTK_SCROLL_PAGE_UP:
4229 gtk_adjustment_set_value (sheet->vadjustment,
4230 sheet->vadjustment->value -
4231 sheet->vadjustment->page_increment);
4235 g_assert_not_reached ();
4240 vpixel += psppire_axis_start_pixel (sheet->vaxis,
4241 min_visible_row (sheet));
4243 new_row = row_from_ypixel (sheet, vpixel);
4245 change_active_cell (sheet, new_row,
4246 sheet->active_cell.col);
4251 step_sheet (PsppireSheet *sheet, GtkScrollType dir)
4253 gint current_row = sheet->active_cell.row;
4254 gint current_col = sheet->active_cell.col;
4255 PsppireSheetCell new_cell ;
4256 gboolean forbidden = FALSE;
4258 new_cell.row = current_row;
4259 new_cell.col = current_col;
4263 case GTK_SCROLL_STEP_DOWN:
4266 case GTK_SCROLL_STEP_UP:
4269 case GTK_SCROLL_STEP_RIGHT:
4272 case GTK_SCROLL_STEP_LEFT:
4275 case GTK_SCROLL_STEP_FORWARD:
4278 psppire_sheet_model_get_column_count (sheet->model))
4284 case GTK_SCROLL_STEP_BACKWARD:
4286 if (new_cell.col < 0)
4289 psppire_sheet_model_get_column_count (sheet->model) - 1;
4294 g_assert_not_reached ();
4298 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
4299 &sheet->active_cell,
4307 maximize_int (&new_cell.row, 0);
4308 maximize_int (&new_cell.col, 0);
4310 minimize_int (&new_cell.row,
4311 psppire_axis_unit_count (sheet->vaxis) - 1);
4313 minimize_int (&new_cell.col,
4314 psppire_axis_unit_count (sheet->haxis) - 1);
4316 change_active_cell (sheet, new_cell.row, new_cell.col);
4319 if ( new_cell.col > max_fully_visible_column (sheet))
4322 psppire_axis_start_pixel (sheet->haxis,
4324 hpos -= sheet->hadjustment->page_size;
4326 gtk_adjustment_set_value (sheet->hadjustment,
4329 else if ( new_cell.col < min_fully_visible_column (sheet))
4332 psppire_axis_start_pixel (sheet->haxis,
4335 gtk_adjustment_set_value (sheet->hadjustment,
4340 if ( new_cell.row > max_fully_visible_row (sheet))
4343 psppire_axis_start_pixel (sheet->vaxis,
4345 vpos -= sheet->vadjustment->page_size;
4347 gtk_adjustment_set_value (sheet->vadjustment,
4350 else if ( new_cell.row < min_fully_visible_row (sheet))
4353 psppire_axis_start_pixel (sheet->vaxis,
4356 gtk_adjustment_set_value (sheet->vadjustment,
4360 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
4365 psppire_sheet_key_press (GtkWidget *widget,
4368 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
4370 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
4372 switch (key->keyval)
4375 step_sheet (sheet, GTK_SCROLL_STEP_FORWARD);
4378 step_sheet (sheet, GTK_SCROLL_STEP_RIGHT);
4380 case GDK_ISO_Left_Tab:
4381 step_sheet (sheet, GTK_SCROLL_STEP_BACKWARD);
4384 step_sheet (sheet, GTK_SCROLL_STEP_LEFT);
4388 step_sheet (sheet, GTK_SCROLL_STEP_DOWN);
4391 step_sheet (sheet, GTK_SCROLL_STEP_UP);
4395 page_vertical (sheet, GTK_SCROLL_PAGE_DOWN);
4398 page_vertical (sheet, GTK_SCROLL_PAGE_UP);
4402 gtk_adjustment_set_value (sheet->vadjustment,
4403 sheet->vadjustment->lower);
4405 change_active_cell (sheet, 0,
4406 sheet->active_cell.col);
4411 gtk_adjustment_set_value (sheet->vadjustment,
4412 sheet->vadjustment->upper -
4413 sheet->vadjustment->page_size -
4414 sheet->vadjustment->page_increment);
4417 change_active_cellx (sheet,
4418 psppire_axis_unit_count (sheet->vaxis) - 1,
4419 sheet->active_cell.col);
4423 psppire_sheet_real_cell_clear (sheet, sheet->active_cell.row, sheet->active_cell.col);
4434 psppire_sheet_size_request (GtkWidget *widget,
4435 GtkRequisition *requisition)
4437 PsppireSheet *sheet;
4439 g_return_if_fail (widget != NULL);
4440 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
4441 g_return_if_fail (requisition != NULL);
4443 sheet = PSPPIRE_SHEET (widget);
4445 requisition->width = 3 * DEFAULT_COLUMN_WIDTH;
4446 requisition->height = 3 * DEFAULT_ROW_HEIGHT;
4448 /* compute the size of the column title area */
4449 if (sheet->column_titles_visible)
4450 requisition->height += sheet->column_title_area.height;
4452 /* compute the size of the row title area */
4453 if (sheet->row_titles_visible)
4454 requisition->width += sheet->row_title_area.width;
4459 psppire_sheet_size_allocate (GtkWidget *widget,
4460 GtkAllocation *allocation)
4462 PsppireSheet *sheet;
4463 GtkAllocation sheet_allocation;
4466 g_return_if_fail (widget != NULL);
4467 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
4468 g_return_if_fail (allocation != NULL);
4470 sheet = PSPPIRE_SHEET (widget);
4471 widget->allocation = *allocation;
4472 border_width = GTK_CONTAINER (widget)->border_width;
4474 if (GTK_WIDGET_REALIZED (widget))
4475 gdk_window_move_resize (widget->window,
4476 allocation->x + border_width,
4477 allocation->y + border_width,
4478 allocation->width - 2 * border_width,
4479 allocation->height - 2 * border_width);
4481 sheet_allocation.x = 0;
4482 sheet_allocation.y = 0;
4483 sheet_allocation.width = allocation->width - 2 * border_width;
4484 sheet_allocation.height = allocation->height - 2 * border_width;
4486 if (GTK_WIDGET_REALIZED (widget))
4487 gdk_window_move_resize (sheet->sheet_window,
4490 sheet_allocation.width,
4491 sheet_allocation.height);
4493 /* position the window which holds the column title buttons */
4494 sheet->column_title_area.x = 0;
4495 sheet->column_title_area.y = 0;
4496 sheet->column_title_area.width = sheet_allocation.width ;
4499 /* position the window which holds the row title buttons */
4500 sheet->row_title_area.x = 0;
4501 sheet->row_title_area.y = 0;
4502 sheet->row_title_area.height = sheet_allocation.height;
4504 if (sheet->row_titles_visible)
4505 sheet->column_title_area.x += sheet->row_title_area.width;
4507 if (sheet->column_titles_visible)
4508 sheet->row_title_area.y += sheet->column_title_area.height;
4510 if (GTK_WIDGET_REALIZED (widget) && sheet->column_titles_visible)
4511 gdk_window_move_resize (sheet->column_title_window,
4512 sheet->column_title_area.x,
4513 sheet->column_title_area.y,
4514 sheet->column_title_area.width,
4515 sheet->column_title_area.height);
4518 if (GTK_WIDGET_REALIZED (widget) && sheet->row_titles_visible)
4519 gdk_window_move_resize (sheet->row_title_window,
4520 sheet->row_title_area.x,
4521 sheet->row_title_area.y,
4522 sheet->row_title_area.width,
4523 sheet->row_title_area.height);
4525 size_allocate_global_button (sheet);
4529 gint width = sheet->column_title_area.width;
4531 if ( sheet->row_titles_visible)
4532 width -= sheet->row_title_area.width;
4534 g_object_set (sheet->haxis,
4535 "minimum-extent", width,
4542 gint height = sheet->row_title_area.height;
4544 if ( sheet->column_titles_visible)
4545 height -= sheet->column_title_area.height;
4547 g_object_set (sheet->vaxis,
4548 "minimum-extent", height,
4553 /* set the scrollbars adjustments */
4554 adjust_scrollbars (sheet);
4558 draw_column_title_buttons (PsppireSheet *sheet)
4562 if (!sheet->column_titles_visible) return;
4563 if (!GTK_WIDGET_REALIZED (sheet))
4566 gdk_drawable_get_size (sheet->sheet_window, &width, NULL);
4569 if (sheet->row_titles_visible)
4571 x = sheet->row_title_area.width;
4574 if (sheet->column_title_area.width != width || sheet->column_title_area.x != x)
4576 sheet->column_title_area.width = width;
4577 sheet->column_title_area.x = x;
4578 gdk_window_move_resize (sheet->column_title_window,
4579 sheet->column_title_area.x,
4580 sheet->column_title_area.y,
4581 sheet->column_title_area.width,
4582 sheet->column_title_area.height);
4585 if (max_visible_column (sheet) ==
4586 psppire_axis_unit_count (sheet->haxis) - 1)
4587 gdk_window_clear_area (sheet->column_title_window,
4589 sheet->column_title_area.width,
4590 sheet->column_title_area.height);
4592 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
4594 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
4595 max_visible_column (sheet));
4599 draw_row_title_buttons (PsppireSheet *sheet)
4604 if (!sheet->row_titles_visible) return;
4605 if (!GTK_WIDGET_REALIZED (sheet))
4608 gdk_drawable_get_size (sheet->sheet_window, NULL, &height);
4610 if (sheet->column_titles_visible)
4612 y = sheet->column_title_area.height;
4615 if (sheet->row_title_area.height != height || sheet->row_title_area.y != y)
4617 sheet->row_title_area.y = y;
4618 sheet->row_title_area.height = height;
4619 gdk_window_move_resize (sheet->row_title_window,
4620 sheet->row_title_area.x,
4621 sheet->row_title_area.y,
4622 sheet->row_title_area.width,
4623 sheet->row_title_area.height);
4626 if (max_visible_row (sheet) == psppire_axis_unit_count (sheet->vaxis) - 1)
4627 gdk_window_clear_area (sheet->row_title_window,
4629 sheet->row_title_area.width,
4630 sheet->row_title_area.height);
4632 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
4634 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
4635 max_visible_row (sheet));
4640 psppire_sheet_size_allocate_entry (PsppireSheet *sheet)
4642 GtkAllocation entry_alloc;
4643 PsppireSheetCellAttr attributes = { 0 };
4644 GtkEntry *sheet_entry;
4646 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4647 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
4649 sheet_entry = psppire_sheet_get_entry (sheet);
4651 if ( ! psppire_sheet_get_attributes (sheet, sheet->active_cell.row,
4652 sheet->active_cell.col,
4656 if ( GTK_WIDGET_REALIZED (sheet->entry_widget) )
4658 GtkStyle *style = GTK_WIDGET (sheet_entry)->style;
4660 style->bg[GTK_STATE_NORMAL] = attributes.background;
4661 style->fg[GTK_STATE_NORMAL] = attributes.foreground;
4662 style->text[GTK_STATE_NORMAL] = attributes.foreground;
4663 style->bg[GTK_STATE_ACTIVE] = attributes.background;
4664 style->fg[GTK_STATE_ACTIVE] = attributes.foreground;
4665 style->text[GTK_STATE_ACTIVE] = attributes.foreground;
4668 rectangle_from_cell (sheet, sheet->active_cell.row,
4669 sheet->active_cell.col, &entry_alloc);
4671 entry_alloc.width -= BORDER_WIDTH ;
4672 entry_alloc.height -= BORDER_WIDTH ;
4673 entry_alloc.x += DIV_RND_UP (BORDER_WIDTH, 2);
4674 entry_alloc.y += DIV_RND_UP (BORDER_WIDTH, 2);
4677 gtk_widget_set_size_request (sheet->entry_widget, entry_alloc.width,
4678 entry_alloc.height);
4679 gtk_widget_size_allocate (sheet->entry_widget, &entry_alloc);
4683 /* Copy the sheet's font to the entry widget */
4685 set_entry_widget_font (PsppireSheet *sheet)
4687 GtkRcStyle *style = gtk_widget_get_modifier_style (sheet->entry_widget);
4689 pango_font_description_free (style->font_desc);
4690 style->font_desc = pango_font_description_copy (GTK_WIDGET (sheet)->style->font_desc);
4692 gtk_widget_modify_style (sheet->entry_widget, style);
4696 create_sheet_entry (PsppireSheet *sheet)
4698 if (sheet->entry_widget)
4700 gtk_widget_unparent (sheet->entry_widget);
4703 sheet->entry_widget = g_object_new (sheet->entry_type, NULL);
4704 g_object_ref_sink (sheet->entry_widget);
4706 gtk_widget_size_request (sheet->entry_widget, NULL);
4708 if ( GTK_IS_ENTRY (sheet->entry_widget))
4710 g_object_set (sheet->entry_widget,
4715 if (GTK_WIDGET_REALIZED (sheet))
4717 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
4718 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
4719 gtk_widget_realize (sheet->entry_widget);
4722 g_signal_connect_swapped (sheet->entry_widget, "key_press_event",
4723 G_CALLBACK (psppire_sheet_entry_key_press),
4726 set_entry_widget_font (sheet);
4728 gtk_widget_show (sheet->entry_widget);
4732 /* Finds the last child widget that happens to be of type GtkEntry */
4734 find_entry (GtkWidget *w, gpointer user_data)
4736 GtkWidget **entry = user_data;
4737 if ( GTK_IS_ENTRY (w))
4745 psppire_sheet_get_entry (PsppireSheet *sheet)
4747 GtkWidget *w = sheet->entry_widget;
4749 g_return_val_if_fail (sheet != NULL, NULL);
4750 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
4751 g_return_val_if_fail (sheet->entry_widget != NULL, NULL);
4753 while (! GTK_IS_ENTRY (w))
4755 GtkWidget *entry = NULL;
4757 if (GTK_IS_CONTAINER (w))
4759 gtk_container_forall (GTK_CONTAINER (w), find_entry, &entry);
4768 return GTK_ENTRY (w);
4773 draw_button (PsppireSheet *sheet, GdkWindow *window,
4774 PsppireSheetButton *button, gboolean is_sensitive,
4775 GdkRectangle allocation)
4777 GtkShadowType shadow_type;
4778 gint text_width = 0, text_height = 0;
4779 PangoAlignment align = PANGO_ALIGN_LEFT;
4785 g_return_if_fail (sheet != NULL);
4786 g_return_if_fail (button != NULL);
4789 rtl = gtk_widget_get_direction (GTK_WIDGET (sheet)) == GTK_TEXT_DIR_RTL;
4791 gdk_window_clear_area (window,
4792 allocation.x, allocation.y,
4793 allocation.width, allocation.height);
4795 gtk_widget_ensure_style (sheet->button);
4797 gtk_paint_box (sheet->button->style, window,
4798 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
4799 &allocation, GTK_WIDGET (sheet->button),
4801 allocation.x, allocation.y,
4802 allocation.width, allocation.height);
4804 state = button->state;
4805 if (!is_sensitive) state = GTK_STATE_INSENSITIVE;
4807 if (state == GTK_STATE_ACTIVE)
4808 shadow_type = GTK_SHADOW_IN;
4810 shadow_type = GTK_SHADOW_OUT;
4812 if (state != GTK_STATE_NORMAL && state != GTK_STATE_INSENSITIVE)
4813 gtk_paint_box (sheet->button->style, window,
4814 button->state, shadow_type,
4815 &allocation, GTK_WIDGET (sheet->button),
4817 allocation.x, allocation.y,
4818 allocation.width, allocation.height);
4820 if ( button->overstruck)
4822 GdkPoint points[2] = {
4823 {allocation.x, allocation.y},
4824 {allocation.x + allocation.width,
4825 allocation.y + allocation.height}
4828 gtk_paint_polygon (sheet->button->style,
4840 if (button->label_visible)
4842 text_height = DEFAULT_ROW_HEIGHT -
4843 2 * COLUMN_TITLES_HEIGHT;
4845 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4847 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc,
4850 allocation.y += 2 * sheet->button->style->ythickness;
4852 if (button->label && strlen (button->label) > 0)
4854 PangoRectangle rect;
4855 gchar *line = button->label;
4857 PangoLayout *layout = NULL;
4858 gint real_x = allocation.x;
4859 gint real_y = allocation.y;
4861 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), line);
4862 pango_layout_get_extents (layout, NULL, &rect);
4864 text_width = PANGO_PIXELS (rect.width);
4865 switch (button->justification)
4867 case GTK_JUSTIFY_LEFT:
4868 real_x = allocation.x + COLUMN_TITLES_HEIGHT;
4869 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4871 case GTK_JUSTIFY_RIGHT:
4872 real_x = allocation.x + allocation.width - text_width - COLUMN_TITLES_HEIGHT;
4873 align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
4875 case GTK_JUSTIFY_CENTER:
4877 real_x = allocation.x + (allocation.width - text_width)/2;
4878 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4879 pango_layout_set_justify (layout, TRUE);
4881 pango_layout_set_alignment (layout, align);
4882 gtk_paint_layout (GTK_WIDGET (sheet)->style,
4891 g_object_unref (layout);
4894 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4896 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc, NULL);
4900 psppire_sheet_button_free (button);
4904 /* Draw the column title buttons FIRST through to LAST */
4906 draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4910 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4912 if (!sheet->column_titles_visible) return;
4914 g_return_if_fail (first >= min_visible_column (sheet));
4915 g_return_if_fail (last <= max_visible_column (sheet));
4918 rect.height = sheet->column_title_area.height;
4919 rect.x = psppire_axis_start_pixel (sheet->haxis, first) + CELL_SPACING;
4920 rect.width = psppire_axis_start_pixel (sheet->haxis, last) + CELL_SPACING
4921 + psppire_axis_unit_size (sheet->haxis, last);
4923 rect.x -= sheet->hadjustment->value;
4925 minimize_int (&rect.width, sheet->column_title_area.width);
4926 maximize_int (&rect.x, 0);
4928 gdk_window_begin_paint_rect (sheet->column_title_window, &rect);
4930 for (col = first ; col <= last ; ++col)
4932 GdkRectangle allocation;
4933 gboolean is_sensitive = FALSE;
4935 PsppireSheetButton *
4936 button = psppire_sheet_model_get_column_button (sheet->model, col);
4938 allocation.x = psppire_axis_start_pixel (sheet->haxis, col)
4940 allocation.x -= sheet->hadjustment->value;
4942 allocation.height = sheet->column_title_area.height;
4943 allocation.width = psppire_axis_unit_size (sheet->haxis, col);
4944 is_sensitive = psppire_sheet_model_get_column_sensitivity (sheet->model, col);
4946 draw_button (sheet, sheet->column_title_window,
4947 button, is_sensitive, allocation);
4950 gdk_window_end_paint (sheet->column_title_window);
4955 draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4959 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4961 if (!sheet->row_titles_visible) return;
4963 g_return_if_fail (first >= min_visible_row (sheet));
4964 g_return_if_fail (last <= max_visible_row (sheet));
4967 rect.width = sheet->row_title_area.width;
4968 rect.y = psppire_axis_start_pixel (sheet->vaxis, first) + CELL_SPACING;
4969 rect.height = psppire_axis_start_pixel (sheet->vaxis, last) + CELL_SPACING
4970 + psppire_axis_unit_size (sheet->vaxis, last);
4972 rect.y -= sheet->vadjustment->value;
4974 minimize_int (&rect.height, sheet->row_title_area.height);
4975 maximize_int (&rect.y, 0);
4977 gdk_window_begin_paint_rect (sheet->row_title_window, &rect);
4978 for (row = first; row <= last; ++row)
4980 GdkRectangle allocation;
4982 gboolean is_sensitive = FALSE;
4984 PsppireSheetButton *button =
4985 psppire_sheet_model_get_row_button (sheet->model, row);
4987 allocation.y = psppire_axis_start_pixel (sheet->vaxis, row)
4989 allocation.y -= sheet->vadjustment->value;
4991 allocation.width = sheet->row_title_area.width;
4992 allocation.height = psppire_axis_unit_size (sheet->vaxis, row);
4993 is_sensitive = psppire_sheet_model_get_row_sensitivity (sheet->model, row);
4995 draw_button (sheet, sheet->row_title_window,
4996 button, is_sensitive, allocation);
4999 gdk_window_end_paint (sheet->row_title_window);
5006 * vadjustment_value_changed
5007 * hadjustment_value_changed */
5011 update_adjustment (GtkAdjustment *adj, PsppireAxis *axis, gint page_size)
5014 (adj->value + adj->page_size)
5016 (adj->upper - adj->lower);
5018 const glong last_item = psppire_axis_unit_count (axis) - 1;
5020 if (isnan (position) || position < 0)
5024 psppire_axis_start_pixel (axis, last_item)
5026 psppire_axis_unit_size (axis, last_item)
5030 adj->page_size = page_size;
5033 adj->value = position * (adj->upper - adj->lower) - adj->page_size;
5035 if ( adj->value < adj->lower)
5036 adj->value = adj->lower;
5039 gtk_adjustment_changed (adj);
5044 adjust_scrollbars (PsppireSheet *sheet)
5048 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
5051 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
5053 if ( sheet->row_titles_visible)
5054 width -= sheet->row_title_area.width;
5056 if (sheet->column_titles_visible)
5057 height -= sheet->column_title_area.height;
5059 if (sheet->vadjustment)
5061 glong last_row = psppire_axis_unit_count (sheet->vaxis) - 1;
5063 sheet->vadjustment->step_increment =
5065 psppire_axis_unit_size (sheet->vaxis, last_row);
5067 sheet->vadjustment->page_increment =
5069 sheet->column_title_area.height -
5070 psppire_axis_unit_size (sheet->vaxis, last_row);
5072 update_adjustment (sheet->vadjustment, sheet->vaxis, height);
5075 if (sheet->hadjustment)
5077 gint last_col = psppire_axis_unit_count (sheet->haxis) - 1;
5078 sheet->hadjustment->step_increment = 1;
5080 sheet->hadjustment->page_increment = width;
5082 sheet->hadjustment->upper =
5083 psppire_axis_start_pixel (sheet->haxis, last_col)
5085 psppire_axis_unit_size (sheet->haxis, last_col)
5088 update_adjustment (sheet->hadjustment, sheet->haxis, width);
5092 /* Subtracts the region of WIDGET from REGION */
5094 subtract_widget_region (GdkRegion *region, GtkWidget *widget)
5097 GdkRectangle intersect;
5100 gdk_region_get_clipbox (region, &rect);
5101 gtk_widget_intersect (widget,
5105 region2 = gdk_region_rectangle (&intersect);
5106 gdk_region_subtract (region, region2);
5107 gdk_region_destroy (region2);
5111 vadjustment_value_changed (GtkAdjustment *adjustment,
5115 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5117 g_return_if_fail (adjustment != NULL);
5119 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
5121 gtk_widget_hide (sheet->entry_widget);
5124 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
5126 subtract_widget_region (region, sheet->button);
5127 gdk_window_begin_paint_region (sheet->sheet_window, region);
5129 draw_sheet_region (sheet, region);
5131 draw_row_title_buttons (sheet);
5132 psppire_sheet_draw_active_cell (sheet);
5134 gdk_window_end_paint (sheet->sheet_window);
5135 gdk_region_destroy (region);
5140 hadjustment_value_changed (GtkAdjustment *adjustment,
5144 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5146 g_return_if_fail (adjustment != NULL);
5148 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
5150 gtk_widget_hide (sheet->entry_widget);
5154 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
5156 subtract_widget_region (region, sheet->button);
5157 gdk_window_begin_paint_region (sheet->sheet_window, region);
5159 draw_sheet_region (sheet, region);
5161 draw_column_title_buttons (sheet);
5163 psppire_sheet_draw_active_cell (sheet);
5165 gdk_window_end_paint (sheet->sheet_window);
5167 gdk_region_destroy (region);
5171 /* COLUMN RESIZING */
5173 draw_xor_vline (PsppireSheet *sheet)
5176 gint xpos = sheet->x_drag;
5177 gdk_drawable_get_size (sheet->sheet_window,
5180 if (sheet->row_titles_visible)
5181 xpos += sheet->row_title_area.width;
5183 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
5185 sheet->column_title_area.height,
5187 height + CELL_SPACING);
5192 draw_xor_hline (PsppireSheet *sheet)
5196 gint ypos = sheet->y_drag;
5198 gdk_drawable_get_size (sheet->sheet_window,
5202 if (sheet->column_titles_visible)
5203 ypos += sheet->column_title_area.height;
5205 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
5206 sheet->row_title_area.width,
5208 width + CELL_SPACING,
5212 /* SELECTED RANGE */
5214 draw_xor_rectangle (PsppireSheet *sheet, PsppireSheetRange range)
5217 GdkRectangle clip_area, area;
5220 area.x = psppire_axis_start_pixel (sheet->haxis, range.col0);
5221 area.y = psppire_axis_start_pixel (sheet->vaxis, range.row0);
5222 area.width = psppire_axis_start_pixel (sheet->haxis, range.coli)- area.x+
5223 psppire_axis_unit_size (sheet->haxis, range.coli);
5224 area.height = psppire_axis_start_pixel (sheet->vaxis, range.rowi)- area.y +
5225 psppire_axis_unit_size (sheet->vaxis, range.rowi);
5227 clip_area.x = sheet->row_title_area.width;
5228 clip_area.y = sheet->column_title_area.height;
5230 gdk_drawable_get_size (sheet->sheet_window,
5231 &clip_area.width, &clip_area.height);
5233 if (!sheet->row_titles_visible) clip_area.x = 0;
5234 if (!sheet->column_titles_visible) clip_area.y = 0;
5238 area.width = area.width + area.x;
5241 if (area.width > clip_area.width) area.width = clip_area.width + 10;
5244 area.height = area.height + area.y;
5247 if (area.height > clip_area.height) area.height = clip_area.height + 10;
5251 clip_area.width += 3;
5252 clip_area.height += 3;
5254 gdk_gc_get_values (sheet->xor_gc, &values);
5256 gdk_gc_set_clip_rectangle (sheet->xor_gc, &clip_area);
5258 gdk_draw_rectangle (sheet->sheet_window,
5261 area.x + i, area.y + i,
5262 area.width - 2 * i, area.height - 2 * i);
5265 gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
5267 gdk_gc_set_foreground (sheet->xor_gc, &values.foreground);
5272 set_column_width (PsppireSheet *sheet,
5276 g_return_if_fail (sheet != NULL);
5277 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
5279 if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis))
5285 psppire_axis_resize (sheet->haxis, column,
5286 width - sheet->cell_padding->left -
5287 sheet->cell_padding->right);
5289 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
5291 draw_column_title_buttons (sheet);
5292 adjust_scrollbars (sheet);
5293 psppire_sheet_size_allocate_entry (sheet);
5294 redraw_range (sheet, NULL);
5299 set_row_height (PsppireSheet *sheet,
5303 g_return_if_fail (sheet != NULL);
5304 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
5306 if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis))
5312 psppire_axis_resize (sheet->vaxis, row,
5313 height - sheet->cell_padding->top -
5314 sheet->cell_padding->bottom);
5316 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) )
5318 draw_row_title_buttons (sheet);
5319 adjust_scrollbars (sheet);
5320 psppire_sheet_size_allocate_entry (sheet);
5321 redraw_range (sheet, NULL);
5326 psppire_sheet_get_attributes (const PsppireSheet *sheet, gint row, gint col,
5327 PsppireSheetCellAttr *attr)
5330 const GtkJustification *j ;
5331 GdkColormap *colormap;
5333 g_return_val_if_fail (sheet != NULL, FALSE);
5334 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), FALSE);
5336 if (row < 0 || col < 0) return FALSE;
5338 attr->foreground = GTK_WIDGET (sheet)->style->black;
5339 attr->background = sheet->color[BG_COLOR];
5341 attr->border.width = 0;
5342 attr->border.line_style = GDK_LINE_SOLID;
5343 attr->border.cap_style = GDK_CAP_NOT_LAST;
5344 attr->border.join_style = GDK_JOIN_MITER;
5345 attr->border.mask = 0;
5346 attr->border.color = GTK_WIDGET (sheet)->style->black;
5348 colormap = gtk_widget_get_colormap (GTK_WIDGET (sheet));
5349 fg = psppire_sheet_model_get_foreground (sheet->model, row, col);
5352 gdk_colormap_alloc_color (colormap, fg, TRUE, TRUE);
5353 attr->foreground = *fg;
5356 bg = psppire_sheet_model_get_background (sheet->model, row, col);
5359 gdk_colormap_alloc_color (colormap, bg, TRUE, TRUE);
5360 attr->background = *bg;
5363 attr->justification =
5364 psppire_sheet_model_get_column_justification (sheet->model, col);
5366 j = psppire_sheet_model_get_justification (sheet->model, row, col);
5368 attr->justification = *j;
5374 psppire_sheet_button_size_request (PsppireSheet *sheet,
5375 const PsppireSheetButton *button,
5376 GtkRequisition *button_requisition)
5378 GtkRequisition requisition;
5379 GtkRequisition label_requisition;
5381 label_requisition.height = DEFAULT_ROW_HEIGHT;
5382 label_requisition.width = COLUMN_MIN_WIDTH;
5384 requisition.height = DEFAULT_ROW_HEIGHT;
5385 requisition.width = COLUMN_MIN_WIDTH;
5388 *button_requisition = requisition;
5389 button_requisition->width = MAX (requisition.width, label_requisition.width);
5390 button_requisition->height = MAX (requisition.height, label_requisition.height);
5395 psppire_sheet_forall (GtkContainer *container,
5396 gboolean include_internals,
5397 GtkCallback callback,
5398 gpointer callback_data)
5400 PsppireSheet *sheet = PSPPIRE_SHEET (container);
5402 g_return_if_fail (callback != NULL);
5404 if (sheet->button && sheet->button->parent)
5405 (* callback) (sheet->button, callback_data);
5407 if (sheet->entry_widget && GTK_IS_CONTAINER (sheet->entry_widget))
5408 (* callback) (sheet->entry_widget, callback_data);
5413 psppire_sheet_get_model (const PsppireSheet *sheet)
5415 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
5417 return sheet->model;
5421 PsppireSheetButton *
5422 psppire_sheet_button_new (void)
5424 PsppireSheetButton *button = g_malloc (sizeof (PsppireSheetButton));
5426 button->state = GTK_STATE_NORMAL;
5427 button->label = NULL;
5428 button->label_visible = TRUE;
5429 button->justification = GTK_JUSTIFY_FILL;
5430 button->overstruck = FALSE;
5437 psppire_sheet_button_free (PsppireSheetButton *button)
5439 if (!button) return ;
5441 g_free (button->label);
5446 append_cell_text (GString *string, const PsppireSheet *sheet, gint r, gint c)
5448 gchar *celltext = psppire_sheet_cell_get_text (sheet, r, c);
5450 if ( NULL == celltext)
5453 g_string_append (string, celltext);
5459 range_to_text (const PsppireSheet *sheet)
5464 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
5467 string = g_string_sized_new (80);
5469 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
5471 for (c = sheet->range.col0; c < sheet->range.coli; ++c)
5473 append_cell_text (string, sheet, r, c);
5474 g_string_append (string, "\t");
5476 append_cell_text (string, sheet, r, c);
5477 if ( r < sheet->range.rowi)
5478 g_string_append (string, "\n");
5485 range_to_html (const PsppireSheet *sheet)
5490 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
5493 string = g_string_sized_new (480);
5495 g_string_append (string, "<html>\n");
5496 g_string_append (string, "<body>\n");
5497 g_string_append (string, "<table>\n");
5498 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
5500 g_string_append (string, "<tr>\n");
5501 for (c = sheet->range.col0; c <= sheet->range.coli; ++c)
5503 g_string_append (string, "<td>");
5504 append_cell_text (string, sheet, r, c);
5505 g_string_append (string, "</td>\n");
5507 g_string_append (string, "</tr>\n");
5509 g_string_append (string, "</table>\n");
5510 g_string_append (string, "</body>\n");
5511 g_string_append (string, "</html>\n");
5523 primary_get_cb (GtkClipboard *clipboard,
5524 GtkSelectionData *selection_data,
5528 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5529 GString *string = NULL;
5533 case SELECT_FMT_TEXT:
5534 string = range_to_text (sheet);
5536 case SELECT_FMT_HTML:
5537 string = range_to_html (sheet);
5540 g_assert_not_reached ();
5543 gtk_selection_data_set (selection_data, selection_data->target,
5545 (const guchar *) string->str, string->len);
5546 g_string_free (string, TRUE);
5550 primary_clear_cb (GtkClipboard *clipboard,
5553 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5554 if ( ! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
5557 psppire_sheet_real_unselect_range (sheet, NULL);
5561 psppire_sheet_update_primary_selection (PsppireSheet *sheet)
5563 static const GtkTargetEntry targets[] = {
5564 { "UTF8_STRING", 0, SELECT_FMT_TEXT },
5565 { "STRING", 0, SELECT_FMT_TEXT },
5566 { "TEXT", 0, SELECT_FMT_TEXT },
5567 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
5568 { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
5569 { "text/plain", 0, SELECT_FMT_TEXT },
5570 { "text/html", 0, SELECT_FMT_HTML }
5573 GtkClipboard *clipboard;
5575 if (!GTK_WIDGET_REALIZED (sheet))
5578 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (sheet),
5579 GDK_SELECTION_PRIMARY);
5581 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
5583 if (!gtk_clipboard_set_with_owner (clipboard, targets,
5584 G_N_ELEMENTS (targets),
5585 primary_get_cb, primary_clear_cb,
5587 primary_clear_cb (clipboard, sheet);
5591 if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (sheet))
5592 gtk_clipboard_clear (clipboard);