2 Copyright (C) 2006, 2008, 2009 Free Software Foundation
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 This file is derived from the gtksheet.c and extensively modified for the
19 requirements of PSPPIRE. The changes are copyright by the
20 Free Software Foundation. The copyright notice for the original work is
24 /* GtkSheet widget for Gtk+.
25 * Copyright (C) 1999-2001 Adrian E. Feiguin <adrian@ifir.ifir.edu.ar>
27 * Based on GtkClist widget by Jay Painter, but major changes.
28 * Memory allocation routines inspired on SC (Spreadsheet Calculator)
30 * This library is free software; you can redistribute it and/or
31 * modify it under the terms of the GNU Lesser General Public
32 * License as published by the Free Software Foundation; either
33 * version 2.1 of the License, or (at your option) any later version.
35 * This library is distributed in the hope that it will be useful,
36 * but WITHOUT ANY WARRANTY; without even the implied warranty of
37 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
38 * Lesser General Public License for more details.
40 * You should have received a copy of the GNU Lesser General Public
41 * License along with this library; if not, write to the Free Software
42 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
46 * SECTION:psppiresheet
47 * @short_description: spreadsheet widget for gtk2
49 * PsppireSheet is a matrix widget for GTK+. It consists of an scrollable grid of
50 * cells where you can allocate text. Cell contents can be edited interactively
51 * through a specially designed entry, GtkItemEntry.
61 #include <gdk/gdkkeysyms.h>
62 #include <gtk/gtksignal.h>
63 #include <gtk/gtkbutton.h>
64 #include <gtk/gtkadjustment.h>
65 #include <gtk/gtktypeutils.h>
66 #include <gtk/gtkentry.h>
67 #include <gtk/gtkcontainer.h>
68 #include <pango/pango.h>
69 #include "psppire-sheet.h"
70 #include <ui/gui/psppire-marshal.h>
71 #include <ui/gui/sheet/psppire-sheetmodel.h>
72 #include <ui/gui/sheet/psppire-axis.h>
73 #include <libpspp/misc.h>
80 PSPPIRE_SHEET_IN_XDRAG = 1 << 1,
81 PSPPIRE_SHEET_IN_YDRAG = 1 << 2,
82 PSPPIRE_SHEET_IN_DRAG = 1 << 3,
84 /* This flag is set when the user is actually in the process
85 of making a selection - ie while the mouse button is
88 PSPPIRE_SHEET_IN_SELECTION = 1 << 4,
90 PSPPIRE_SHEET_IN_RESIZE = 1 << 5
93 #define PSPPIRE_SHEET_FLAGS(sheet) (PSPPIRE_SHEET (sheet)->flags)
94 #define PSPPIRE_SHEET_SET_FLAGS(sheet,flag) (PSPPIRE_SHEET_FLAGS (sheet) |= (flag))
95 #define PSPPIRE_SHEET_UNSET_FLAGS(sheet,flag) (PSPPIRE_SHEET_FLAGS (sheet) &= ~ (flag))
97 #define PSPPIRE_SHEET_IN_XDRAG(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_XDRAG)
98 #define PSPPIRE_SHEET_IN_YDRAG(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_YDRAG)
99 #define PSPPIRE_SHEET_IN_DRAG(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_DRAG)
100 #define PSPPIRE_SHEET_IN_SELECTION(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_SELECTION)
101 #define PSPPIRE_SHEET_IN_RESIZE(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_RESIZE)
103 #define CELL_SPACING 1
105 #define TIMEOUT_HOVER 300
106 #define COLUMN_MIN_WIDTH 10
107 #define COLUMN_TITLES_HEIGHT 4
108 #define DEFAULT_COLUMN_WIDTH 80
109 #define DEFAULT_ROW_HEIGHT 25
111 static void set_entry_widget_font (PsppireSheet *sheet);
113 static void psppire_sheet_update_primary_selection (PsppireSheet *sheet);
114 static void draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint n);
115 static void draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint n);
116 static void redraw_range (PsppireSheet *sheet, PsppireSheetRange *range);
119 static void set_row_height (PsppireSheet *sheet,
123 static void destroy_hover_window (PsppireSheetHoverTitle *);
124 static PsppireSheetHoverTitle *create_hover_window (void);
127 dispose_string (const PsppireSheet *sheet, gchar *text)
129 PsppireSheetModel *model = psppire_sheet_get_model (sheet);
134 if (psppire_sheet_model_free_strings (model))
139 /* FIXME: Why bother with these two ? */
141 /* returns the column index from a pixel location */
143 column_from_xpixel (const PsppireSheet *sheet, gint pixel)
145 return psppire_axis_unit_at_pixel (sheet->haxis, pixel);
149 row_from_ypixel (const PsppireSheet *sheet, gint pixel)
151 return psppire_axis_unit_at_pixel (sheet->vaxis, pixel);
155 /* Return the lowest row number which is wholly or partially on
156 the visible range of the sheet */
158 min_visible_row (const PsppireSheet *sheet)
160 return row_from_ypixel (sheet, sheet->vadjustment->value);
164 min_fully_visible_row (const PsppireSheet *sheet)
166 glong row = min_visible_row (sheet);
168 if ( psppire_axis_start_pixel (sheet->vaxis, row) < sheet->vadjustment->value)
175 max_visible_row (const PsppireSheet *sheet)
177 return row_from_ypixel (sheet, sheet->vadjustment->value + sheet->vadjustment->page_size);
182 max_fully_visible_row (const PsppireSheet *sheet)
184 glong row = max_visible_row (sheet);
186 if ( psppire_axis_start_pixel (sheet->vaxis, row)
188 psppire_axis_unit_size (sheet->vaxis, row)
189 > sheet->vadjustment->value)
196 /* Returns the lowest column number which is wholly or partially
199 min_visible_column (const PsppireSheet *sheet)
201 return column_from_xpixel (sheet, sheet->hadjustment->value);
205 min_fully_visible_column (const PsppireSheet *sheet)
207 glong col = min_visible_column (sheet);
209 if ( psppire_axis_start_pixel (sheet->haxis, col) < sheet->hadjustment->value)
216 /* Returns the highest column number which is wholly or partially
219 max_visible_column (const PsppireSheet *sheet)
221 return column_from_xpixel (sheet, sheet->hadjustment->value + sheet->hadjustment->page_size);
225 max_fully_visible_column (const PsppireSheet *sheet)
227 glong col = max_visible_column (sheet);
229 if ( psppire_axis_start_pixel (sheet->haxis, col)
231 psppire_axis_unit_size (sheet->haxis, col)
232 > sheet->hadjustment->value)
240 /* The size of the region (in pixels) around the row/column boundaries
241 where the height/width may be grabbed to change size */
245 on_column_boundary (const PsppireSheet *sheet, gint x, gint *column)
250 x += sheet->hadjustment->value;
255 col = column_from_xpixel (sheet, x);
257 pixel = x - DRAG_WIDTH / 2;
261 if ( column_from_xpixel (sheet, pixel) < col )
267 if ( column_from_xpixel (sheet, x + DRAG_WIDTH / 2) > col )
277 on_row_boundary (const PsppireSheet *sheet, gint y, gint *row)
282 y += sheet->vadjustment->value;
287 r = row_from_ypixel (sheet, y);
289 pixel = y - DRAG_WIDTH / 2;
293 if ( row_from_ypixel (sheet, pixel) < r )
299 if ( row_from_ypixel (sheet, y + DRAG_WIDTH / 2) > r )
309 static inline gboolean
310 POSSIBLE_DRAG (const PsppireSheet *sheet, gint x, gint y,
311 gint *drag_row, gint *drag_column)
315 /* Can't drag if nothing is selected */
316 if ( sheet->range.row0 < 0 || sheet->range.rowi < 0 ||
317 sheet->range.col0 < 0 || sheet->range.coli < 0 )
320 *drag_column = column_from_xpixel (sheet, x);
321 *drag_row = row_from_ypixel (sheet, y);
323 if (x >= psppire_axis_start_pixel (sheet->haxis, sheet->range.col0) - DRAG_WIDTH / 2 &&
324 x <= psppire_axis_start_pixel (sheet->haxis, sheet->range.coli) +
325 psppire_axis_unit_size (sheet->haxis, sheet->range.coli) + DRAG_WIDTH / 2)
327 ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.row0);
328 if (y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2)
330 *drag_row = sheet->range.row0;
333 ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) +
334 psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi);
335 if (y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2)
337 *drag_row = sheet->range.rowi;
342 if (y >= psppire_axis_start_pixel (sheet->vaxis, sheet->range.row0) - DRAG_WIDTH / 2 &&
343 y <= psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) +
344 psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi) + DRAG_WIDTH / 2)
346 xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.col0);
347 if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2)
349 *drag_column = sheet->range.col0;
352 xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.coli) +
353 psppire_axis_unit_size (sheet->haxis, sheet->range.coli);
354 if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2)
356 *drag_column = sheet->range.coli;
364 static inline gboolean
365 POSSIBLE_RESIZE (const PsppireSheet *sheet, gint x, gint y,
366 gint *drag_row, gint *drag_column)
370 /* Can't drag if nothing is selected */
371 if ( sheet->range.row0 < 0 || sheet->range.rowi < 0 ||
372 sheet->range.col0 < 0 || sheet->range.coli < 0 )
375 xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.coli)+
376 psppire_axis_unit_size (sheet->haxis, sheet->range.coli);
378 ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) +
379 psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi);
381 if (sheet->select_status == PSPPIRE_SHEET_COLUMN_SELECTED)
382 ydrag = psppire_axis_start_pixel (sheet->vaxis, min_visible_row (sheet));
384 if (sheet->select_status == PSPPIRE_SHEET_ROW_SELECTED)
385 xdrag = psppire_axis_start_pixel (sheet->haxis, min_visible_column (sheet));
387 *drag_column = column_from_xpixel (sheet, x);
388 *drag_row = row_from_ypixel (sheet, y);
390 if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2 &&
391 y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2) return TRUE;
398 rectangle_from_range (PsppireSheet *sheet, const PsppireSheetRange *input,
401 PsppireSheetRange range = *input;
403 if ( range.row0 == -1 ) range.row0 = min_visible_row (sheet);
404 if ( range.rowi == -1 ) range.rowi = max_visible_row (sheet);
405 if ( range.col0 == -1 ) range.col0 = min_visible_column (sheet);
406 if ( range.coli == -1 ) range.coli = max_visible_column (sheet);
408 r->x = psppire_axis_start_pixel (sheet->haxis, range.col0);
409 r->x -= round (sheet->hadjustment->value);
411 r->y = psppire_axis_start_pixel (sheet->vaxis, range.row0);
412 r->y -= round (sheet->vadjustment->value);
414 r->width = psppire_axis_start_pixel (sheet->haxis, range.coli) -
415 psppire_axis_start_pixel (sheet->haxis, range.col0) +
416 psppire_axis_unit_size (sheet->haxis, range.coli);
418 r->height = psppire_axis_start_pixel (sheet->vaxis, range.rowi) -
419 psppire_axis_start_pixel (sheet->vaxis, range.row0) +
420 psppire_axis_unit_size (sheet->vaxis, range.rowi);
422 if ( sheet->column_titles_visible)
424 r->y += sheet->column_title_area.height;
427 if ( sheet->row_titles_visible)
429 r->x += sheet->row_title_area.width;
436 rectangle_from_cell (PsppireSheet *sheet, gint row, gint col,
439 PsppireSheetRange range;
440 g_return_val_if_fail (row >= 0, FALSE);
441 g_return_val_if_fail (col >= 0, FALSE);
443 range.row0 = range.rowi = row;
444 range.col0 = range.coli = col;
446 return rectangle_from_range (sheet, &range, r);
450 static void psppire_sheet_class_init (PsppireSheetClass *klass);
451 static void psppire_sheet_init (PsppireSheet *sheet);
452 static void psppire_sheet_dispose (GObject *object);
453 static void psppire_sheet_finalize (GObject *object);
454 static void psppire_sheet_style_set (GtkWidget *widget,
455 GtkStyle *previous_style);
456 static void psppire_sheet_realize (GtkWidget *widget);
457 static void psppire_sheet_unrealize (GtkWidget *widget);
458 static void psppire_sheet_map (GtkWidget *widget);
459 static void psppire_sheet_unmap (GtkWidget *widget);
460 static gint psppire_sheet_expose (GtkWidget *widget,
461 GdkEventExpose *event);
463 static void psppire_sheet_forall (GtkContainer *container,
464 gboolean include_internals,
465 GtkCallback callback,
466 gpointer callback_data);
468 static gboolean psppire_sheet_set_scroll_adjustments (PsppireSheet *sheet,
469 GtkAdjustment *hadjustment,
470 GtkAdjustment *vadjustment);
472 static gint psppire_sheet_button_press (GtkWidget *widget,
473 GdkEventButton *event);
474 static gint psppire_sheet_button_release (GtkWidget *widget,
475 GdkEventButton *event);
476 static gint psppire_sheet_motion (GtkWidget *widget,
477 GdkEventMotion *event);
478 static gboolean psppire_sheet_crossing_notify (GtkWidget *widget,
479 GdkEventCrossing *event);
480 static gint psppire_sheet_entry_key_press (GtkWidget *widget,
482 static gboolean psppire_sheet_key_press (GtkWidget *widget,
484 static void psppire_sheet_size_request (GtkWidget *widget,
485 GtkRequisition *requisition);
486 static void psppire_sheet_size_allocate (GtkWidget *widget,
487 GtkAllocation *allocation);
489 static gboolean psppire_sheet_focus_in (GtkWidget *widget,
490 GdkEventFocus *event);
494 static gboolean psppire_sheet_range_isvisible (const PsppireSheet *sheet,
495 const PsppireSheetRange *range);
496 static gboolean psppire_sheet_cell_isvisible (PsppireSheet *sheet,
497 gint row, gint column);
498 /* Drawing Routines */
501 static void psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint column);
504 /* draw visible part of range. */
505 static void draw_sheet_region (PsppireSheet *sheet, GdkRegion *region);
509 static void psppire_sheet_draw_border (PsppireSheet *sheet,
510 PsppireSheetRange range);
512 /* Active Cell handling */
514 static void psppire_sheet_hide_entry_widget (PsppireSheet *sheet);
515 static void change_active_cell (PsppireSheet *sheet, gint row, gint col);
516 static gboolean psppire_sheet_draw_active_cell (PsppireSheet *sheet);
517 static void psppire_sheet_show_entry_widget (PsppireSheet *sheet);
518 static gboolean psppire_sheet_click_cell (PsppireSheet *sheet,
525 static void adjust_scrollbars (PsppireSheet *sheet);
526 static void vadjustment_value_changed (GtkAdjustment *adjustment,
528 static void hadjustment_value_changed (GtkAdjustment *adjustment,
532 static void draw_xor_vline (PsppireSheet *sheet);
533 static void draw_xor_hline (PsppireSheet *sheet);
534 static void draw_xor_rectangle (PsppireSheet *sheet,
535 PsppireSheetRange range);
539 static void create_global_button (PsppireSheet *sheet);
540 static void global_button_clicked (GtkWidget *widget,
544 static void create_sheet_entry (PsppireSheet *sheet);
545 static void psppire_sheet_size_allocate_entry (PsppireSheet *sheet);
547 /* Sheet button gadgets */
549 static void draw_column_title_buttons (PsppireSheet *sheet);
550 static void draw_row_title_buttons (PsppireSheet *sheet);
553 static void size_allocate_global_button (PsppireSheet *sheet);
554 static void psppire_sheet_button_size_request (PsppireSheet *sheet,
555 const PsppireSheetButton *button,
556 GtkRequisition *requisition);
558 static void psppire_sheet_real_cell_clear (PsppireSheet *sheet,
580 static GtkContainerClass *parent_class = NULL;
581 static guint sheet_signals[LAST_SIGNAL] = { 0 };
585 psppire_sheet_get_type ()
587 static GType sheet_type = 0;
591 static const GTypeInfo sheet_info =
593 sizeof (PsppireSheetClass),
596 (GClassInitFunc) psppire_sheet_class_init,
599 sizeof (PsppireSheet),
601 (GInstanceInitFunc) psppire_sheet_init,
606 g_type_register_static (GTK_TYPE_BIN, "PsppireSheet",
614 static PsppireSheetRange*
615 psppire_sheet_range_copy (const PsppireSheetRange *range)
617 PsppireSheetRange *new_range;
619 g_return_val_if_fail (range != NULL, NULL);
621 new_range = g_new (PsppireSheetRange, 1);
629 psppire_sheet_range_free (PsppireSheetRange *range)
631 g_return_if_fail (range != NULL);
637 psppire_sheet_range_get_type (void)
639 static GType sheet_range_type = 0;
641 if (!sheet_range_type)
644 g_boxed_type_register_static ("PsppireSheetRange",
645 (GBoxedCopyFunc) psppire_sheet_range_copy,
646 (GBoxedFreeFunc) psppire_sheet_range_free);
649 return sheet_range_type;
652 static PsppireSheetCell*
653 psppire_sheet_cell_copy (const PsppireSheetCell *cell)
655 PsppireSheetCell *new_cell;
657 g_return_val_if_fail (cell != NULL, NULL);
659 new_cell = g_new (PsppireSheetCell, 1);
667 psppire_sheet_cell_free (PsppireSheetCell *cell)
669 g_return_if_fail (cell != NULL);
675 psppire_sheet_cell_get_type (void)
677 static GType sheet_cell_type = 0;
679 if (!sheet_cell_type)
682 g_boxed_type_register_static ("PsppireSheetCell",
683 (GBoxedCopyFunc) psppire_sheet_cell_copy,
684 (GBoxedFreeFunc) psppire_sheet_cell_free);
687 return sheet_cell_type;
702 resize_column (PsppireSheet *sheet, gint unit, glong size)
704 PsppireSheetRange range;
706 range.coli = max_visible_column (sheet);
707 range.row0 = min_visible_row (sheet);
708 range.rowi = max_visible_row (sheet);
710 redraw_range (sheet, &range);
712 draw_column_title_buttons_range (sheet, range.col0, range.coli);
717 psppire_sheet_set_horizontal_axis (PsppireSheet *sheet, PsppireAxis *a)
720 g_object_unref (sheet->haxis);
723 g_signal_connect_swapped (a, "resize-unit", G_CALLBACK (resize_column), sheet);
726 g_object_ref (sheet->haxis);
730 resize_row (PsppireSheet *sheet, gint unit, glong size)
732 PsppireSheetRange range;
733 range.col0 = min_visible_column (sheet);
734 range.coli = max_visible_column (sheet);
736 range.rowi = max_visible_row (sheet);
738 redraw_range (sheet, &range);
740 draw_row_title_buttons_range (sheet, range.row0, range.rowi);
744 psppire_sheet_set_vertical_axis (PsppireSheet *sheet, PsppireAxis *a)
747 g_object_unref (sheet->vaxis);
751 g_signal_connect_swapped (a, "resize-unit", G_CALLBACK (resize_row), sheet);
754 g_object_ref (sheet->vaxis);
757 static const GtkBorder default_cell_padding = {3, 3, 3, 3};
760 psppire_sheet_set_property (GObject *object,
766 PsppireSheet *sheet = PSPPIRE_SHEET (object);
770 case PROP_CELL_PADDING:
771 if ( sheet->cell_padding)
772 g_boxed_free (GTK_TYPE_BORDER, sheet->cell_padding);
774 sheet->cell_padding = g_value_dup_boxed (value);
776 if (NULL == sheet->cell_padding)
777 sheet->cell_padding = g_boxed_copy (GTK_TYPE_BORDER,
778 &default_cell_padding);
781 g_object_set (sheet->vaxis, "padding",
782 sheet->cell_padding->top + sheet->cell_padding->bottom,
786 g_object_set (sheet->haxis, "padding",
787 sheet->cell_padding->left + sheet->cell_padding->right,
791 psppire_sheet_set_vertical_axis (sheet, g_value_get_pointer (value));
792 g_object_set (sheet->vaxis, "padding",
793 sheet->cell_padding->top + sheet->cell_padding->bottom,
797 psppire_sheet_set_horizontal_axis (sheet, g_value_get_pointer (value));
798 g_object_set (sheet->haxis, "padding",
799 sheet->cell_padding->left + sheet->cell_padding->right,
803 psppire_sheet_set_model (sheet, g_value_get_pointer (value));
806 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
812 psppire_sheet_get_property (GObject *object,
817 PsppireSheet *sheet = PSPPIRE_SHEET (object);
821 case PROP_CELL_PADDING:
822 g_value_set_boxed (value, sheet->cell_padding);
825 g_value_set_pointer (value, sheet->vaxis);
828 g_value_set_pointer (value, sheet->haxis);
831 g_value_set_pointer (value, sheet->model);
834 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
841 psppire_sheet_class_init (PsppireSheetClass *klass)
843 GObjectClass *object_class = G_OBJECT_CLASS (klass);
845 GParamSpec *haxis_spec ;
846 GParamSpec *vaxis_spec ;
847 GParamSpec *model_spec ;
848 GParamSpec *cell_padding_spec ;
850 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
851 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
853 parent_class = g_type_class_peek_parent (klass);
856 * PsppireSheet::select-row
857 * @sheet: the sheet widget that emitted the signal
858 * @row: the newly selected row index
860 * A row has been selected.
862 sheet_signals[SELECT_ROW] =
863 g_signal_new ("select-row",
864 G_TYPE_FROM_CLASS (object_class),
866 offsetof (PsppireSheetClass, select_row),
868 g_cclosure_marshal_VOID__INT,
875 * PsppireSheet::select - column
876 * @sheet: the sheet widget that emitted the signal
877 * @column: the newly selected column index
879 * A column has been selected.
881 sheet_signals[SELECT_COLUMN] =
882 g_signal_new ("select-column",
883 G_TYPE_FROM_CLASS (object_class),
885 offsetof (PsppireSheetClass, select_column),
887 g_cclosure_marshal_VOID__INT,
894 * PsppireSheet::double-click-row
895 * @sheet: the sheet widget that emitted the signal
896 * @row: the row that was double clicked.
898 * A row's title button has been double clicked
900 sheet_signals[DOUBLE_CLICK_ROW] =
901 g_signal_new ("double-click-row",
902 G_TYPE_FROM_CLASS (object_class),
906 g_cclosure_marshal_VOID__INT,
913 * PsppireSheet::double-click-column
914 * @sheet: the sheet widget that emitted the signal
915 * @column: the column that was double clicked.
917 * A column's title button has been double clicked
919 sheet_signals[DOUBLE_CLICK_COLUMN] =
920 g_signal_new ("double-click-column",
921 G_TYPE_FROM_CLASS (object_class),
925 g_cclosure_marshal_VOID__INT,
932 * PsppireSheet::button-event-column
933 * @sheet: the sheet widget that emitted the signal
934 * @column: the column on which the event occured.
936 * A button event occured on a column title button
938 sheet_signals[BUTTON_EVENT_COLUMN] =
939 g_signal_new ("button-event-column",
940 G_TYPE_FROM_CLASS (object_class),
944 psppire_marshal_VOID__INT_POINTER,
953 * PsppireSheet::button-event-row
954 * @sheet: the sheet widget that emitted the signal
955 * @column: the column on which the event occured.
957 * A button event occured on a row title button
959 sheet_signals[BUTTON_EVENT_ROW] =
960 g_signal_new ("button-event-row",
961 G_TYPE_FROM_CLASS (object_class),
965 psppire_marshal_VOID__INT_POINTER,
973 sheet_signals[SELECT_RANGE] =
974 g_signal_new ("select-range",
975 G_TYPE_FROM_CLASS (object_class),
977 offsetof (PsppireSheetClass, select_range),
979 g_cclosure_marshal_VOID__BOXED,
982 PSPPIRE_TYPE_SHEET_RANGE);
985 sheet_signals[RESIZE_RANGE] =
986 g_signal_new ("resize-range",
987 G_TYPE_FROM_CLASS (object_class),
989 offsetof (PsppireSheetClass, resize_range),
991 psppire_marshal_VOID__BOXED_BOXED,
994 PSPPIRE_TYPE_SHEET_RANGE, PSPPIRE_TYPE_SHEET_RANGE
997 sheet_signals[MOVE_RANGE] =
998 g_signal_new ("move-range",
999 G_TYPE_FROM_CLASS (object_class),
1001 offsetof (PsppireSheetClass, move_range),
1003 psppire_marshal_VOID__BOXED_BOXED,
1006 PSPPIRE_TYPE_SHEET_RANGE, PSPPIRE_TYPE_SHEET_RANGE
1009 sheet_signals[TRAVERSE] =
1010 g_signal_new ("traverse",
1011 G_TYPE_FROM_CLASS (object_class),
1013 offsetof (PsppireSheetClass, traverse),
1015 psppire_marshal_BOOLEAN__BOXED_POINTER,
1017 PSPPIRE_TYPE_SHEET_CELL,
1021 sheet_signals[ACTIVATE] =
1022 g_signal_new ("activate",
1023 G_TYPE_FROM_CLASS (object_class),
1025 offsetof (PsppireSheetClass, activate),
1027 psppire_marshal_VOID__INT_INT_INT_INT,
1029 G_TYPE_INT, G_TYPE_INT,
1030 G_TYPE_INT, G_TYPE_INT);
1032 widget_class->set_scroll_adjustments_signal =
1033 g_signal_new ("set-scroll-adjustments",
1034 G_TYPE_FROM_CLASS (object_class),
1036 offsetof (PsppireSheetClass, set_scroll_adjustments),
1038 psppire_marshal_VOID__OBJECT_OBJECT,
1039 G_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
1042 container_class->add = NULL;
1043 container_class->remove = NULL;
1044 container_class->forall = psppire_sheet_forall;
1045 container_class->set_focus_child = NULL;
1047 object_class->dispose = psppire_sheet_dispose;
1048 object_class->finalize = psppire_sheet_finalize;
1051 g_param_spec_boxed ("cell-padding",
1053 "The space between a cell's contents and its border",
1055 G_PARAM_CONSTRUCT | G_PARAM_READABLE | G_PARAM_WRITABLE);
1058 g_param_spec_pointer ("vertical-axis",
1060 "A pointer to the PsppireAxis object for the rows",
1061 G_PARAM_READABLE | G_PARAM_WRITABLE );
1064 g_param_spec_pointer ("horizontal-axis",
1066 "A pointer to the PsppireAxis object for the columns",
1067 G_PARAM_READABLE | G_PARAM_WRITABLE );
1070 g_param_spec_pointer ("model",
1072 "A pointer to the data model",
1073 G_PARAM_READABLE | G_PARAM_WRITABLE );
1076 object_class->set_property = psppire_sheet_set_property;
1077 object_class->get_property = psppire_sheet_get_property;
1079 g_object_class_install_property (object_class,
1083 g_object_class_install_property (object_class,
1087 g_object_class_install_property (object_class,
1091 g_object_class_install_property (object_class,
1096 widget_class->realize = psppire_sheet_realize;
1097 widget_class->unrealize = psppire_sheet_unrealize;
1098 widget_class->map = psppire_sheet_map;
1099 widget_class->unmap = psppire_sheet_unmap;
1100 widget_class->style_set = psppire_sheet_style_set;
1101 widget_class->button_press_event = psppire_sheet_button_press;
1102 widget_class->button_release_event = psppire_sheet_button_release;
1103 widget_class->motion_notify_event = psppire_sheet_motion;
1104 widget_class->enter_notify_event = psppire_sheet_crossing_notify;
1105 widget_class->leave_notify_event = psppire_sheet_crossing_notify;
1106 widget_class->key_press_event = psppire_sheet_key_press;
1107 widget_class->expose_event = psppire_sheet_expose;
1108 widget_class->size_request = psppire_sheet_size_request;
1109 widget_class->size_allocate = psppire_sheet_size_allocate;
1110 widget_class->focus_in_event = psppire_sheet_focus_in;
1111 widget_class->focus_out_event = NULL;
1113 klass->set_scroll_adjustments = psppire_sheet_set_scroll_adjustments;
1114 klass->select_row = NULL;
1115 klass->select_column = NULL;
1116 klass->select_range = NULL;
1117 klass->resize_range = NULL;
1118 klass->move_range = NULL;
1119 klass->traverse = NULL;
1120 klass->activate = NULL;
1121 klass->changed = NULL;
1125 psppire_sheet_init (PsppireSheet *sheet)
1127 sheet->model = NULL;
1128 sheet->haxis = NULL;
1129 sheet->vaxis = NULL;
1132 sheet->selection_mode = GTK_SELECTION_NONE;
1133 sheet->select_status = PSPPIRE_SHEET_NORMAL;
1135 GTK_WIDGET_UNSET_FLAGS (sheet, GTK_NO_WINDOW);
1136 GTK_WIDGET_SET_FLAGS (sheet, GTK_CAN_FOCUS);
1138 sheet->column_title_window = NULL;
1139 sheet->column_title_area.x = 0;
1140 sheet->column_title_area.y = 0;
1141 sheet->column_title_area.width = 0;
1142 sheet->column_title_area.height = DEFAULT_ROW_HEIGHT;
1144 sheet->row_title_window = NULL;
1145 sheet->row_title_area.x = 0;
1146 sheet->row_title_area.y = 0;
1147 sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
1148 sheet->row_title_area.height = 0;
1151 sheet->active_cell.row = 0;
1152 sheet->active_cell.col = 0;
1154 sheet->range.row0 = 0;
1155 sheet->range.rowi = 0;
1156 sheet->range.col0 = 0;
1157 sheet->range.coli = 0;
1159 sheet->sheet_window = NULL;
1160 sheet->entry_widget = NULL;
1161 sheet->button = NULL;
1163 sheet->hadjustment = NULL;
1164 sheet->vadjustment = NULL;
1166 sheet->cursor_drag = NULL;
1168 sheet->xor_gc = NULL;
1169 sheet->fg_gc = NULL;
1170 sheet->bg_gc = NULL;
1173 sheet->show_grid = TRUE;
1175 sheet->motion_timer = 0;
1177 sheet->row_titles_visible = TRUE;
1178 sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
1180 sheet->column_titles_visible = TRUE;
1183 /* create sheet entry */
1184 sheet->entry_type = GTK_TYPE_ENTRY;
1185 create_sheet_entry (sheet);
1187 /* create global selection button */
1188 create_global_button (sheet);
1192 /* Cause RANGE to be redrawn. If RANGE is null, then the
1193 entire visible range will be redrawn.
1196 redraw_range (PsppireSheet *sheet, PsppireSheetRange *range)
1200 if ( ! GTK_WIDGET_REALIZED (sheet))
1203 if ( NULL != range )
1204 rectangle_from_range (sheet, range, &rect);
1207 GdkRegion *r = gdk_drawable_get_visible_region (sheet->sheet_window);
1208 gdk_region_get_clipbox (r, &rect);
1210 if ( sheet->column_titles_visible)
1212 rect.y += sheet->column_title_area.height;
1213 rect.height -= sheet->column_title_area.height;
1216 if ( sheet->row_titles_visible)
1218 rect.x += sheet->row_title_area.width;
1219 rect.width -= sheet->row_title_area.width;
1223 gdk_window_invalidate_rect (sheet->sheet_window, &rect, FALSE);
1227 /* Callback which occurs whenever columns are inserted / deleted in the model */
1229 columns_inserted_deleted_callback (PsppireSheetModel *model, gint first_column,
1233 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1235 PsppireSheetRange range;
1236 gint model_columns = psppire_sheet_model_get_column_count (model);
1239 /* Need to update all the columns starting from the first column and onwards.
1240 * Previous column are unchanged, so don't need to be updated.
1242 range.col0 = first_column;
1244 range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1245 range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1247 adjust_scrollbars (sheet);
1249 if (sheet->active_cell.col >= model_columns)
1250 change_active_cell (sheet, sheet->active_cell.row, model_columns - 1);
1252 draw_column_title_buttons_range (sheet,
1253 first_column, max_visible_column (sheet));
1256 redraw_range (sheet, &range);
1262 /* Callback which occurs whenever rows are inserted / deleted in the model */
1264 rows_inserted_deleted_callback (PsppireSheetModel *model, gint first_row,
1265 gint n_rows, gpointer data)
1267 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1269 PsppireSheetRange range;
1271 gint model_rows = psppire_sheet_model_get_row_count (model);
1273 /* Need to update all the rows starting from the first row and onwards.
1274 * Previous rows are unchanged, so don't need to be updated.
1276 range.row0 = first_row;
1278 range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1279 range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1281 adjust_scrollbars (sheet);
1283 if (sheet->active_cell.row >= model_rows)
1284 change_active_cell (sheet, model_rows - 1, sheet->active_cell.col);
1286 draw_row_title_buttons_range (sheet, first_row, max_visible_row (sheet));
1288 redraw_range (sheet, &range);
1292 If row0 or rowi are negative, then all rows will be updated.
1293 If col0 or coli are negative, then all columns will be updated.
1296 range_update_callback (PsppireSheetModel *m, gint row0, gint col0,
1297 gint rowi, gint coli, gpointer data)
1299 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1301 PsppireSheetRange range;
1308 if ( !GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1311 if ( ( row0 < 0 && col0 < 0 ) || ( rowi < 0 && coli < 0 ) )
1313 redraw_range (sheet, NULL);
1314 adjust_scrollbars (sheet);
1316 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
1317 max_visible_row (sheet));
1319 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
1320 max_visible_column (sheet));
1324 else if ( row0 < 0 || rowi < 0 )
1326 range.row0 = min_visible_row (sheet);
1327 range.rowi = max_visible_row (sheet);
1329 else if ( col0 < 0 || coli < 0 )
1331 range.col0 = min_visible_column (sheet);
1332 range.coli = max_visible_column (sheet);
1335 redraw_range (sheet, &range);
1340 * psppire_sheet_new:
1341 * @rows: initial number of rows
1342 * @columns: initial number of columns
1343 * @title: sheet title
1344 * @model: the model to use for the sheet data
1346 * Creates a new sheet widget with the given number of rows and columns.
1348 * Returns: the new sheet widget
1351 psppire_sheet_new (PsppireSheetModel *model)
1353 GtkWidget *widget = g_object_new (PSPPIRE_TYPE_SHEET,
1361 * psppire_sheet_set_model
1362 * @sheet: the sheet to set the model for
1363 * @model: the model to use for the sheet data
1365 * Sets the model for a PsppireSheet
1369 psppire_sheet_set_model (PsppireSheet *sheet, PsppireSheetModel *model)
1371 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1373 if (sheet->model ) g_object_unref (sheet->model);
1375 sheet->model = model;
1379 g_object_ref (model);
1381 sheet->update_handler_id = g_signal_connect (model, "range_changed",
1382 G_CALLBACK (range_update_callback),
1385 g_signal_connect (model, "rows_inserted",
1386 G_CALLBACK (rows_inserted_deleted_callback), sheet);
1388 g_signal_connect (model, "rows_deleted",
1389 G_CALLBACK (rows_inserted_deleted_callback), sheet);
1391 g_signal_connect (model, "columns_inserted",
1392 G_CALLBACK (columns_inserted_deleted_callback), sheet);
1394 g_signal_connect (model, "columns_deleted",
1395 G_CALLBACK (columns_inserted_deleted_callback), sheet);
1401 psppire_sheet_change_entry (PsppireSheet *sheet, GtkType entry_type)
1403 g_return_if_fail (sheet != NULL);
1404 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1406 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
1407 psppire_sheet_hide_entry_widget (sheet);
1409 sheet->entry_type = entry_type;
1411 create_sheet_entry (sheet);
1413 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
1414 psppire_sheet_show_entry_widget (sheet);
1418 psppire_sheet_show_grid (PsppireSheet *sheet, gboolean show)
1420 g_return_if_fail (sheet != NULL);
1421 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1423 if (show == sheet->show_grid) return;
1425 sheet->show_grid = show;
1427 redraw_range (sheet, NULL);
1431 psppire_sheet_grid_visible (PsppireSheet *sheet)
1433 g_return_val_if_fail (sheet != NULL, 0);
1434 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
1436 return sheet->show_grid;
1440 psppire_sheet_get_columns_count (PsppireSheet *sheet)
1442 g_return_val_if_fail (sheet != NULL, 0);
1443 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
1445 return psppire_axis_unit_count (sheet->haxis);
1448 static void set_column_width (PsppireSheet *sheet,
1454 psppire_sheet_show_column_titles (PsppireSheet *sheet)
1456 if (sheet->column_titles_visible) return;
1458 sheet->column_titles_visible = TRUE;
1460 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1463 gdk_window_show (sheet->column_title_window);
1464 gdk_window_move_resize (sheet->column_title_window,
1465 sheet->column_title_area.x,
1466 sheet->column_title_area.y,
1467 sheet->column_title_area.width,
1468 sheet->column_title_area.height);
1470 adjust_scrollbars (sheet);
1472 if (sheet->vadjustment)
1473 g_signal_emit_by_name (sheet->vadjustment,
1476 size_allocate_global_button (sheet);
1478 if ( sheet->row_titles_visible)
1479 gtk_widget_show (sheet->button);
1484 psppire_sheet_show_row_titles (PsppireSheet *sheet)
1486 if (sheet->row_titles_visible) return;
1488 sheet->row_titles_visible = TRUE;
1491 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1493 gdk_window_show (sheet->row_title_window);
1494 gdk_window_move_resize (sheet->row_title_window,
1495 sheet->row_title_area.x,
1496 sheet->row_title_area.y,
1497 sheet->row_title_area.width,
1498 sheet->row_title_area.height);
1500 adjust_scrollbars (sheet);
1503 if (sheet->hadjustment)
1504 g_signal_emit_by_name (sheet->hadjustment,
1507 size_allocate_global_button (sheet);
1509 if ( sheet->column_titles_visible)
1510 gtk_widget_show (sheet->button);
1514 psppire_sheet_hide_column_titles (PsppireSheet *sheet)
1516 if (!sheet->column_titles_visible) return;
1518 sheet->column_titles_visible = FALSE;
1520 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1522 if (sheet->column_title_window)
1523 gdk_window_hide (sheet->column_title_window);
1525 gtk_widget_hide (sheet->button);
1527 adjust_scrollbars (sheet);
1530 if (sheet->vadjustment)
1531 g_signal_emit_by_name (sheet->vadjustment,
1536 psppire_sheet_hide_row_titles (PsppireSheet *sheet)
1538 if (!sheet->row_titles_visible) return;
1540 sheet->row_titles_visible = FALSE;
1542 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1544 if (sheet->row_title_window)
1545 gdk_window_hide (sheet->row_title_window);
1547 gtk_widget_hide (sheet->button);
1549 adjust_scrollbars (sheet);
1552 if (sheet->hadjustment)
1553 g_signal_emit_by_name (sheet->hadjustment,
1558 /* Scroll the sheet so that the cell ROW, COLUMN is visible.
1559 If {ROW,COL}_ALIGN is zero, then the cell will be placed
1560 at the {top,left} of the sheet. If it's 1, then it'll
1561 be placed at the {bottom,right}.
1562 ROW or COL may be -1, in which case scrolling in that dimension
1566 psppire_sheet_moveto (PsppireSheet *sheet,
1574 g_return_if_fail (row_align >= 0);
1575 g_return_if_fail (col_align >= 0);
1577 g_return_if_fail (row_align <= 1);
1578 g_return_if_fail (col_align <= 1);
1580 g_return_if_fail (col <
1581 psppire_axis_unit_count (sheet->haxis));
1582 g_return_if_fail (row <
1583 psppire_axis_unit_count (sheet->vaxis));
1585 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
1590 gint y = psppire_axis_start_pixel (sheet->vaxis, row);
1592 gtk_adjustment_set_value (sheet->vadjustment, y - height * row_align);
1598 gint x = psppire_axis_start_pixel (sheet->haxis, col);
1600 gtk_adjustment_set_value (sheet->hadjustment, x - width * col_align);
1608 psppire_sheet_range_isvisible (const PsppireSheet *sheet,
1609 const PsppireSheetRange *range)
1611 g_return_val_if_fail (sheet != NULL, FALSE);
1613 if (range->row0 < 0 || range->row0 >= psppire_axis_unit_count (sheet->vaxis))
1616 if (range->rowi < 0 || range->rowi >= psppire_axis_unit_count (sheet->vaxis))
1619 if (range->col0 < 0 || range->col0 >= psppire_axis_unit_count (sheet->haxis))
1622 if (range->coli < 0 || range->coli >= psppire_axis_unit_count (sheet->haxis))
1625 if (range->rowi < min_visible_row (sheet))
1628 if (range->row0 > max_visible_row (sheet))
1631 if (range->coli < min_visible_column (sheet))
1634 if (range->col0 > max_visible_column (sheet))
1641 psppire_sheet_cell_isvisible (PsppireSheet *sheet,
1642 gint row, gint column)
1644 PsppireSheetRange range;
1647 range.col0 = column;
1649 range.coli = column;
1651 return psppire_sheet_range_isvisible (sheet, &range);
1655 psppire_sheet_get_visible_range (PsppireSheet *sheet, PsppireSheetRange *range)
1657 g_return_if_fail (sheet != NULL);
1658 g_return_if_fail (PSPPIRE_IS_SHEET (sheet)) ;
1659 g_return_if_fail (range != NULL);
1661 range->row0 = min_visible_row (sheet);
1662 range->col0 = min_visible_column (sheet);
1663 range->rowi = max_visible_row (sheet);
1664 range->coli = max_visible_column (sheet);
1669 psppire_sheet_set_scroll_adjustments (PsppireSheet *sheet,
1670 GtkAdjustment *hadjustment,
1671 GtkAdjustment *vadjustment)
1673 if ( sheet->vadjustment != vadjustment )
1675 if (sheet->vadjustment)
1676 g_object_unref (sheet->vadjustment);
1677 sheet->vadjustment = vadjustment;
1681 g_object_ref (vadjustment);
1683 g_signal_connect (sheet->vadjustment, "value_changed",
1684 G_CALLBACK (vadjustment_value_changed),
1689 if ( sheet->hadjustment != hadjustment )
1691 if (sheet->hadjustment)
1692 g_object_unref (sheet->hadjustment);
1694 sheet->hadjustment = hadjustment;
1698 g_object_ref (hadjustment);
1700 g_signal_connect (sheet->hadjustment, "value_changed",
1701 G_CALLBACK (hadjustment_value_changed),
1709 psppire_sheet_finalize (GObject *object)
1711 PsppireSheet *sheet;
1713 g_return_if_fail (object != NULL);
1714 g_return_if_fail (PSPPIRE_IS_SHEET (object));
1716 sheet = PSPPIRE_SHEET (object);
1718 if (G_OBJECT_CLASS (parent_class)->finalize)
1719 (*G_OBJECT_CLASS (parent_class)->finalize) (object);
1723 psppire_sheet_dispose (GObject *object)
1725 PsppireSheet *sheet = PSPPIRE_SHEET (object);
1727 g_return_if_fail (object != NULL);
1728 g_return_if_fail (PSPPIRE_IS_SHEET (object));
1730 if ( sheet->dispose_has_run )
1733 sheet->dispose_has_run = TRUE;
1735 if ( sheet->cell_padding)
1736 g_boxed_free (GTK_TYPE_BORDER, sheet->cell_padding);
1738 if (sheet->model) g_object_unref (sheet->model);
1739 if (sheet->vaxis) g_object_unref (sheet->vaxis);
1740 if (sheet->haxis) g_object_unref (sheet->haxis);
1742 g_object_unref (sheet->button);
1743 sheet->button = NULL;
1745 /* unref adjustments */
1746 if (sheet->hadjustment)
1748 g_signal_handlers_disconnect_matched (sheet->hadjustment,
1749 G_SIGNAL_MATCH_DATA,
1753 g_object_unref (sheet->hadjustment);
1754 sheet->hadjustment = NULL;
1757 if (sheet->vadjustment)
1759 g_signal_handlers_disconnect_matched (sheet->vadjustment,
1760 G_SIGNAL_MATCH_DATA,
1764 g_object_unref (sheet->vadjustment);
1766 sheet->vadjustment = NULL;
1769 if (G_OBJECT_CLASS (parent_class)->dispose)
1770 (*G_OBJECT_CLASS (parent_class)->dispose) (object);
1774 psppire_sheet_style_set (GtkWidget *widget,
1775 GtkStyle *previous_style)
1777 PsppireSheet *sheet;
1779 g_return_if_fail (widget != NULL);
1780 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1782 if (GTK_WIDGET_CLASS (parent_class)->style_set)
1783 (*GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous_style);
1785 sheet = PSPPIRE_SHEET (widget);
1787 if (GTK_WIDGET_REALIZED (widget))
1789 gtk_style_set_background (widget->style, widget->window, widget->state);
1792 set_entry_widget_font (sheet);
1797 psppire_sheet_realize (GtkWidget *widget)
1799 PsppireSheet *sheet;
1800 GdkWindowAttr attributes;
1801 const gint attributes_mask =
1802 GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_CURSOR;
1805 GdkColormap *colormap;
1806 GdkDisplay *display;
1808 g_return_if_fail (widget != NULL);
1809 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1811 sheet = PSPPIRE_SHEET (widget);
1813 colormap = gtk_widget_get_colormap (widget);
1814 display = gtk_widget_get_display (widget);
1816 attributes.window_type = GDK_WINDOW_CHILD;
1817 attributes.x = widget->allocation.x;
1818 attributes.y = widget->allocation.y;
1819 attributes.width = widget->allocation.width;
1820 attributes.height = widget->allocation.height;
1821 attributes.wclass = GDK_INPUT_OUTPUT;
1823 attributes.visual = gtk_widget_get_visual (widget);
1824 attributes.colormap = colormap;
1826 attributes.event_mask = gtk_widget_get_events (widget);
1827 attributes.event_mask |= (GDK_EXPOSURE_MASK |
1828 GDK_BUTTON_PRESS_MASK |
1829 GDK_BUTTON_RELEASE_MASK |
1830 GDK_KEY_PRESS_MASK |
1831 GDK_ENTER_NOTIFY_MASK |
1832 GDK_LEAVE_NOTIFY_MASK |
1833 GDK_POINTER_MOTION_MASK |
1834 GDK_POINTER_MOTION_HINT_MASK);
1836 attributes.cursor = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
1839 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1841 gdk_window_set_user_data (widget->window, sheet);
1843 widget->style = gtk_style_attach (widget->style, widget->window);
1845 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1847 gdk_color_parse ("white", &sheet->color[BG_COLOR]);
1848 gdk_colormap_alloc_color (colormap, &sheet->color[BG_COLOR], FALSE,
1850 gdk_color_parse ("gray", &sheet->color[GRID_COLOR]);
1851 gdk_colormap_alloc_color (colormap, &sheet->color[GRID_COLOR], FALSE,
1856 attributes.width = sheet->column_title_area.width;
1857 attributes.height = sheet->column_title_area.height;
1860 /* column - title window */
1861 sheet->column_title_window =
1862 gdk_window_new (widget->window, &attributes, attributes_mask);
1863 gdk_window_set_user_data (sheet->column_title_window, sheet);
1864 gtk_style_set_background (widget->style, sheet->column_title_window,
1870 attributes.width = sheet->row_title_area.width;
1871 attributes.height = sheet->row_title_area.height;
1873 /* row - title window */
1874 sheet->row_title_window = gdk_window_new (widget->window,
1875 &attributes, attributes_mask);
1876 gdk_window_set_user_data (sheet->row_title_window, sheet);
1877 gtk_style_set_background (widget->style, sheet->row_title_window,
1880 /* sheet - window */
1881 attributes.cursor = gdk_cursor_new_for_display (display, GDK_PLUS);
1886 sheet->sheet_window = gdk_window_new (widget->window,
1887 &attributes, attributes_mask);
1888 gdk_window_set_user_data (sheet->sheet_window, sheet);
1890 gdk_cursor_unref (attributes.cursor);
1892 gdk_window_set_background (sheet->sheet_window, &widget->style->white);
1893 gdk_window_show (sheet->sheet_window);
1896 sheet->fg_gc = gdk_gc_new (widget->window);
1897 sheet->bg_gc = gdk_gc_new (widget->window);
1899 values.foreground = widget->style->white;
1900 values.function = GDK_INVERT;
1901 values.subwindow_mode = GDK_INCLUDE_INFERIORS;
1902 values.line_width = MAX (sheet->cell_padding->left,
1903 MAX (sheet->cell_padding->right,
1904 MAX (sheet->cell_padding->top,
1905 sheet->cell_padding->bottom)));
1907 sheet->xor_gc = gdk_gc_new_with_values (widget->window,
1915 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
1916 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
1918 gtk_widget_set_parent_window (sheet->button, sheet->sheet_window);
1919 gtk_widget_set_parent (sheet->button, GTK_WIDGET (sheet));
1921 sheet->button->style = gtk_style_attach (sheet->button->style,
1922 sheet->sheet_window);
1925 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
1927 if (sheet->column_titles_visible)
1928 gdk_window_show (sheet->column_title_window);
1929 if (sheet->row_titles_visible)
1930 gdk_window_show (sheet->row_title_window);
1932 sheet->hover_window = create_hover_window ();
1934 draw_row_title_buttons (sheet);
1935 draw_column_title_buttons (sheet);
1937 psppire_sheet_update_primary_selection (sheet);
1940 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1944 create_global_button (PsppireSheet *sheet)
1946 sheet->button = gtk_button_new_with_label (" ");
1948 GTK_WIDGET_UNSET_FLAGS(sheet->button, GTK_CAN_FOCUS);
1950 g_object_ref_sink (sheet->button);
1952 g_signal_connect (sheet->button,
1954 G_CALLBACK (global_button_clicked),
1959 size_allocate_global_button (PsppireSheet *sheet)
1961 GtkAllocation allocation;
1963 if (!sheet->column_titles_visible) return;
1964 if (!sheet->row_titles_visible) return;
1966 gtk_widget_size_request (sheet->button, NULL);
1970 allocation.width = sheet->row_title_area.width;
1971 allocation.height = sheet->column_title_area.height;
1973 gtk_widget_size_allocate (sheet->button, &allocation);
1977 global_button_clicked (GtkWidget *widget, gpointer data)
1979 psppire_sheet_click_cell (PSPPIRE_SHEET (data), -1, -1);
1984 psppire_sheet_unrealize (GtkWidget *widget)
1986 PsppireSheet *sheet;
1988 g_return_if_fail (widget != NULL);
1989 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1991 sheet = PSPPIRE_SHEET (widget);
1993 gdk_cursor_unref (sheet->cursor_drag);
1994 sheet->cursor_drag = NULL;
1996 gdk_colormap_free_colors (gtk_widget_get_colormap (widget),
1997 sheet->color, n_COLORS);
1999 g_object_unref (sheet->xor_gc);
2000 g_object_unref (sheet->fg_gc);
2001 g_object_unref (sheet->bg_gc);
2003 destroy_hover_window (sheet->hover_window);
2005 gdk_window_destroy (sheet->sheet_window);
2006 gdk_window_destroy (sheet->column_title_window);
2007 gdk_window_destroy (sheet->row_title_window);
2009 gtk_widget_unparent (sheet->entry_widget);
2010 if (sheet->button != NULL)
2011 gtk_widget_unparent (sheet->button);
2013 if (GTK_WIDGET_CLASS (parent_class)->unrealize)
2014 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
2018 psppire_sheet_map (GtkWidget *widget)
2020 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2022 g_return_if_fail (widget != NULL);
2023 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
2025 if (!GTK_WIDGET_MAPPED (widget))
2027 GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
2029 gdk_window_show (widget->window);
2030 gdk_window_show (sheet->sheet_window);
2032 if (sheet->column_titles_visible)
2034 draw_column_title_buttons (sheet);
2035 gdk_window_show (sheet->column_title_window);
2037 if (sheet->row_titles_visible)
2039 draw_row_title_buttons (sheet);
2040 gdk_window_show (sheet->row_title_window);
2043 if (!GTK_WIDGET_MAPPED (sheet->entry_widget)
2044 && sheet->active_cell.row >= 0
2045 && sheet->active_cell.col >= 0 )
2047 gtk_widget_show (sheet->entry_widget);
2048 gtk_widget_map (sheet->entry_widget);
2051 if (!GTK_WIDGET_MAPPED (sheet->button))
2053 gtk_widget_show (sheet->button);
2054 gtk_widget_map (sheet->button);
2057 redraw_range (sheet, NULL);
2058 change_active_cell (sheet,
2059 sheet->active_cell.row,
2060 sheet->active_cell.col);
2065 psppire_sheet_unmap (GtkWidget *widget)
2067 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2069 if (!GTK_WIDGET_MAPPED (widget))
2072 GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
2074 gdk_window_hide (sheet->sheet_window);
2075 if (sheet->column_titles_visible)
2076 gdk_window_hide (sheet->column_title_window);
2077 if (sheet->row_titles_visible)
2078 gdk_window_hide (sheet->row_title_window);
2079 gdk_window_hide (widget->window);
2081 gtk_widget_unmap (sheet->entry_widget);
2082 gtk_widget_unmap (sheet->button);
2083 gtk_widget_unmap (sheet->hover_window->window);
2086 /* get cell attributes of the given cell */
2087 /* TRUE means that the cell is currently allocated */
2088 static gboolean psppire_sheet_get_attributes (const PsppireSheet *sheet,
2090 PsppireSheetCellAttr *attributes);
2095 psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint col)
2097 PangoLayout *layout;
2098 PangoRectangle text;
2099 PangoFontDescription *font_desc = GTK_WIDGET (sheet)->style->font_desc;
2104 PsppireSheetCellAttr attributes;
2107 g_return_if_fail (sheet != NULL);
2109 /* bail now if we aren't yet drawable */
2110 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
2113 row >= psppire_axis_unit_count (sheet->vaxis))
2117 col >= psppire_axis_unit_count (sheet->haxis))
2120 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2122 /* select GC for background rectangle */
2123 gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground);
2124 gdk_gc_set_foreground (sheet->bg_gc, &attributes.background);
2126 rectangle_from_cell (sheet, row, col, &area);
2128 gdk_gc_set_line_attributes (sheet->fg_gc, 1, 0, 0, 0);
2130 if (sheet->show_grid)
2132 gdk_gc_set_foreground (sheet->bg_gc, &sheet->color[GRID_COLOR]);
2134 gdk_draw_rectangle (sheet->sheet_window,
2138 area.width, area.height);
2142 label = psppire_sheet_cell_get_text (sheet, row, col);
2147 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), label);
2148 dispose_string (sheet, label);
2151 pango_layout_set_font_description (layout, font_desc);
2153 pango_layout_get_pixel_extents (layout, NULL, &text);
2155 gdk_gc_set_clip_rectangle (sheet->fg_gc, &area);
2157 font_height = pango_font_description_get_size (font_desc);
2158 if ( !pango_font_description_get_size_is_absolute (font_desc))
2159 font_height /= PANGO_SCALE;
2162 if ( sheet->cell_padding )
2164 area.x += sheet->cell_padding->left;
2165 area.width -= sheet->cell_padding->right
2166 + sheet->cell_padding->left;
2168 area.y += sheet->cell_padding->top;
2169 area.height -= sheet->cell_padding->bottom
2171 sheet->cell_padding->top;
2174 /* Centre the text vertically */
2175 area.y += (area.height - font_height) / 2.0;
2177 switch (attributes.justification)
2179 case GTK_JUSTIFY_RIGHT:
2180 area.x += area.width - text.width;
2182 case GTK_JUSTIFY_CENTER:
2183 area.x += (area.width - text.width) / 2.0;
2185 case GTK_JUSTIFY_LEFT:
2189 g_critical ("Unhandled justification %d in column %d\n",
2190 attributes.justification, col);
2194 gdk_draw_layout (sheet->sheet_window, sheet->fg_gc,
2199 gdk_gc_set_clip_rectangle (sheet->fg_gc, NULL);
2200 g_object_unref (layout);
2205 draw_sheet_region (PsppireSheet *sheet, GdkRegion *region)
2207 PsppireSheetRange range;
2212 PsppireSheetRange drawing_range;
2214 gdk_region_get_clipbox (region, &area);
2216 y = area.y + sheet->vadjustment->value;
2217 x = area.x + sheet->hadjustment->value;
2219 if ( sheet->column_titles_visible)
2220 y -= sheet->column_title_area.height;
2222 if ( sheet->row_titles_visible)
2223 x -= sheet->row_title_area.width;
2225 maximize_int (&x, 0);
2226 maximize_int (&y, 0);
2228 range.row0 = row_from_ypixel (sheet, y);
2229 range.rowi = row_from_ypixel (sheet, y + area.height);
2231 range.col0 = column_from_xpixel (sheet, x);
2232 range.coli = column_from_xpixel (sheet, x + area.width);
2234 g_return_if_fail (sheet != NULL);
2235 g_return_if_fail (PSPPIRE_SHEET (sheet));
2237 if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (sheet))) return;
2238 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2239 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
2242 drawing_range.row0 = MAX (range.row0, min_visible_row (sheet));
2243 drawing_range.col0 = MAX (range.col0, min_visible_column (sheet));
2244 drawing_range.rowi = MIN (range.rowi, max_visible_row (sheet));
2245 drawing_range.coli = MIN (range.coli, max_visible_column (sheet));
2247 g_return_if_fail (drawing_range.rowi >= drawing_range.row0);
2248 g_return_if_fail (drawing_range.coli >= drawing_range.col0);
2250 for (i = drawing_range.row0; i <= drawing_range.rowi; i++)
2252 for (j = drawing_range.col0; j <= drawing_range.coli; j++)
2253 psppire_sheet_cell_draw (sheet, i, j);
2256 if (sheet->select_status == PSPPIRE_SHEET_NORMAL &&
2257 sheet->active_cell.row >= drawing_range.row0 &&
2258 sheet->active_cell.row <= drawing_range.rowi &&
2259 sheet->active_cell.col >= drawing_range.col0 &&
2260 sheet->active_cell.col <= drawing_range.coli)
2261 psppire_sheet_show_entry_widget (sheet);
2267 safe_strcmp (const gchar *s1, const gchar *s2)
2269 if ( !s1 && !s2) return 0;
2270 if ( !s1) return -1;
2271 if ( !s2) return +1;
2272 return strcmp (s1, s2);
2276 psppire_sheet_set_cell (PsppireSheet *sheet, gint row, gint col,
2277 GtkJustification justification,
2280 PsppireSheetModel *model ;
2283 g_return_if_fail (sheet != NULL);
2284 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2286 if (col >= psppire_axis_unit_count (sheet->haxis)
2287 || row >= psppire_axis_unit_count (sheet->vaxis))
2290 if (col < 0 || row < 0) return;
2292 model = psppire_sheet_get_model (sheet);
2294 old_text = psppire_sheet_model_get_string (model, row, col);
2296 if (0 != safe_strcmp (old_text, text))
2298 g_signal_handler_block (sheet->model, sheet->update_handler_id);
2299 psppire_sheet_model_set_string (model, text, row, col);
2300 g_signal_handler_unblock (sheet->model, sheet->update_handler_id);
2303 if ( psppire_sheet_model_free_strings (model))
2309 psppire_sheet_cell_clear (PsppireSheet *sheet, gint row, gint column)
2311 PsppireSheetRange range;
2313 g_return_if_fail (sheet != NULL);
2314 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2315 if (column >= psppire_axis_unit_count (sheet->haxis) ||
2316 row >= psppire_axis_unit_count (sheet->vaxis)) return;
2318 if (column < 0 || row < 0) return;
2322 range.col0 = min_visible_column (sheet);
2323 range.coli = max_visible_column (sheet);
2325 psppire_sheet_real_cell_clear (sheet, row, column);
2327 redraw_range (sheet, &range);
2331 psppire_sheet_real_cell_clear (PsppireSheet *sheet, gint row, gint column)
2333 PsppireSheetModel *model = psppire_sheet_get_model (sheet);
2335 gchar *old_text = psppire_sheet_cell_get_text (sheet, row, column);
2337 if (old_text && strlen (old_text) > 0 )
2339 psppire_sheet_model_datum_clear (model, row, column);
2342 dispose_string (sheet, old_text);
2346 psppire_sheet_cell_get_text (const PsppireSheet *sheet, gint row, gint col)
2348 PsppireSheetModel *model;
2349 g_return_val_if_fail (sheet != NULL, NULL);
2350 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
2352 if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis))
2354 if (col < 0 || row < 0) return NULL;
2356 model = psppire_sheet_get_model (sheet);
2361 return psppire_sheet_model_get_string (model, row, col);
2365 /* Convert X, Y (in pixels) to *ROW, *COLUMN
2366 If the function returns FALSE, then the results will be unreliable.
2369 psppire_sheet_get_pixel_info (PsppireSheet *sheet,
2377 *column = -G_MAXINT;
2379 g_return_val_if_fail (sheet != NULL, 0);
2380 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2382 /* bounds checking, return false if the user clicked
2390 if ( sheet->column_titles_visible)
2391 y -= sheet->column_title_area.height;
2393 y += sheet->vadjustment->value;
2395 if ( y < 0 && sheet->column_titles_visible)
2401 trow = row_from_ypixel (sheet, y);
2402 if (trow > psppire_axis_unit_count (sheet->vaxis))
2408 if ( sheet->row_titles_visible)
2409 x -= sheet->row_title_area.width;
2411 x += sheet->hadjustment->value;
2413 if ( x < 0 && sheet->row_titles_visible)
2419 tcol = column_from_xpixel (sheet, x);
2420 if (tcol > psppire_axis_unit_count (sheet->haxis))
2430 psppire_sheet_get_cell_area (PsppireSheet *sheet,
2435 g_return_val_if_fail (sheet != NULL, 0);
2436 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2438 if (row >= psppire_axis_unit_count (sheet->vaxis) || column >= psppire_axis_unit_count (sheet->haxis))
2441 area->x = (column == -1) ? 0 : psppire_axis_start_pixel (sheet->haxis, column);
2442 area->y = (row == -1) ? 0 : psppire_axis_start_pixel (sheet->vaxis, row);
2444 area->width= (column == -1) ? sheet->row_title_area.width
2445 : psppire_axis_unit_size (sheet->haxis, column);
2447 area->height= (row == -1) ? sheet->column_title_area.height
2448 : psppire_axis_unit_size (sheet->vaxis, row);
2454 psppire_sheet_set_active_cell (PsppireSheet *sheet, gint row, gint col)
2456 g_return_if_fail (sheet != NULL);
2457 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2459 if (row < -1 || col < -1)
2462 if (row >= psppire_axis_unit_count (sheet->vaxis)
2464 col >= psppire_axis_unit_count (sheet->haxis))
2467 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2470 if ( row == -1 || col == -1)
2472 psppire_sheet_hide_entry_widget (sheet);
2476 change_active_cell (sheet, row, col);
2480 psppire_sheet_get_active_cell (PsppireSheet *sheet, gint *row, gint *column)
2482 g_return_if_fail (sheet != NULL);
2483 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2485 if ( row ) *row = sheet->active_cell.row;
2486 if (column) *column = sheet->active_cell.col;
2490 entry_load_text (PsppireSheet *sheet)
2494 GtkJustification justification;
2495 PsppireSheetCellAttr attributes;
2497 if (!GTK_WIDGET_VISIBLE (sheet->entry_widget)) return;
2498 if (sheet->select_status != PSPPIRE_SHEET_NORMAL) return;
2500 row = sheet->active_cell.row;
2501 col = sheet->active_cell.col;
2503 if (row < 0 || col < 0) return;
2505 text = gtk_entry_get_text (psppire_sheet_get_entry (sheet));
2507 if (text && strlen (text) > 0)
2509 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2510 justification = attributes.justification;
2511 psppire_sheet_set_cell (sheet, row, col, justification, text);
2517 psppire_sheet_hide_entry_widget (PsppireSheet *sheet)
2519 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2522 if (sheet->active_cell.row < 0 ||
2523 sheet->active_cell.col < 0) return;
2525 gtk_widget_hide (sheet->entry_widget);
2526 gtk_widget_unmap (sheet->entry_widget);
2528 GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2532 change_active_cell (PsppireSheet *sheet, gint row, gint col)
2534 gint old_row, old_col;
2536 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2538 if (row < 0 || col < 0)
2541 if ( row > psppire_axis_unit_count (sheet->vaxis)
2542 || col > psppire_axis_unit_count (sheet->haxis))
2545 old_row = sheet->active_cell.row;
2546 old_col = sheet->active_cell.col;
2548 entry_load_text (sheet);
2550 /* Erase the old cell border */
2551 psppire_sheet_draw_active_cell (sheet);
2553 sheet->active_cell.row = row;
2554 sheet->active_cell.col = col;
2556 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
2558 GTK_WIDGET_UNSET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2560 psppire_sheet_draw_active_cell (sheet);
2561 psppire_sheet_show_entry_widget (sheet);
2563 GTK_WIDGET_SET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2565 g_signal_emit (sheet, sheet_signals [ACTIVATE], 0,
2566 row, col, old_row, old_col);
2571 psppire_sheet_show_entry_widget (PsppireSheet *sheet)
2573 GtkEntry *sheet_entry;
2574 PsppireSheetCellAttr attributes;
2578 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2580 row = sheet->active_cell.row;
2581 col = sheet->active_cell.col;
2583 /* Don't show the active cell, if there is no active cell: */
2584 if (! (row >= 0 && col >= 0)) /* e.g row or coll == -1. */
2587 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2588 if (sheet->select_status != PSPPIRE_SHEET_NORMAL) return;
2589 if (PSPPIRE_SHEET_IN_SELECTION (sheet)) return;
2591 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2593 sheet_entry = psppire_sheet_get_entry (sheet);
2595 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2597 if (GTK_IS_ENTRY (sheet_entry))
2599 gchar *text = psppire_sheet_cell_get_text (sheet, row, col);
2600 const gchar *old_text = gtk_entry_get_text (GTK_ENTRY (sheet_entry));
2603 text = g_strdup ("");
2605 if (strcmp (old_text, text) != 0)
2606 gtk_entry_set_text (sheet_entry, text);
2608 dispose_string (sheet, text);
2611 switch (attributes.justification)
2613 case GTK_JUSTIFY_RIGHT:
2614 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 1.0);
2616 case GTK_JUSTIFY_CENTER:
2617 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.5);
2619 case GTK_JUSTIFY_LEFT:
2621 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.0);
2627 psppire_sheet_size_allocate_entry (sheet);
2629 gtk_widget_set_sensitive (GTK_WIDGET (sheet_entry),
2630 psppire_sheet_model_is_editable (sheet->model,
2632 gtk_widget_map (sheet->entry_widget);
2636 psppire_sheet_draw_active_cell (PsppireSheet *sheet)
2639 PsppireSheetRange range;
2641 row = sheet->active_cell.row;
2642 col = sheet->active_cell.col;
2644 if (row < 0 || col < 0) return FALSE;
2646 if (!psppire_sheet_cell_isvisible (sheet, row, col))
2649 range.col0 = range.coli = col;
2650 range.row0 = range.rowi = row;
2652 psppire_sheet_draw_border (sheet, range);
2660 psppire_sheet_draw_border (PsppireSheet *sheet, PsppireSheetRange new_range)
2664 rectangle_from_range (sheet, &new_range, &area);
2669 gdk_gc_set_clip_rectangle (sheet->xor_gc, &area);
2671 area.x += sheet->cell_padding->left / 2;
2672 area.y += sheet->cell_padding->top / 2;
2673 area.width -= (sheet->cell_padding->left + sheet->cell_padding->right ) / 2;
2674 area.height -= (sheet->cell_padding->top + sheet->cell_padding->bottom ) / 2;
2676 gdk_draw_rectangle (sheet->sheet_window, sheet->xor_gc,
2683 gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
2688 /* Selection related functions */
2691 psppire_sheet_select_row (PsppireSheet *sheet, gint row)
2694 sheet->select_status = PSPPIRE_SHEET_ROW_SELECTED;
2696 sheet->range.col0 = sheet->range.coli = -1;
2697 sheet->range.row0 = sheet->range.rowi = row;
2699 rectangle_from_range (sheet, &sheet->range, &area);
2703 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
2707 psppire_sheet_select_column (PsppireSheet *sheet, gint column)
2710 sheet->select_status = PSPPIRE_SHEET_COLUMN_SELECTED;
2712 sheet->range.col0 = sheet->range.coli = column;
2713 sheet->range.row0 = sheet->range.rowi = -1;
2715 rectangle_from_range (sheet, &sheet->range, &area);
2719 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
2724 psppire_sheet_select_range (PsppireSheet *sheet, const PsppireSheetRange *range)
2727 sheet->select_status = PSPPIRE_SHEET_RANGE_SELECTED;
2729 sheet->range = *range;
2731 rectangle_from_range (sheet, range, &area);
2734 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
2739 psppire_sheet_unselect_range (PsppireSheet *sheet)
2742 sheet->select_status = PSPPIRE_SHEET_NORMAL;
2744 rectangle_from_range (sheet, &sheet->range, &area);
2747 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
2751 psppire_sheet_real_select_range (PsppireSheet *sheet,
2752 const PsppireSheetRange *range)
2754 g_return_if_fail (sheet != NULL);
2756 if (range == NULL) range = &sheet->range;
2758 memcpy (&sheet->range, range, sizeof (*range));
2760 if (range->row0 < 0 || range->rowi < 0) return;
2761 if (range->col0 < 0 || range->coli < 0) return;
2763 psppire_sheet_update_primary_selection (sheet);
2765 g_signal_emit (sheet, sheet_signals[SELECT_RANGE], 0, &sheet->range);
2770 psppire_sheet_get_selected_range (PsppireSheet *sheet, PsppireSheetRange *range)
2772 g_return_if_fail (sheet != NULL);
2773 *range = sheet->range;
2778 psppire_sheet_expose (GtkWidget *widget, GdkEventExpose *event)
2780 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2782 g_return_val_if_fail (event != NULL, FALSE);
2784 if (!GTK_WIDGET_DRAWABLE (widget))
2787 /* exposure events on the sheet */
2788 if (event->window == sheet->row_title_window &&
2789 sheet->row_titles_visible)
2791 draw_row_title_buttons_range (sheet,
2792 min_visible_row (sheet),
2793 max_visible_row (sheet));
2796 if (event->window == sheet->column_title_window &&
2797 sheet->column_titles_visible)
2799 draw_column_title_buttons_range (sheet,
2800 min_visible_column (sheet),
2801 max_visible_column (sheet));
2804 if (event->window == sheet->sheet_window)
2806 draw_sheet_region (sheet, event->region);
2808 if (sheet->select_status != PSPPIRE_SHEET_NORMAL)
2811 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
2812 psppire_sheet_range_draw (sheet, &sheet->range);
2814 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
2815 psppire_sheet_range_draw (sheet, &sheet->drag_range);
2821 rectangle_from_range (sheet, &sheet->range, &area);
2823 gdk_draw_rectangle (sheet->sheet_window,
2826 area.x + 1, area.y + 1,
2827 area.width, area.height);
2831 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
2832 draw_xor_rectangle (sheet, sheet->drag_range);
2837 if ((!PSPPIRE_SHEET_IN_XDRAG (sheet)) && (!PSPPIRE_SHEET_IN_YDRAG (sheet)))
2840 PsppireSheetRange range;
2841 range.row0 = range.rowi = sheet->active_cell.row;
2842 range.col0 = range.coli = sheet->active_cell.col;
2844 rectangle_from_range (sheet, &range, &rect);
2846 if (GDK_OVERLAP_RECTANGLE_OUT !=
2847 gdk_region_rect_in (event->region, &rect))
2849 psppire_sheet_draw_active_cell (sheet);
2855 (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
2862 psppire_sheet_button_press (GtkWidget *widget, GdkEventButton *event)
2864 PsppireSheet *sheet;
2865 GdkModifierType mods;
2870 g_return_val_if_fail (widget != NULL, FALSE);
2871 g_return_val_if_fail (PSPPIRE_IS_SHEET (widget), FALSE);
2872 g_return_val_if_fail (event != NULL, FALSE);
2874 sheet = PSPPIRE_SHEET (widget);
2876 /* Cancel any pending tooltips */
2877 if (sheet->motion_timer)
2879 g_source_remove (sheet->motion_timer);
2880 sheet->motion_timer = 0;
2883 gtk_widget_get_pointer (widget, &x, &y);
2884 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
2887 if (event->window == sheet->column_title_window)
2889 sheet->x_drag = event->x;
2890 g_signal_emit (sheet,
2891 sheet_signals[BUTTON_EVENT_COLUMN], 0,
2894 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
2896 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
2897 g_signal_emit (sheet,
2898 sheet_signals[DOUBLE_CLICK_COLUMN], 0, column);
2901 else if (event->window == sheet->row_title_window)
2903 g_signal_emit (sheet,
2904 sheet_signals[BUTTON_EVENT_ROW], 0,
2907 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
2909 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
2910 g_signal_emit (sheet,
2911 sheet_signals[DOUBLE_CLICK_ROW], 0, row);
2915 gdk_window_get_pointer (widget->window, NULL, NULL, &mods);
2917 if (! (mods & GDK_BUTTON1_MASK)) return TRUE;
2920 /* press on resize windows */
2921 if (event->window == sheet->column_title_window)
2923 sheet->x_drag = event->x;
2925 if (on_column_boundary (sheet, sheet->x_drag, &sheet->drag_cell.col))
2927 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
2928 gdk_pointer_grab (sheet->column_title_window, FALSE,
2929 GDK_POINTER_MOTION_HINT_MASK |
2930 GDK_BUTTON1_MOTION_MASK |
2931 GDK_BUTTON_RELEASE_MASK,
2932 NULL, NULL, event->time);
2934 draw_xor_vline (sheet);
2939 if (event->window == sheet->row_title_window)
2941 sheet->y_drag = event->y;
2943 if (on_row_boundary (sheet, sheet->y_drag, &sheet->drag_cell.row))
2945 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
2946 gdk_pointer_grab (sheet->row_title_window, FALSE,
2947 GDK_POINTER_MOTION_HINT_MASK |
2948 GDK_BUTTON1_MOTION_MASK |
2949 GDK_BUTTON_RELEASE_MASK,
2950 NULL, NULL, event->time);
2952 draw_xor_hline (sheet);
2957 /* the sheet itself does not handle other than single click events */
2958 if (event->type != GDK_BUTTON_PRESS) return FALSE;
2960 /* selections on the sheet */
2961 if (event->window == sheet->sheet_window)
2963 gtk_widget_get_pointer (widget, &x, &y);
2964 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
2965 gdk_pointer_grab (sheet->sheet_window, FALSE,
2966 GDK_POINTER_MOTION_HINT_MASK |
2967 GDK_BUTTON1_MOTION_MASK |
2968 GDK_BUTTON_RELEASE_MASK,
2969 NULL, NULL, event->time);
2970 gtk_grab_add (GTK_WIDGET (sheet));
2972 if (psppire_sheet_click_cell (sheet, row, column))
2974 if ( sheet->select_status == PSPPIRE_SHEET_NORMAL)
2976 sheet->range.row0 = row;
2977 sheet->range.col0 = column;
2981 psppire_sheet_unselect_range (sheet);
2986 if (event->window == sheet->column_title_window)
2988 gtk_widget_get_pointer (widget, &x, &y);
2989 if ( sheet->row_titles_visible)
2990 x -= sheet->row_title_area.width;
2992 x += sheet->hadjustment->value;
2994 column = column_from_xpixel (sheet, x);
2996 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
2998 gtk_grab_add (GTK_WIDGET (sheet));
2999 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3003 if (event->window == sheet->row_title_window)
3005 gtk_widget_get_pointer (widget, &x, &y);
3006 if ( sheet->column_titles_visible)
3007 y -= sheet->column_title_area.height;
3009 y += sheet->vadjustment->value;
3011 row = row_from_ypixel (sheet, y);
3012 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
3014 gtk_grab_add (GTK_WIDGET (sheet));
3015 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3023 psppire_sheet_click_cell (PsppireSheet *sheet, gint row, gint column)
3025 PsppireSheetCell cell;
3026 gboolean forbid_move;
3031 if (row >= psppire_axis_unit_count (sheet->vaxis)
3032 || column >= psppire_axis_unit_count (sheet->haxis))
3037 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
3038 &sheet->active_cell,
3044 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
3047 row = sheet->active_cell.row;
3048 column = sheet->active_cell.col;
3050 change_active_cell (sheet, row, column);
3054 if (row == -1 && column >= 0)
3056 psppire_sheet_select_column (sheet, column);
3060 if (column == -1 && row >= 0)
3062 psppire_sheet_select_row (sheet, row);
3066 if (row == -1 && column == -1)
3068 sheet->range.row0 = 0;
3069 sheet->range.col0 = 0;
3070 sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
3072 psppire_axis_unit_count (sheet->haxis) - 1;
3073 psppire_sheet_select_range (sheet, NULL);
3077 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
3078 change_active_cell (sheet, row, column);
3080 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
3086 psppire_sheet_button_release (GtkWidget *widget,
3087 GdkEventButton *event)
3089 GdkDisplay *display = gtk_widget_get_display (widget);
3091 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3093 /* release on resize windows */
3094 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3097 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
3098 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3100 gdk_display_pointer_ungrab (display, event->time);
3101 draw_xor_vline (sheet);
3104 psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)
3105 + sheet->hadjustment->value;
3107 set_column_width (sheet, sheet->drag_cell.col, width);
3112 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3115 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
3116 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3118 gdk_display_pointer_ungrab (display, event->time);
3119 draw_xor_hline (sheet);
3122 psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row) +
3123 sheet->vadjustment->value;
3125 set_row_height (sheet, sheet->drag_cell.row, height);
3130 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3132 PsppireSheetRange old_range;
3133 draw_xor_rectangle (sheet, sheet->drag_range);
3134 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_DRAG);
3135 gdk_display_pointer_ungrab (display, event->time);
3137 psppire_sheet_unselect_range (sheet);
3139 old_range = sheet->range;
3140 sheet->range = sheet->drag_range;
3141 sheet->drag_range = old_range;
3142 g_signal_emit (sheet, sheet_signals[MOVE_RANGE], 0,
3143 &sheet->drag_range, &sheet->range);
3144 psppire_sheet_select_range (sheet, &sheet->range);
3147 if (PSPPIRE_SHEET_IN_RESIZE (sheet))
3149 PsppireSheetRange old_range;
3150 draw_xor_rectangle (sheet, sheet->drag_range);
3151 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_RESIZE);
3152 gdk_display_pointer_ungrab (display, event->time);
3154 psppire_sheet_unselect_range (sheet);
3156 old_range = sheet->range;
3157 sheet->range = sheet->drag_range;
3158 sheet->drag_range = old_range;
3160 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
3161 sheet->select_status = PSPPIRE_SHEET_RANGE_SELECTED;
3163 g_signal_emit (sheet, sheet_signals[RESIZE_RANGE], 0,
3164 &sheet->drag_range, &sheet->range);
3165 psppire_sheet_select_range (sheet, &sheet->range);
3168 if (PSPPIRE_SHEET_IN_SELECTION (sheet))
3170 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3171 sheet->select_status = PSPPIRE_SHEET_RANGE_SELECTED;
3173 change_active_cell (sheet, sheet->active_cell.row,
3174 sheet->active_cell.col);
3177 gdk_display_pointer_ungrab (display, event->time);
3178 gtk_grab_remove (GTK_WIDGET (sheet));
3180 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3189 /* Shamelessly lifted from gtktooltips */
3191 psppire_sheet_subtitle_paint_window (GtkWidget *tip_window)
3195 gtk_widget_size_request (tip_window, &req);
3196 gtk_paint_flat_box (tip_window->style, tip_window->window,
3197 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
3198 NULL, GTK_WIDGET(tip_window), "tooltip",
3199 0, 0, req.width, req.height);
3205 destroy_hover_window (PsppireSheetHoverTitle *h)
3207 gtk_widget_destroy (h->window);
3211 static PsppireSheetHoverTitle *
3212 create_hover_window (void)
3214 PsppireSheetHoverTitle *hw = g_malloc (sizeof (*hw));
3216 hw->window = gtk_window_new (GTK_WINDOW_POPUP);
3218 #if GTK_CHECK_VERSION (2, 9, 0)
3219 gtk_window_set_type_hint (GTK_WINDOW (hw->window),
3220 GDK_WINDOW_TYPE_HINT_TOOLTIP);
3223 gtk_widget_set_app_paintable (hw->window, TRUE);
3224 gtk_window_set_resizable (GTK_WINDOW (hw->window), FALSE);
3225 gtk_widget_set_name (hw->window, "gtk-tooltips");
3226 gtk_container_set_border_width (GTK_CONTAINER (hw->window), 4);
3228 g_signal_connect (hw->window,
3230 G_CALLBACK (psppire_sheet_subtitle_paint_window),
3233 hw->label = gtk_label_new (NULL);
3236 gtk_label_set_line_wrap (GTK_LABEL (hw->label), TRUE);
3237 gtk_misc_set_alignment (GTK_MISC (hw->label), 0.5, 0.5);
3239 gtk_container_add (GTK_CONTAINER (hw->window), hw->label);
3241 gtk_widget_show (hw->label);
3243 g_signal_connect (hw->window,
3245 G_CALLBACK (gtk_widget_destroyed),
3251 #define HOVER_WINDOW_Y_OFFSET 2
3254 show_subtitle (PsppireSheet *sheet, gint row, gint column,
3255 const gchar *subtitle)
3264 gtk_label_set_text (GTK_LABEL (sheet->hover_window->label),
3268 sheet->hover_window->row = row;
3269 sheet->hover_window->column = column;
3271 gdk_window_get_origin (GTK_WIDGET (sheet)->window, &x, &y);
3273 gtk_widget_get_pointer (GTK_WIDGET (sheet), &px, &py);
3275 gtk_widget_show (sheet->hover_window->window);
3277 width = GTK_WIDGET (sheet->hover_window->label)->allocation.width;
3283 y += sheet->column_title_area.y;
3284 y += sheet->column_title_area.height;
3285 y += HOVER_WINDOW_Y_OFFSET;
3291 x += sheet->row_title_area.x;
3292 x += sheet->row_title_area.width * 2 / 3.0;
3295 gtk_window_move (GTK_WINDOW (sheet->hover_window->window),
3300 motion_timeout_callback (gpointer data)
3302 PsppireSheet *sheet = PSPPIRE_SHEET (data);
3306 gdk_threads_enter ();
3307 gtk_widget_get_pointer (GTK_WIDGET (sheet), &x, &y);
3309 if ( psppire_sheet_get_pixel_info (sheet, x, y, &row, &column) )
3311 if (sheet->row_title_under && row >= 0)
3313 gchar *text = psppire_sheet_model_get_row_subtitle (sheet->model, row);
3315 show_subtitle (sheet, row, -1, text);
3319 if (sheet->column_title_under && column >= 0)
3321 gchar *text = psppire_sheet_model_get_column_subtitle (sheet->model,
3324 show_subtitle (sheet, -1, column, text);
3330 gdk_threads_leave ();
3335 psppire_sheet_motion (GtkWidget *widget, GdkEventMotion *event)
3337 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3338 GdkModifierType mods;
3339 GdkCursorType new_cursor;
3342 GdkDisplay *display;
3344 g_return_val_if_fail (event != NULL, FALSE);
3346 display = gtk_widget_get_display (widget);
3348 /* selections on the sheet */
3352 if (!GTK_WIDGET_VISIBLE (sheet->hover_window->window))
3354 if ( sheet->motion_timer > 0 )
3355 g_source_remove (sheet->motion_timer);
3356 sheet->motion_timer =
3357 g_timeout_add (TIMEOUT_HOVER, motion_timeout_callback, sheet);
3363 gtk_widget_get_pointer (widget, &wx, &wy);
3365 if ( psppire_sheet_get_pixel_info (sheet, wx, wy, &row, &column) )
3367 if ( row != sheet->hover_window->row ||
3368 column != sheet->hover_window->column)
3370 gtk_widget_hide (sheet->hover_window->window);
3375 if (event->window == sheet->column_title_window)
3377 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3378 on_column_boundary (sheet, x, &column))
3380 new_cursor = GDK_SB_H_DOUBLE_ARROW;
3381 if (new_cursor != sheet->cursor_drag->type)
3383 gdk_cursor_unref (sheet->cursor_drag);
3384 sheet->cursor_drag =
3385 gdk_cursor_new_for_display (display, new_cursor);
3387 gdk_window_set_cursor (sheet->column_title_window,
3388 sheet->cursor_drag);
3393 new_cursor = GDK_TOP_LEFT_ARROW;
3394 if (!PSPPIRE_SHEET_IN_XDRAG (sheet) &&
3395 new_cursor != sheet->cursor_drag->type)
3397 gdk_cursor_unref (sheet->cursor_drag);
3398 sheet->cursor_drag =
3399 gdk_cursor_new_for_display (display, new_cursor);
3400 gdk_window_set_cursor (sheet->column_title_window,
3401 sheet->cursor_drag);
3405 else if (event->window == sheet->row_title_window)
3407 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3408 on_row_boundary (sheet, y, &row))
3410 new_cursor = GDK_SB_V_DOUBLE_ARROW;
3411 if (new_cursor != sheet->cursor_drag->type)
3413 gdk_cursor_unref (sheet->cursor_drag);
3414 sheet->cursor_drag =
3415 gdk_cursor_new_for_display (display, new_cursor);
3416 gdk_window_set_cursor (sheet->row_title_window,
3417 sheet->cursor_drag);
3422 new_cursor = GDK_TOP_LEFT_ARROW;
3423 if (!PSPPIRE_SHEET_IN_YDRAG (sheet) &&
3424 new_cursor != sheet->cursor_drag->type)
3426 gdk_cursor_unref (sheet->cursor_drag);
3427 sheet->cursor_drag =
3428 gdk_cursor_new_for_display (display, new_cursor);
3429 gdk_window_set_cursor (sheet->row_title_window,
3430 sheet->cursor_drag);
3435 new_cursor = GDK_PLUS;
3436 if ( event->window == sheet->sheet_window &&
3437 !POSSIBLE_DRAG (sheet, x, y, &row, &column) &&
3438 !PSPPIRE_SHEET_IN_DRAG (sheet) &&
3439 !POSSIBLE_RESIZE (sheet, x, y, &row, &column) &&
3440 !PSPPIRE_SHEET_IN_RESIZE (sheet) &&
3441 new_cursor != sheet->cursor_drag->type)
3443 gdk_cursor_unref (sheet->cursor_drag);
3444 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
3445 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3448 new_cursor = GDK_TOP_LEFT_ARROW;
3449 if ( event->window == sheet->sheet_window &&
3450 ! (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
3451 PSPPIRE_SHEET_IN_RESIZE (sheet)) &&
3452 (POSSIBLE_DRAG (sheet, x, y, &row, &column) ||
3453 PSPPIRE_SHEET_IN_DRAG (sheet)) &&
3454 new_cursor != sheet->cursor_drag->type)
3456 gdk_cursor_unref (sheet->cursor_drag);
3457 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
3458 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3461 new_cursor = GDK_SIZING;
3462 if ( event->window == sheet->sheet_window &&
3463 sheet->selection_mode != GTK_SELECTION_NONE &&
3464 !PSPPIRE_SHEET_IN_DRAG (sheet) &&
3465 (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
3466 PSPPIRE_SHEET_IN_RESIZE (sheet)) &&
3467 new_cursor != sheet->cursor_drag->type)
3469 gdk_cursor_unref (sheet->cursor_drag);
3470 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_SIZING);
3471 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3475 gdk_window_get_pointer (widget->window, &x, &y, &mods);
3476 if (! (mods & GDK_BUTTON1_MASK)) return FALSE;
3478 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3480 if (event->x != sheet->x_drag)
3482 draw_xor_vline (sheet);
3483 sheet->x_drag = event->x;
3484 draw_xor_vline (sheet);
3490 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3492 if (event->y != sheet->y_drag)
3494 draw_xor_hline (sheet);
3495 sheet->y_drag = event->y;
3496 draw_xor_hline (sheet);
3502 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3504 PsppireSheetRange aux;
3505 column = column_from_xpixel (sheet, x)- sheet->drag_cell.col;
3506 row = row_from_ypixel (sheet, y) - sheet->drag_cell.row;
3507 if (sheet->select_status == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
3508 if (sheet->select_status == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
3512 if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
3513 aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
3515 aux = sheet->drag_range;
3516 sheet->drag_range.row0 = sheet->range.row0 + row;
3517 sheet->drag_range.col0 = sheet->range.col0 + column;
3518 sheet->drag_range.rowi = sheet->range.rowi + row;
3519 sheet->drag_range.coli = sheet->range.coli + column;
3520 if (aux.row0 != sheet->drag_range.row0 ||
3521 aux.col0 != sheet->drag_range.col0)
3523 draw_xor_rectangle (sheet, aux);
3524 draw_xor_rectangle (sheet, sheet->drag_range);
3530 if (PSPPIRE_SHEET_IN_RESIZE (sheet))
3532 PsppireSheetRange aux;
3533 gint v_h, current_col, current_row, col_threshold, row_threshold;
3535 if (abs (x - psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)) >
3536 abs (y - psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row))) v_h = 2;
3538 current_col = column_from_xpixel (sheet, x);
3539 current_row = row_from_ypixel (sheet, y);
3540 column = current_col - sheet->drag_cell.col;
3541 row = current_row - sheet->drag_cell.row;
3543 /*use half of column width resp. row height as threshold to
3545 col_threshold = psppire_axis_start_pixel (sheet->haxis, current_col) +
3546 psppire_axis_unit_size (sheet->haxis, current_col) / 2;
3549 if (x < col_threshold)
3552 else if (column < 0)
3554 if (x > col_threshold)
3557 row_threshold = psppire_axis_start_pixel (sheet->vaxis, current_row) +
3558 psppire_axis_unit_size (sheet->vaxis, current_row)/2;
3561 if (y < row_threshold)
3566 if (y > row_threshold)
3570 if (sheet->select_status == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
3571 if (sheet->select_status == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
3581 if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
3582 aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
3584 aux = sheet->drag_range;
3585 sheet->drag_range = sheet->range;
3587 if (row < 0) sheet->drag_range.row0 = sheet->range.row0 + row;
3588 if (row > 0) sheet->drag_range.rowi = sheet->range.rowi + row;
3589 if (column < 0) sheet->drag_range.col0 = sheet->range.col0 + column;
3590 if (column > 0) sheet->drag_range.coli = sheet->range.coli + column;
3592 if (aux.row0 != sheet->drag_range.row0 ||
3593 aux.rowi != sheet->drag_range.rowi ||
3594 aux.col0 != sheet->drag_range.col0 ||
3595 aux.coli != sheet->drag_range.coli)
3597 draw_xor_rectangle (sheet, aux);
3598 draw_xor_rectangle (sheet, sheet->drag_range);
3604 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
3606 if (sheet->select_status == PSPPIRE_SHEET_NORMAL && row == sheet->active_cell.row &&
3607 column == sheet->active_cell.col) return TRUE;
3609 if ( mods & GDK_BUTTON1_MASK)
3611 if (PSPPIRE_SHEET_IN_SELECTION (sheet) )
3613 /* Redraw the old range */
3614 psppire_sheet_unselect_range (sheet);
3616 sheet->range.rowi = row;
3617 sheet->range.coli = column;
3619 /* Redraw the new range */
3620 psppire_sheet_select_range (sheet, &sheet->range);
3624 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3632 psppire_sheet_crossing_notify (GtkWidget *widget,
3633 GdkEventCrossing *event)
3635 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3637 if (event->window == sheet->column_title_window)
3638 sheet->column_title_under = event->type == GDK_ENTER_NOTIFY;
3639 else if (event->window == sheet->row_title_window)
3640 sheet->row_title_under = event->type == GDK_ENTER_NOTIFY;
3642 if (event->type == GDK_LEAVE_NOTIFY)
3643 gtk_widget_hide (sheet->hover_window->window);
3650 psppire_sheet_focus_in (GtkWidget *w,
3651 GdkEventFocus *event)
3653 PsppireSheet *sheet = PSPPIRE_SHEET (w);
3655 gtk_widget_grab_focus (sheet->entry_widget);
3663 psppire_sheet_entry_key_press (GtkWidget *widget,
3667 g_signal_emit_by_name (widget, "key_press_event", key, &focus);
3672 /* Number of rows in a step-increment */
3673 #define ROWS_PER_STEP 1
3677 page_vertical (PsppireSheet *sheet, GtkScrollType dir)
3679 gint old_row = sheet->active_cell.row ;
3680 glong vpixel = psppire_axis_start_pixel (sheet->vaxis, old_row);
3684 vpixel -= psppire_axis_start_pixel (sheet->vaxis,
3685 min_visible_row (sheet));
3689 case GTK_SCROLL_PAGE_DOWN:
3690 gtk_adjustment_set_value (sheet->vadjustment,
3691 sheet->vadjustment->value +
3692 sheet->vadjustment->page_increment);
3694 case GTK_SCROLL_PAGE_UP:
3695 gtk_adjustment_set_value (sheet->vadjustment,
3696 sheet->vadjustment->value -
3697 sheet->vadjustment->page_increment);
3701 g_assert_not_reached ();
3706 vpixel += psppire_axis_start_pixel (sheet->vaxis,
3707 min_visible_row (sheet));
3709 new_row = row_from_ypixel (sheet, vpixel);
3711 change_active_cell (sheet, new_row,
3712 sheet->active_cell.col);
3717 step_sheet (PsppireSheet *sheet, GtkScrollType dir)
3719 gint current_row = sheet->active_cell.row;
3720 gint current_col = sheet->active_cell.col;
3721 PsppireSheetCell new_cell ;
3722 gboolean forbidden = FALSE;
3724 new_cell.row = current_row;
3725 new_cell.col = current_col;
3729 case GTK_SCROLL_STEP_DOWN:
3732 case GTK_SCROLL_STEP_UP:
3735 case GTK_SCROLL_STEP_RIGHT:
3738 case GTK_SCROLL_STEP_LEFT:
3741 case GTK_SCROLL_STEP_FORWARD:
3744 psppire_sheet_model_get_column_count (sheet->model))
3750 case GTK_SCROLL_STEP_BACKWARD:
3752 if (new_cell.col < 0)
3755 psppire_sheet_model_get_column_count (sheet->model) - 1;
3760 g_assert_not_reached ();
3764 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
3765 &sheet->active_cell,
3773 maximize_int (&new_cell.row, 0);
3774 maximize_int (&new_cell.col, 0);
3776 minimize_int (&new_cell.row,
3777 psppire_axis_unit_count (sheet->vaxis) - 1);
3779 minimize_int (&new_cell.col,
3780 psppire_axis_unit_count (sheet->haxis) - 1);
3782 change_active_cell (sheet, new_cell.row, new_cell.col);
3785 if ( new_cell.col > max_fully_visible_column (sheet))
3788 psppire_axis_start_pixel (sheet->haxis,
3790 hpos -= sheet->hadjustment->page_size;
3792 gtk_adjustment_set_value (sheet->hadjustment,
3795 else if ( new_cell.col < min_fully_visible_column (sheet))
3798 psppire_axis_start_pixel (sheet->haxis,
3801 gtk_adjustment_set_value (sheet->hadjustment,
3806 if ( new_cell.row > max_fully_visible_row (sheet))
3809 psppire_axis_start_pixel (sheet->vaxis,
3811 vpos -= sheet->vadjustment->page_size;
3813 gtk_adjustment_set_value (sheet->vadjustment,
3816 else if ( new_cell.row < min_fully_visible_row (sheet))
3819 psppire_axis_start_pixel (sheet->vaxis,
3822 gtk_adjustment_set_value (sheet->vadjustment,
3826 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
3831 psppire_sheet_key_press (GtkWidget *widget,
3834 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3836 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3838 switch (key->keyval)
3841 step_sheet (sheet, GTK_SCROLL_STEP_FORWARD);
3844 step_sheet (sheet, GTK_SCROLL_STEP_RIGHT);
3846 case GDK_ISO_Left_Tab:
3847 step_sheet (sheet, GTK_SCROLL_STEP_BACKWARD);
3850 step_sheet (sheet, GTK_SCROLL_STEP_LEFT);
3854 step_sheet (sheet, GTK_SCROLL_STEP_DOWN);
3857 step_sheet (sheet, GTK_SCROLL_STEP_UP);
3861 page_vertical (sheet, GTK_SCROLL_PAGE_DOWN);
3864 page_vertical (sheet, GTK_SCROLL_PAGE_UP);
3868 gtk_adjustment_set_value (sheet->vadjustment,
3869 sheet->vadjustment->lower);
3871 change_active_cell (sheet, 0,
3872 sheet->active_cell.col);
3877 gtk_adjustment_set_value (sheet->vadjustment,
3878 sheet->vadjustment->upper -
3879 sheet->vadjustment->page_size -
3880 sheet->vadjustment->page_increment);
3883 change_active_cellx (sheet,
3884 psppire_axis_unit_count (sheet->vaxis) - 1,
3885 sheet->active_cell.col);
3889 psppire_sheet_real_cell_clear (sheet, sheet->active_cell.row, sheet->active_cell.col);
3900 psppire_sheet_size_request (GtkWidget *widget,
3901 GtkRequisition *requisition)
3903 PsppireSheet *sheet;
3905 g_return_if_fail (widget != NULL);
3906 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
3907 g_return_if_fail (requisition != NULL);
3909 sheet = PSPPIRE_SHEET (widget);
3911 requisition->width = 3 * DEFAULT_COLUMN_WIDTH;
3912 requisition->height = 3 * DEFAULT_ROW_HEIGHT;
3914 /* compute the size of the column title area */
3915 if (sheet->column_titles_visible)
3916 requisition->height += sheet->column_title_area.height;
3918 /* compute the size of the row title area */
3919 if (sheet->row_titles_visible)
3920 requisition->width += sheet->row_title_area.width;
3925 psppire_sheet_size_allocate (GtkWidget *widget,
3926 GtkAllocation *allocation)
3928 PsppireSheet *sheet;
3929 GtkAllocation sheet_allocation;
3932 g_return_if_fail (widget != NULL);
3933 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
3934 g_return_if_fail (allocation != NULL);
3936 sheet = PSPPIRE_SHEET (widget);
3937 widget->allocation = *allocation;
3938 border_width = GTK_CONTAINER (widget)->border_width;
3940 if (GTK_WIDGET_REALIZED (widget))
3941 gdk_window_move_resize (widget->window,
3942 allocation->x + border_width,
3943 allocation->y + border_width,
3944 allocation->width - 2 * border_width,
3945 allocation->height - 2 * border_width);
3947 sheet_allocation.x = 0;
3948 sheet_allocation.y = 0;
3949 sheet_allocation.width = allocation->width - 2 * border_width;
3950 sheet_allocation.height = allocation->height - 2 * border_width;
3952 if (GTK_WIDGET_REALIZED (widget))
3953 gdk_window_move_resize (sheet->sheet_window,
3956 sheet_allocation.width,
3957 sheet_allocation.height);
3959 /* position the window which holds the column title buttons */
3960 sheet->column_title_area.x = 0;
3961 sheet->column_title_area.y = 0;
3962 sheet->column_title_area.width = sheet_allocation.width ;
3965 /* position the window which holds the row title buttons */
3966 sheet->row_title_area.x = 0;
3967 sheet->row_title_area.y = 0;
3968 sheet->row_title_area.height = sheet_allocation.height;
3970 if (sheet->row_titles_visible)
3971 sheet->column_title_area.x += sheet->row_title_area.width;
3973 if (sheet->column_titles_visible)
3974 sheet->row_title_area.y += sheet->column_title_area.height;
3976 if (GTK_WIDGET_REALIZED (widget) && sheet->column_titles_visible)
3977 gdk_window_move_resize (sheet->column_title_window,
3978 sheet->column_title_area.x,
3979 sheet->column_title_area.y,
3980 sheet->column_title_area.width,
3981 sheet->column_title_area.height);
3984 if (GTK_WIDGET_REALIZED (widget) && sheet->row_titles_visible)
3985 gdk_window_move_resize (sheet->row_title_window,
3986 sheet->row_title_area.x,
3987 sheet->row_title_area.y,
3988 sheet->row_title_area.width,
3989 sheet->row_title_area.height);
3991 size_allocate_global_button (sheet);
3995 gint width = sheet->column_title_area.width;
3997 if ( sheet->row_titles_visible)
3998 width -= sheet->row_title_area.width;
4000 g_object_set (sheet->haxis,
4001 "minimum-extent", width,
4008 gint height = sheet->row_title_area.height;
4010 if ( sheet->column_titles_visible)
4011 height -= sheet->column_title_area.height;
4013 g_object_set (sheet->vaxis,
4014 "minimum-extent", height,
4019 /* set the scrollbars adjustments */
4020 adjust_scrollbars (sheet);
4024 draw_column_title_buttons (PsppireSheet *sheet)
4028 if (!sheet->column_titles_visible) return;
4029 if (!GTK_WIDGET_REALIZED (sheet))
4032 gdk_drawable_get_size (sheet->sheet_window, &width, NULL);
4035 if (sheet->row_titles_visible)
4037 x = sheet->row_title_area.width;
4040 if (sheet->column_title_area.width != width || sheet->column_title_area.x != x)
4042 sheet->column_title_area.width = width;
4043 sheet->column_title_area.x = x;
4044 gdk_window_move_resize (sheet->column_title_window,
4045 sheet->column_title_area.x,
4046 sheet->column_title_area.y,
4047 sheet->column_title_area.width,
4048 sheet->column_title_area.height);
4051 if (max_visible_column (sheet) ==
4052 psppire_axis_unit_count (sheet->haxis) - 1)
4053 gdk_window_clear_area (sheet->column_title_window,
4055 sheet->column_title_area.width,
4056 sheet->column_title_area.height);
4058 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
4060 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
4061 max_visible_column (sheet));
4065 draw_row_title_buttons (PsppireSheet *sheet)
4070 if (!sheet->row_titles_visible) return;
4071 if (!GTK_WIDGET_REALIZED (sheet))
4074 gdk_drawable_get_size (sheet->sheet_window, NULL, &height);
4076 if (sheet->column_titles_visible)
4078 y = sheet->column_title_area.height;
4081 if (sheet->row_title_area.height != height || sheet->row_title_area.y != y)
4083 sheet->row_title_area.y = y;
4084 sheet->row_title_area.height = height;
4085 gdk_window_move_resize (sheet->row_title_window,
4086 sheet->row_title_area.x,
4087 sheet->row_title_area.y,
4088 sheet->row_title_area.width,
4089 sheet->row_title_area.height);
4092 if (max_visible_row (sheet) == psppire_axis_unit_count (sheet->vaxis) - 1)
4093 gdk_window_clear_area (sheet->row_title_window,
4095 sheet->row_title_area.width,
4096 sheet->row_title_area.height);
4098 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
4100 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
4101 max_visible_row (sheet));
4106 psppire_sheet_size_allocate_entry (PsppireSheet *sheet)
4108 GtkAllocation entry_alloc;
4109 PsppireSheetCellAttr attributes = { 0 };
4110 GtkEntry *sheet_entry;
4112 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4113 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
4115 sheet_entry = psppire_sheet_get_entry (sheet);
4117 if ( ! psppire_sheet_get_attributes (sheet, sheet->active_cell.row,
4118 sheet->active_cell.col,
4122 if ( GTK_WIDGET_REALIZED (sheet->entry_widget) )
4124 GtkStyle *style = GTK_WIDGET (sheet_entry)->style;
4126 style->bg[GTK_STATE_NORMAL] = attributes.background;
4127 style->fg[GTK_STATE_NORMAL] = attributes.foreground;
4128 style->text[GTK_STATE_NORMAL] = attributes.foreground;
4129 style->bg[GTK_STATE_ACTIVE] = attributes.background;
4130 style->fg[GTK_STATE_ACTIVE] = attributes.foreground;
4131 style->text[GTK_STATE_ACTIVE] = attributes.foreground;
4134 rectangle_from_cell (sheet, sheet->active_cell.row,
4135 sheet->active_cell.col, &entry_alloc);
4137 entry_alloc.x += sheet->cell_padding->left;
4138 entry_alloc.y += sheet->cell_padding->right;
4139 entry_alloc.width -= sheet->cell_padding->left + sheet->cell_padding->right;
4140 entry_alloc.height -= sheet->cell_padding->top + sheet->cell_padding->bottom;
4143 gtk_widget_set_size_request (sheet->entry_widget, entry_alloc.width,
4144 entry_alloc.height);
4145 gtk_widget_size_allocate (sheet->entry_widget, &entry_alloc);
4149 /* Copy the sheet's font to the entry widget */
4151 set_entry_widget_font (PsppireSheet *sheet)
4153 GtkRcStyle *style = gtk_widget_get_modifier_style (sheet->entry_widget);
4155 pango_font_description_free (style->font_desc);
4156 style->font_desc = pango_font_description_copy (GTK_WIDGET (sheet)->style->font_desc);
4158 gtk_widget_modify_style (sheet->entry_widget, style);
4162 create_sheet_entry (PsppireSheet *sheet)
4164 if (sheet->entry_widget)
4166 gtk_widget_unparent (sheet->entry_widget);
4169 sheet->entry_widget = g_object_new (sheet->entry_type, NULL);
4170 g_object_ref_sink (sheet->entry_widget);
4172 gtk_widget_size_request (sheet->entry_widget, NULL);
4174 if ( GTK_IS_ENTRY (sheet->entry_widget))
4176 g_object_set (sheet->entry_widget,
4181 if (GTK_WIDGET_REALIZED (sheet))
4183 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
4184 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
4185 gtk_widget_realize (sheet->entry_widget);
4188 g_signal_connect_swapped (sheet->entry_widget, "key_press_event",
4189 G_CALLBACK (psppire_sheet_entry_key_press),
4192 set_entry_widget_font (sheet);
4194 gtk_widget_show (sheet->entry_widget);
4198 /* Finds the last child widget that happens to be of type GtkEntry */
4200 find_entry (GtkWidget *w, gpointer user_data)
4202 GtkWidget **entry = user_data;
4203 if ( GTK_IS_ENTRY (w))
4211 psppire_sheet_get_entry (PsppireSheet *sheet)
4213 GtkWidget *w = sheet->entry_widget;
4215 g_return_val_if_fail (sheet != NULL, NULL);
4216 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
4217 g_return_val_if_fail (sheet->entry_widget != NULL, NULL);
4219 while (! GTK_IS_ENTRY (w))
4221 GtkWidget *entry = NULL;
4223 if (GTK_IS_CONTAINER (w))
4225 gtk_container_forall (GTK_CONTAINER (w), find_entry, &entry);
4234 return GTK_ENTRY (w);
4239 draw_button (PsppireSheet *sheet, GdkWindow *window,
4240 PsppireSheetButton *button, gboolean is_sensitive,
4241 GdkRectangle allocation)
4243 GtkShadowType shadow_type;
4244 gint text_width = 0, text_height = 0;
4245 PangoAlignment align = PANGO_ALIGN_LEFT;
4251 g_return_if_fail (sheet != NULL);
4252 g_return_if_fail (button != NULL);
4255 rtl = gtk_widget_get_direction (GTK_WIDGET (sheet)) == GTK_TEXT_DIR_RTL;
4257 gdk_window_clear_area (window,
4258 allocation.x, allocation.y,
4259 allocation.width, allocation.height);
4261 gtk_widget_ensure_style (sheet->button);
4263 gtk_paint_box (sheet->button->style, window,
4264 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
4266 GTK_WIDGET (sheet->button),
4268 allocation.x, allocation.y,
4269 allocation.width, allocation.height);
4271 state = button->state;
4272 if (!is_sensitive) state = GTK_STATE_INSENSITIVE;
4274 if (state == GTK_STATE_ACTIVE)
4275 shadow_type = GTK_SHADOW_IN;
4277 shadow_type = GTK_SHADOW_OUT;
4279 if (state != GTK_STATE_NORMAL && state != GTK_STATE_INSENSITIVE)
4280 gtk_paint_box (sheet->button->style, window,
4281 button->state, shadow_type,
4282 &allocation, GTK_WIDGET (sheet->button),
4284 allocation.x, allocation.y,
4285 allocation.width, allocation.height);
4287 if ( button->overstruck)
4289 GdkPoint points[2] = {
4290 {allocation.x, allocation.y},
4291 {allocation.x + allocation.width,
4292 allocation.y + allocation.height}
4295 gtk_paint_polygon (sheet->button->style,
4307 if (button->label_visible)
4309 text_height = DEFAULT_ROW_HEIGHT -
4310 2 * COLUMN_TITLES_HEIGHT;
4312 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4314 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc,
4317 allocation.y += 2 * sheet->button->style->ythickness;
4319 if (button->label && strlen (button->label) > 0)
4321 PangoRectangle rect;
4322 gchar *line = button->label;
4324 PangoLayout *layout = NULL;
4325 gint real_x = allocation.x;
4326 gint real_y = allocation.y;
4328 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), line);
4329 pango_layout_get_extents (layout, NULL, &rect);
4331 text_width = PANGO_PIXELS (rect.width);
4332 switch (button->justification)
4334 case GTK_JUSTIFY_LEFT:
4335 real_x = allocation.x + COLUMN_TITLES_HEIGHT;
4336 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4338 case GTK_JUSTIFY_RIGHT:
4339 real_x = allocation.x + allocation.width - text_width - COLUMN_TITLES_HEIGHT;
4340 align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
4342 case GTK_JUSTIFY_CENTER:
4344 real_x = allocation.x + (allocation.width - text_width)/2;
4345 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4346 pango_layout_set_justify (layout, TRUE);
4348 pango_layout_set_alignment (layout, align);
4349 gtk_paint_layout (GTK_WIDGET (sheet)->style,
4358 g_object_unref (layout);
4361 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4363 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc, NULL);
4367 psppire_sheet_button_free (button);
4371 /* Draw the column title buttons FIRST through to LAST */
4373 draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4377 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4379 if (!sheet->column_titles_visible) return;
4381 g_return_if_fail (first >= min_visible_column (sheet));
4382 g_return_if_fail (last <= max_visible_column (sheet));
4385 rect.height = sheet->column_title_area.height;
4386 rect.x = psppire_axis_start_pixel (sheet->haxis, first) + CELL_SPACING;
4387 rect.width = psppire_axis_start_pixel (sheet->haxis, last) + CELL_SPACING
4388 + psppire_axis_unit_size (sheet->haxis, last);
4390 rect.x -= sheet->hadjustment->value;
4392 minimize_int (&rect.width, sheet->column_title_area.width);
4393 maximize_int (&rect.x, 0);
4395 gdk_window_begin_paint_rect (sheet->column_title_window, &rect);
4397 for (col = first ; col <= last ; ++col)
4399 GdkRectangle allocation;
4400 gboolean is_sensitive = FALSE;
4402 PsppireSheetButton *
4403 button = psppire_sheet_model_get_column_button (sheet->model, col);
4405 allocation.x = psppire_axis_start_pixel (sheet->haxis, col)
4407 allocation.x -= sheet->hadjustment->value;
4409 allocation.height = sheet->column_title_area.height;
4410 allocation.width = psppire_axis_unit_size (sheet->haxis, col);
4411 is_sensitive = psppire_sheet_model_get_column_sensitivity (sheet->model, col);
4413 draw_button (sheet, sheet->column_title_window,
4414 button, is_sensitive, allocation);
4417 gdk_window_end_paint (sheet->column_title_window);
4422 draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4426 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4428 if (!sheet->row_titles_visible) return;
4430 g_return_if_fail (first >= min_visible_row (sheet));
4431 g_return_if_fail (last <= max_visible_row (sheet));
4434 rect.width = sheet->row_title_area.width;
4435 rect.y = psppire_axis_start_pixel (sheet->vaxis, first) + CELL_SPACING;
4436 rect.height = psppire_axis_start_pixel (sheet->vaxis, last) + CELL_SPACING
4437 + psppire_axis_unit_size (sheet->vaxis, last);
4439 rect.y -= sheet->vadjustment->value;
4441 minimize_int (&rect.height, sheet->row_title_area.height);
4442 maximize_int (&rect.y, 0);
4444 gdk_window_begin_paint_rect (sheet->row_title_window, &rect);
4445 for (row = first; row <= last; ++row)
4447 GdkRectangle allocation;
4449 gboolean is_sensitive = FALSE;
4451 PsppireSheetButton *button =
4452 psppire_sheet_model_get_row_button (sheet->model, row);
4454 allocation.y = psppire_axis_start_pixel (sheet->vaxis, row)
4456 allocation.y -= sheet->vadjustment->value;
4458 allocation.width = sheet->row_title_area.width;
4459 allocation.height = psppire_axis_unit_size (sheet->vaxis, row);
4460 is_sensitive = psppire_sheet_model_get_row_sensitivity (sheet->model, row);
4462 draw_button (sheet, sheet->row_title_window,
4463 button, is_sensitive, allocation);
4466 gdk_window_end_paint (sheet->row_title_window);
4473 * vadjustment_value_changed
4474 * hadjustment_value_changed */
4478 update_adjustment (GtkAdjustment *adj, PsppireAxis *axis, gint page_size)
4481 (adj->value + adj->page_size)
4483 (adj->upper - adj->lower);
4485 const glong last_item = psppire_axis_unit_count (axis) - 1;
4487 if (isnan (position) || position < 0)
4491 psppire_axis_start_pixel (axis, last_item)
4493 psppire_axis_unit_size (axis, last_item)
4497 adj->page_size = page_size;
4500 adj->value = position * (adj->upper - adj->lower) - adj->page_size;
4502 if ( adj->value < adj->lower)
4503 adj->value = adj->lower;
4506 gtk_adjustment_changed (adj);
4511 adjust_scrollbars (PsppireSheet *sheet)
4515 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4518 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
4520 if ( sheet->row_titles_visible)
4521 width -= sheet->row_title_area.width;
4523 if (sheet->column_titles_visible)
4524 height -= sheet->column_title_area.height;
4526 if (sheet->vadjustment)
4528 glong last_row = psppire_axis_unit_count (sheet->vaxis) - 1;
4530 sheet->vadjustment->step_increment =
4532 psppire_axis_unit_size (sheet->vaxis, last_row);
4534 sheet->vadjustment->page_increment =
4536 sheet->column_title_area.height -
4537 psppire_axis_unit_size (sheet->vaxis, last_row);
4539 update_adjustment (sheet->vadjustment, sheet->vaxis, height);
4542 if (sheet->hadjustment)
4544 gint last_col = psppire_axis_unit_count (sheet->haxis) - 1;
4545 sheet->hadjustment->step_increment = 1;
4547 sheet->hadjustment->page_increment = width;
4549 sheet->hadjustment->upper =
4550 psppire_axis_start_pixel (sheet->haxis, last_col)
4552 psppire_axis_unit_size (sheet->haxis, last_col)
4555 update_adjustment (sheet->hadjustment, sheet->haxis, width);
4559 /* Subtracts the region of WIDGET from REGION */
4561 subtract_widget_region (GdkRegion *region, GtkWidget *widget)
4564 GdkRectangle intersect;
4567 gdk_region_get_clipbox (region, &rect);
4568 gtk_widget_intersect (widget,
4572 region2 = gdk_region_rectangle (&intersect);
4573 gdk_region_subtract (region, region2);
4574 gdk_region_destroy (region2);
4578 vadjustment_value_changed (GtkAdjustment *adjustment,
4582 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4584 g_return_if_fail (adjustment != NULL);
4586 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
4588 gtk_widget_hide (sheet->entry_widget);
4591 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
4593 subtract_widget_region (region, sheet->button);
4594 gdk_window_begin_paint_region (sheet->sheet_window, region);
4596 draw_sheet_region (sheet, region);
4598 draw_row_title_buttons (sheet);
4599 psppire_sheet_draw_active_cell (sheet);
4601 gdk_window_end_paint (sheet->sheet_window);
4602 gdk_region_destroy (region);
4607 hadjustment_value_changed (GtkAdjustment *adjustment,
4611 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4613 g_return_if_fail (adjustment != NULL);
4615 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
4617 gtk_widget_hide (sheet->entry_widget);
4621 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
4623 subtract_widget_region (region, sheet->button);
4624 gdk_window_begin_paint_region (sheet->sheet_window, region);
4626 draw_sheet_region (sheet, region);
4628 draw_column_title_buttons (sheet);
4630 psppire_sheet_draw_active_cell (sheet);
4632 gdk_window_end_paint (sheet->sheet_window);
4634 gdk_region_destroy (region);
4638 /* COLUMN RESIZING */
4640 draw_xor_vline (PsppireSheet *sheet)
4643 gint xpos = sheet->x_drag;
4644 gdk_drawable_get_size (sheet->sheet_window,
4647 if (sheet->row_titles_visible)
4648 xpos += sheet->row_title_area.width;
4650 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
4652 sheet->column_title_area.height,
4654 height + CELL_SPACING);
4659 draw_xor_hline (PsppireSheet *sheet)
4663 gint ypos = sheet->y_drag;
4665 gdk_drawable_get_size (sheet->sheet_window,
4669 if (sheet->column_titles_visible)
4670 ypos += sheet->column_title_area.height;
4672 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
4673 sheet->row_title_area.width,
4675 width + CELL_SPACING,
4679 /* SELECTED RANGE */
4681 draw_xor_rectangle (PsppireSheet *sheet, PsppireSheetRange range)
4684 GdkRectangle clip_area, area;
4687 area.x = psppire_axis_start_pixel (sheet->haxis, range.col0);
4688 area.y = psppire_axis_start_pixel (sheet->vaxis, range.row0);
4689 area.width = psppire_axis_start_pixel (sheet->haxis, range.coli)- area.x+
4690 psppire_axis_unit_size (sheet->haxis, range.coli);
4691 area.height = psppire_axis_start_pixel (sheet->vaxis, range.rowi)- area.y +
4692 psppire_axis_unit_size (sheet->vaxis, range.rowi);
4694 clip_area.x = sheet->row_title_area.width;
4695 clip_area.y = sheet->column_title_area.height;
4697 gdk_drawable_get_size (sheet->sheet_window,
4698 &clip_area.width, &clip_area.height);
4700 if (!sheet->row_titles_visible) clip_area.x = 0;
4701 if (!sheet->column_titles_visible) clip_area.y = 0;
4705 area.width = area.width + area.x;
4708 if (area.width > clip_area.width) area.width = clip_area.width + 10;
4711 area.height = area.height + area.y;
4714 if (area.height > clip_area.height) area.height = clip_area.height + 10;
4718 clip_area.width += 3;
4719 clip_area.height += 3;
4721 gdk_gc_get_values (sheet->xor_gc, &values);
4723 gdk_gc_set_clip_rectangle (sheet->xor_gc, &clip_area);
4725 gdk_draw_rectangle (sheet->sheet_window,
4728 area.x + i, area.y + i,
4729 area.width - 2 * i, area.height - 2 * i);
4732 gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
4734 gdk_gc_set_foreground (sheet->xor_gc, &values.foreground);
4739 set_column_width (PsppireSheet *sheet,
4743 g_return_if_fail (sheet != NULL);
4744 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
4746 if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis))
4752 psppire_axis_resize (sheet->haxis, column,
4753 width - sheet->cell_padding->left -
4754 sheet->cell_padding->right);
4756 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4758 draw_column_title_buttons (sheet);
4759 adjust_scrollbars (sheet);
4760 psppire_sheet_size_allocate_entry (sheet);
4761 redraw_range (sheet, NULL);
4766 set_row_height (PsppireSheet *sheet,
4770 g_return_if_fail (sheet != NULL);
4771 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
4773 if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis))
4779 psppire_axis_resize (sheet->vaxis, row,
4780 height - sheet->cell_padding->top -
4781 sheet->cell_padding->bottom);
4783 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) )
4785 draw_row_title_buttons (sheet);
4786 adjust_scrollbars (sheet);
4787 psppire_sheet_size_allocate_entry (sheet);
4788 redraw_range (sheet, NULL);
4793 psppire_sheet_get_attributes (const PsppireSheet *sheet, gint row, gint col,
4794 PsppireSheetCellAttr *attr)
4797 const GtkJustification *j ;
4798 GdkColormap *colormap;
4800 g_return_val_if_fail (sheet != NULL, FALSE);
4801 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), FALSE);
4803 if (row < 0 || col < 0) return FALSE;
4805 attr->foreground = GTK_WIDGET (sheet)->style->black;
4806 attr->background = sheet->color[BG_COLOR];
4808 attr->border.width = 0;
4809 attr->border.line_style = GDK_LINE_SOLID;
4810 attr->border.cap_style = GDK_CAP_NOT_LAST;
4811 attr->border.join_style = GDK_JOIN_MITER;
4812 attr->border.mask = 0;
4813 attr->border.color = GTK_WIDGET (sheet)->style->black;
4815 colormap = gtk_widget_get_colormap (GTK_WIDGET (sheet));
4816 fg = psppire_sheet_model_get_foreground (sheet->model, row, col);
4819 gdk_colormap_alloc_color (colormap, fg, TRUE, TRUE);
4820 attr->foreground = *fg;
4823 bg = psppire_sheet_model_get_background (sheet->model, row, col);
4826 gdk_colormap_alloc_color (colormap, bg, TRUE, TRUE);
4827 attr->background = *bg;
4830 attr->justification =
4831 psppire_sheet_model_get_column_justification (sheet->model, col);
4833 j = psppire_sheet_model_get_justification (sheet->model, row, col);
4835 attr->justification = *j;
4841 psppire_sheet_button_size_request (PsppireSheet *sheet,
4842 const PsppireSheetButton *button,
4843 GtkRequisition *button_requisition)
4845 GtkRequisition requisition;
4846 GtkRequisition label_requisition;
4848 label_requisition.height = DEFAULT_ROW_HEIGHT;
4849 label_requisition.width = COLUMN_MIN_WIDTH;
4851 requisition.height = DEFAULT_ROW_HEIGHT;
4852 requisition.width = COLUMN_MIN_WIDTH;
4855 *button_requisition = requisition;
4856 button_requisition->width = MAX (requisition.width, label_requisition.width);
4857 button_requisition->height = MAX (requisition.height, label_requisition.height);
4862 psppire_sheet_forall (GtkContainer *container,
4863 gboolean include_internals,
4864 GtkCallback callback,
4865 gpointer callback_data)
4867 PsppireSheet *sheet = PSPPIRE_SHEET (container);
4869 g_return_if_fail (callback != NULL);
4871 if (sheet->button && sheet->button->parent)
4872 (* callback) (sheet->button, callback_data);
4874 if (sheet->entry_widget && GTK_IS_CONTAINER (sheet->entry_widget))
4875 (* callback) (sheet->entry_widget, callback_data);
4880 psppire_sheet_get_model (const PsppireSheet *sheet)
4882 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
4884 return sheet->model;
4888 PsppireSheetButton *
4889 psppire_sheet_button_new (void)
4891 PsppireSheetButton *button = g_malloc (sizeof (PsppireSheetButton));
4893 button->state = GTK_STATE_NORMAL;
4894 button->label = NULL;
4895 button->label_visible = TRUE;
4896 button->justification = GTK_JUSTIFY_FILL;
4897 button->overstruck = FALSE;
4904 psppire_sheet_button_free (PsppireSheetButton *button)
4906 if (!button) return ;
4908 g_free (button->label);
4913 append_cell_text (GString *string, const PsppireSheet *sheet, gint r, gint c)
4915 gchar *celltext = psppire_sheet_cell_get_text (sheet, r, c);
4917 if ( NULL == celltext)
4920 g_string_append (string, celltext);
4926 range_to_text (const PsppireSheet *sheet)
4931 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
4934 string = g_string_sized_new (80);
4936 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
4938 for (c = sheet->range.col0; c < sheet->range.coli; ++c)
4940 append_cell_text (string, sheet, r, c);
4941 g_string_append (string, "\t");
4943 append_cell_text (string, sheet, r, c);
4944 if ( r < sheet->range.rowi)
4945 g_string_append (string, "\n");
4952 range_to_html (const PsppireSheet *sheet)
4957 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
4960 string = g_string_sized_new (480);
4962 g_string_append (string, "<html>\n");
4963 g_string_append (string, "<body>\n");
4964 g_string_append (string, "<table>\n");
4965 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
4967 g_string_append (string, "<tr>\n");
4968 for (c = sheet->range.col0; c <= sheet->range.coli; ++c)
4970 g_string_append (string, "<td>");
4971 append_cell_text (string, sheet, r, c);
4972 g_string_append (string, "</td>\n");
4974 g_string_append (string, "</tr>\n");
4976 g_string_append (string, "</table>\n");
4977 g_string_append (string, "</body>\n");
4978 g_string_append (string, "</html>\n");
4990 primary_get_cb (GtkClipboard *clipboard,
4991 GtkSelectionData *selection_data,
4995 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4996 GString *string = NULL;
5000 case SELECT_FMT_TEXT:
5001 string = range_to_text (sheet);
5003 case SELECT_FMT_HTML:
5004 string = range_to_html (sheet);
5007 g_assert_not_reached ();
5010 gtk_selection_data_set (selection_data, selection_data->target,
5012 (const guchar *) string->str, string->len);
5013 g_string_free (string, TRUE);
5017 primary_clear_cb (GtkClipboard *clipboard,
5020 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5021 if ( ! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
5024 psppire_sheet_unselect_range (sheet);
5028 psppire_sheet_update_primary_selection (PsppireSheet *sheet)
5030 static const GtkTargetEntry targets[] = {
5031 { "UTF8_STRING", 0, SELECT_FMT_TEXT },
5032 { "STRING", 0, SELECT_FMT_TEXT },
5033 { "TEXT", 0, SELECT_FMT_TEXT },
5034 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
5035 { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
5036 { "text/plain", 0, SELECT_FMT_TEXT },
5037 { "text/html", 0, SELECT_FMT_HTML }
5040 GtkClipboard *clipboard;
5042 if (!GTK_WIDGET_REALIZED (sheet))
5045 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (sheet),
5046 GDK_SELECTION_PRIMARY);
5048 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
5050 if (!gtk_clipboard_set_with_owner (clipboard, targets,
5051 G_N_ELEMENTS (targets),
5052 primary_get_cb, primary_clear_cb,
5054 primary_clear_cb (clipboard, sheet);
5058 if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (sheet))
5059 gtk_clipboard_clear (clipboard);