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->select_status = PSPPIRE_SHEET_NORMAL;
1134 GTK_WIDGET_UNSET_FLAGS (sheet, GTK_NO_WINDOW);
1135 GTK_WIDGET_SET_FLAGS (sheet, GTK_CAN_FOCUS);
1137 sheet->column_title_window = NULL;
1138 sheet->column_title_area.x = 0;
1139 sheet->column_title_area.y = 0;
1140 sheet->column_title_area.width = 0;
1141 sheet->column_title_area.height = DEFAULT_ROW_HEIGHT;
1143 sheet->row_title_window = NULL;
1144 sheet->row_title_area.x = 0;
1145 sheet->row_title_area.y = 0;
1146 sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
1147 sheet->row_title_area.height = 0;
1150 sheet->active_cell.row = 0;
1151 sheet->active_cell.col = 0;
1153 sheet->range.row0 = 0;
1154 sheet->range.rowi = 0;
1155 sheet->range.col0 = 0;
1156 sheet->range.coli = 0;
1158 sheet->sheet_window = NULL;
1159 sheet->entry_widget = NULL;
1160 sheet->button = NULL;
1162 sheet->hadjustment = NULL;
1163 sheet->vadjustment = NULL;
1165 sheet->cursor_drag = NULL;
1167 sheet->xor_gc = NULL;
1168 sheet->fg_gc = NULL;
1169 sheet->bg_gc = NULL;
1172 sheet->show_grid = TRUE;
1174 sheet->motion_timer = 0;
1176 sheet->row_titles_visible = TRUE;
1177 sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
1179 sheet->column_titles_visible = TRUE;
1182 /* create sheet entry */
1183 sheet->entry_type = GTK_TYPE_ENTRY;
1184 create_sheet_entry (sheet);
1186 /* create global selection button */
1187 create_global_button (sheet);
1191 /* Cause RANGE to be redrawn. If RANGE is null, then the
1192 entire visible range will be redrawn.
1195 redraw_range (PsppireSheet *sheet, PsppireSheetRange *range)
1199 if ( ! GTK_WIDGET_REALIZED (sheet))
1202 if ( NULL != range )
1203 rectangle_from_range (sheet, range, &rect);
1206 GdkRegion *r = gdk_drawable_get_visible_region (sheet->sheet_window);
1207 gdk_region_get_clipbox (r, &rect);
1209 if ( sheet->column_titles_visible)
1211 rect.y += sheet->column_title_area.height;
1212 rect.height -= sheet->column_title_area.height;
1215 if ( sheet->row_titles_visible)
1217 rect.x += sheet->row_title_area.width;
1218 rect.width -= sheet->row_title_area.width;
1222 gdk_window_invalidate_rect (sheet->sheet_window, &rect, FALSE);
1226 /* Callback which occurs whenever columns are inserted / deleted in the model */
1228 columns_inserted_deleted_callback (PsppireSheetModel *model, gint first_column,
1232 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1234 PsppireSheetRange range;
1235 gint model_columns = psppire_sheet_model_get_column_count (model);
1238 /* Need to update all the columns starting from the first column and onwards.
1239 * Previous column are unchanged, so don't need to be updated.
1241 range.col0 = first_column;
1243 range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1244 range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1246 adjust_scrollbars (sheet);
1248 if (sheet->active_cell.col >= model_columns)
1249 change_active_cell (sheet, sheet->active_cell.row, model_columns - 1);
1251 draw_column_title_buttons_range (sheet,
1252 first_column, max_visible_column (sheet));
1255 redraw_range (sheet, &range);
1261 /* Callback which occurs whenever rows are inserted / deleted in the model */
1263 rows_inserted_deleted_callback (PsppireSheetModel *model, gint first_row,
1264 gint n_rows, gpointer data)
1266 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1268 PsppireSheetRange range;
1270 gint model_rows = psppire_sheet_model_get_row_count (model);
1272 /* Need to update all the rows starting from the first row and onwards.
1273 * Previous rows are unchanged, so don't need to be updated.
1275 range.row0 = first_row;
1277 range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1278 range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1280 adjust_scrollbars (sheet);
1282 if (sheet->active_cell.row >= model_rows)
1283 change_active_cell (sheet, model_rows - 1, sheet->active_cell.col);
1285 draw_row_title_buttons_range (sheet, first_row, max_visible_row (sheet));
1287 redraw_range (sheet, &range);
1291 If row0 or rowi are negative, then all rows will be updated.
1292 If col0 or coli are negative, then all columns will be updated.
1295 range_update_callback (PsppireSheetModel *m, gint row0, gint col0,
1296 gint rowi, gint coli, gpointer data)
1298 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1300 PsppireSheetRange range;
1307 if ( !GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1310 if ( ( row0 < 0 && col0 < 0 ) || ( rowi < 0 && coli < 0 ) )
1312 redraw_range (sheet, NULL);
1313 adjust_scrollbars (sheet);
1315 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
1316 max_visible_row (sheet));
1318 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
1319 max_visible_column (sheet));
1323 else if ( row0 < 0 || rowi < 0 )
1325 range.row0 = min_visible_row (sheet);
1326 range.rowi = max_visible_row (sheet);
1328 else if ( col0 < 0 || coli < 0 )
1330 range.col0 = min_visible_column (sheet);
1331 range.coli = max_visible_column (sheet);
1334 redraw_range (sheet, &range);
1339 * psppire_sheet_new:
1340 * @rows: initial number of rows
1341 * @columns: initial number of columns
1342 * @title: sheet title
1343 * @model: the model to use for the sheet data
1345 * Creates a new sheet widget with the given number of rows and columns.
1347 * Returns: the new sheet widget
1350 psppire_sheet_new (PsppireSheetModel *model)
1352 GtkWidget *widget = g_object_new (PSPPIRE_TYPE_SHEET,
1360 * psppire_sheet_set_model
1361 * @sheet: the sheet to set the model for
1362 * @model: the model to use for the sheet data
1364 * Sets the model for a PsppireSheet
1368 psppire_sheet_set_model (PsppireSheet *sheet, PsppireSheetModel *model)
1370 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1372 if (sheet->model ) g_object_unref (sheet->model);
1374 sheet->model = model;
1378 g_object_ref (model);
1380 sheet->update_handler_id = g_signal_connect (model, "range_changed",
1381 G_CALLBACK (range_update_callback),
1384 g_signal_connect (model, "rows_inserted",
1385 G_CALLBACK (rows_inserted_deleted_callback), sheet);
1387 g_signal_connect (model, "rows_deleted",
1388 G_CALLBACK (rows_inserted_deleted_callback), sheet);
1390 g_signal_connect (model, "columns_inserted",
1391 G_CALLBACK (columns_inserted_deleted_callback), sheet);
1393 g_signal_connect (model, "columns_deleted",
1394 G_CALLBACK (columns_inserted_deleted_callback), sheet);
1400 psppire_sheet_change_entry (PsppireSheet *sheet, GtkType entry_type)
1402 g_return_if_fail (sheet != NULL);
1403 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1405 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
1406 psppire_sheet_hide_entry_widget (sheet);
1408 sheet->entry_type = entry_type;
1410 create_sheet_entry (sheet);
1412 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
1413 psppire_sheet_show_entry_widget (sheet);
1417 psppire_sheet_show_grid (PsppireSheet *sheet, gboolean show)
1419 g_return_if_fail (sheet != NULL);
1420 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1422 if (show == sheet->show_grid) return;
1424 sheet->show_grid = show;
1426 redraw_range (sheet, NULL);
1430 psppire_sheet_grid_visible (PsppireSheet *sheet)
1432 g_return_val_if_fail (sheet != NULL, 0);
1433 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
1435 return sheet->show_grid;
1439 psppire_sheet_get_columns_count (PsppireSheet *sheet)
1441 g_return_val_if_fail (sheet != NULL, 0);
1442 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
1444 return psppire_axis_unit_count (sheet->haxis);
1447 static void set_column_width (PsppireSheet *sheet,
1453 psppire_sheet_show_column_titles (PsppireSheet *sheet)
1455 if (sheet->column_titles_visible) return;
1457 sheet->column_titles_visible = TRUE;
1459 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1462 gdk_window_show (sheet->column_title_window);
1463 gdk_window_move_resize (sheet->column_title_window,
1464 sheet->column_title_area.x,
1465 sheet->column_title_area.y,
1466 sheet->column_title_area.width,
1467 sheet->column_title_area.height);
1469 adjust_scrollbars (sheet);
1471 if (sheet->vadjustment)
1472 g_signal_emit_by_name (sheet->vadjustment,
1475 size_allocate_global_button (sheet);
1477 if ( sheet->row_titles_visible)
1478 gtk_widget_show (sheet->button);
1483 psppire_sheet_show_row_titles (PsppireSheet *sheet)
1485 if (sheet->row_titles_visible) return;
1487 sheet->row_titles_visible = TRUE;
1490 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1492 gdk_window_show (sheet->row_title_window);
1493 gdk_window_move_resize (sheet->row_title_window,
1494 sheet->row_title_area.x,
1495 sheet->row_title_area.y,
1496 sheet->row_title_area.width,
1497 sheet->row_title_area.height);
1499 adjust_scrollbars (sheet);
1502 if (sheet->hadjustment)
1503 g_signal_emit_by_name (sheet->hadjustment,
1506 size_allocate_global_button (sheet);
1508 if ( sheet->column_titles_visible)
1509 gtk_widget_show (sheet->button);
1513 psppire_sheet_hide_column_titles (PsppireSheet *sheet)
1515 if (!sheet->column_titles_visible) return;
1517 sheet->column_titles_visible = FALSE;
1519 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1521 if (sheet->column_title_window)
1522 gdk_window_hide (sheet->column_title_window);
1524 gtk_widget_hide (sheet->button);
1526 adjust_scrollbars (sheet);
1529 if (sheet->vadjustment)
1530 g_signal_emit_by_name (sheet->vadjustment,
1535 psppire_sheet_hide_row_titles (PsppireSheet *sheet)
1537 if (!sheet->row_titles_visible) return;
1539 sheet->row_titles_visible = FALSE;
1541 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1543 if (sheet->row_title_window)
1544 gdk_window_hide (sheet->row_title_window);
1546 gtk_widget_hide (sheet->button);
1548 adjust_scrollbars (sheet);
1551 if (sheet->hadjustment)
1552 g_signal_emit_by_name (sheet->hadjustment,
1557 /* Scroll the sheet so that the cell ROW, COLUMN is visible.
1558 If {ROW,COL}_ALIGN is zero, then the cell will be placed
1559 at the {top,left} of the sheet. If it's 1, then it'll
1560 be placed at the {bottom,right}.
1561 ROW or COL may be -1, in which case scrolling in that dimension
1565 psppire_sheet_moveto (PsppireSheet *sheet,
1573 g_return_if_fail (row_align >= 0);
1574 g_return_if_fail (col_align >= 0);
1576 g_return_if_fail (row_align <= 1);
1577 g_return_if_fail (col_align <= 1);
1579 g_return_if_fail (col <
1580 psppire_axis_unit_count (sheet->haxis));
1581 g_return_if_fail (row <
1582 psppire_axis_unit_count (sheet->vaxis));
1584 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
1589 gint y = psppire_axis_start_pixel (sheet->vaxis, row);
1591 gtk_adjustment_set_value (sheet->vadjustment, y - height * row_align);
1597 gint x = psppire_axis_start_pixel (sheet->haxis, col);
1599 gtk_adjustment_set_value (sheet->hadjustment, x - width * col_align);
1607 psppire_sheet_range_isvisible (const PsppireSheet *sheet,
1608 const PsppireSheetRange *range)
1610 g_return_val_if_fail (sheet != NULL, FALSE);
1612 if (range->row0 < 0 || range->row0 >= psppire_axis_unit_count (sheet->vaxis))
1615 if (range->rowi < 0 || range->rowi >= psppire_axis_unit_count (sheet->vaxis))
1618 if (range->col0 < 0 || range->col0 >= psppire_axis_unit_count (sheet->haxis))
1621 if (range->coli < 0 || range->coli >= psppire_axis_unit_count (sheet->haxis))
1624 if (range->rowi < min_visible_row (sheet))
1627 if (range->row0 > max_visible_row (sheet))
1630 if (range->coli < min_visible_column (sheet))
1633 if (range->col0 > max_visible_column (sheet))
1640 psppire_sheet_cell_isvisible (PsppireSheet *sheet,
1641 gint row, gint column)
1643 PsppireSheetRange range;
1646 range.col0 = column;
1648 range.coli = column;
1650 return psppire_sheet_range_isvisible (sheet, &range);
1654 psppire_sheet_get_visible_range (PsppireSheet *sheet, PsppireSheetRange *range)
1656 g_return_if_fail (sheet != NULL);
1657 g_return_if_fail (PSPPIRE_IS_SHEET (sheet)) ;
1658 g_return_if_fail (range != NULL);
1660 range->row0 = min_visible_row (sheet);
1661 range->col0 = min_visible_column (sheet);
1662 range->rowi = max_visible_row (sheet);
1663 range->coli = max_visible_column (sheet);
1668 psppire_sheet_set_scroll_adjustments (PsppireSheet *sheet,
1669 GtkAdjustment *hadjustment,
1670 GtkAdjustment *vadjustment)
1672 if ( sheet->vadjustment != vadjustment )
1674 if (sheet->vadjustment)
1675 g_object_unref (sheet->vadjustment);
1676 sheet->vadjustment = vadjustment;
1680 g_object_ref (vadjustment);
1682 g_signal_connect (sheet->vadjustment, "value_changed",
1683 G_CALLBACK (vadjustment_value_changed),
1688 if ( sheet->hadjustment != hadjustment )
1690 if (sheet->hadjustment)
1691 g_object_unref (sheet->hadjustment);
1693 sheet->hadjustment = hadjustment;
1697 g_object_ref (hadjustment);
1699 g_signal_connect (sheet->hadjustment, "value_changed",
1700 G_CALLBACK (hadjustment_value_changed),
1708 psppire_sheet_finalize (GObject *object)
1710 PsppireSheet *sheet;
1712 g_return_if_fail (object != NULL);
1713 g_return_if_fail (PSPPIRE_IS_SHEET (object));
1715 sheet = PSPPIRE_SHEET (object);
1717 if (G_OBJECT_CLASS (parent_class)->finalize)
1718 (*G_OBJECT_CLASS (parent_class)->finalize) (object);
1722 psppire_sheet_dispose (GObject *object)
1724 PsppireSheet *sheet = PSPPIRE_SHEET (object);
1726 g_return_if_fail (object != NULL);
1727 g_return_if_fail (PSPPIRE_IS_SHEET (object));
1729 if ( sheet->dispose_has_run )
1732 sheet->dispose_has_run = TRUE;
1734 if ( sheet->cell_padding)
1735 g_boxed_free (GTK_TYPE_BORDER, sheet->cell_padding);
1737 if (sheet->model) g_object_unref (sheet->model);
1738 if (sheet->vaxis) g_object_unref (sheet->vaxis);
1739 if (sheet->haxis) g_object_unref (sheet->haxis);
1741 g_object_unref (sheet->button);
1742 sheet->button = NULL;
1744 /* unref adjustments */
1745 if (sheet->hadjustment)
1747 g_signal_handlers_disconnect_matched (sheet->hadjustment,
1748 G_SIGNAL_MATCH_DATA,
1752 g_object_unref (sheet->hadjustment);
1753 sheet->hadjustment = NULL;
1756 if (sheet->vadjustment)
1758 g_signal_handlers_disconnect_matched (sheet->vadjustment,
1759 G_SIGNAL_MATCH_DATA,
1763 g_object_unref (sheet->vadjustment);
1765 sheet->vadjustment = NULL;
1768 if (G_OBJECT_CLASS (parent_class)->dispose)
1769 (*G_OBJECT_CLASS (parent_class)->dispose) (object);
1773 psppire_sheet_style_set (GtkWidget *widget,
1774 GtkStyle *previous_style)
1776 PsppireSheet *sheet;
1778 g_return_if_fail (widget != NULL);
1779 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1781 if (GTK_WIDGET_CLASS (parent_class)->style_set)
1782 (*GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous_style);
1784 sheet = PSPPIRE_SHEET (widget);
1786 if (GTK_WIDGET_REALIZED (widget))
1788 gtk_style_set_background (widget->style, widget->window, widget->state);
1791 set_entry_widget_font (sheet);
1796 psppire_sheet_realize (GtkWidget *widget)
1798 PsppireSheet *sheet;
1799 GdkWindowAttr attributes;
1800 const gint attributes_mask =
1801 GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_CURSOR;
1804 GdkColormap *colormap;
1805 GdkDisplay *display;
1807 g_return_if_fail (widget != NULL);
1808 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1810 sheet = PSPPIRE_SHEET (widget);
1812 colormap = gtk_widget_get_colormap (widget);
1813 display = gtk_widget_get_display (widget);
1815 attributes.window_type = GDK_WINDOW_CHILD;
1816 attributes.x = widget->allocation.x;
1817 attributes.y = widget->allocation.y;
1818 attributes.width = widget->allocation.width;
1819 attributes.height = widget->allocation.height;
1820 attributes.wclass = GDK_INPUT_OUTPUT;
1822 attributes.visual = gtk_widget_get_visual (widget);
1823 attributes.colormap = colormap;
1825 attributes.event_mask = gtk_widget_get_events (widget);
1826 attributes.event_mask |= (GDK_EXPOSURE_MASK |
1827 GDK_BUTTON_PRESS_MASK |
1828 GDK_BUTTON_RELEASE_MASK |
1829 GDK_KEY_PRESS_MASK |
1830 GDK_ENTER_NOTIFY_MASK |
1831 GDK_LEAVE_NOTIFY_MASK |
1832 GDK_POINTER_MOTION_MASK |
1833 GDK_POINTER_MOTION_HINT_MASK);
1835 attributes.cursor = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
1838 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1840 gdk_window_set_user_data (widget->window, sheet);
1842 widget->style = gtk_style_attach (widget->style, widget->window);
1844 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1846 gdk_color_parse ("white", &sheet->color[BG_COLOR]);
1847 gdk_colormap_alloc_color (colormap, &sheet->color[BG_COLOR], FALSE,
1849 gdk_color_parse ("gray", &sheet->color[GRID_COLOR]);
1850 gdk_colormap_alloc_color (colormap, &sheet->color[GRID_COLOR], FALSE,
1855 attributes.width = sheet->column_title_area.width;
1856 attributes.height = sheet->column_title_area.height;
1859 /* column - title window */
1860 sheet->column_title_window =
1861 gdk_window_new (widget->window, &attributes, attributes_mask);
1862 gdk_window_set_user_data (sheet->column_title_window, sheet);
1863 gtk_style_set_background (widget->style, sheet->column_title_window,
1869 attributes.width = sheet->row_title_area.width;
1870 attributes.height = sheet->row_title_area.height;
1872 /* row - title window */
1873 sheet->row_title_window = gdk_window_new (widget->window,
1874 &attributes, attributes_mask);
1875 gdk_window_set_user_data (sheet->row_title_window, sheet);
1876 gtk_style_set_background (widget->style, sheet->row_title_window,
1879 /* sheet - window */
1880 attributes.cursor = gdk_cursor_new_for_display (display, GDK_PLUS);
1885 sheet->sheet_window = gdk_window_new (widget->window,
1886 &attributes, attributes_mask);
1887 gdk_window_set_user_data (sheet->sheet_window, sheet);
1889 gdk_cursor_unref (attributes.cursor);
1891 gdk_window_set_background (sheet->sheet_window, &widget->style->white);
1892 gdk_window_show (sheet->sheet_window);
1895 sheet->fg_gc = gdk_gc_new (widget->window);
1896 sheet->bg_gc = gdk_gc_new (widget->window);
1898 values.foreground = widget->style->white;
1899 values.function = GDK_INVERT;
1900 values.subwindow_mode = GDK_INCLUDE_INFERIORS;
1901 values.line_width = MAX (sheet->cell_padding->left,
1902 MAX (sheet->cell_padding->right,
1903 MAX (sheet->cell_padding->top,
1904 sheet->cell_padding->bottom)));
1906 sheet->xor_gc = gdk_gc_new_with_values (widget->window,
1914 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
1915 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
1917 gtk_widget_set_parent_window (sheet->button, sheet->sheet_window);
1918 gtk_widget_set_parent (sheet->button, GTK_WIDGET (sheet));
1920 sheet->button->style = gtk_style_attach (sheet->button->style,
1921 sheet->sheet_window);
1924 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
1926 if (sheet->column_titles_visible)
1927 gdk_window_show (sheet->column_title_window);
1928 if (sheet->row_titles_visible)
1929 gdk_window_show (sheet->row_title_window);
1931 sheet->hover_window = create_hover_window ();
1933 draw_row_title_buttons (sheet);
1934 draw_column_title_buttons (sheet);
1936 psppire_sheet_update_primary_selection (sheet);
1939 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1943 create_global_button (PsppireSheet *sheet)
1945 sheet->button = gtk_button_new_with_label (" ");
1947 GTK_WIDGET_UNSET_FLAGS(sheet->button, GTK_CAN_FOCUS);
1949 g_object_ref_sink (sheet->button);
1951 g_signal_connect (sheet->button,
1953 G_CALLBACK (global_button_clicked),
1958 size_allocate_global_button (PsppireSheet *sheet)
1960 GtkAllocation allocation;
1962 if (!sheet->column_titles_visible) return;
1963 if (!sheet->row_titles_visible) return;
1965 gtk_widget_size_request (sheet->button, NULL);
1969 allocation.width = sheet->row_title_area.width;
1970 allocation.height = sheet->column_title_area.height;
1972 gtk_widget_size_allocate (sheet->button, &allocation);
1976 global_button_clicked (GtkWidget *widget, gpointer data)
1978 psppire_sheet_click_cell (PSPPIRE_SHEET (data), -1, -1);
1983 psppire_sheet_unrealize (GtkWidget *widget)
1985 PsppireSheet *sheet;
1987 g_return_if_fail (widget != NULL);
1988 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1990 sheet = PSPPIRE_SHEET (widget);
1992 gdk_cursor_unref (sheet->cursor_drag);
1993 sheet->cursor_drag = NULL;
1995 gdk_colormap_free_colors (gtk_widget_get_colormap (widget),
1996 sheet->color, n_COLORS);
1998 g_object_unref (sheet->xor_gc);
1999 g_object_unref (sheet->fg_gc);
2000 g_object_unref (sheet->bg_gc);
2002 destroy_hover_window (sheet->hover_window);
2004 gdk_window_destroy (sheet->sheet_window);
2005 gdk_window_destroy (sheet->column_title_window);
2006 gdk_window_destroy (sheet->row_title_window);
2008 gtk_widget_unparent (sheet->entry_widget);
2009 if (sheet->button != NULL)
2010 gtk_widget_unparent (sheet->button);
2012 if (GTK_WIDGET_CLASS (parent_class)->unrealize)
2013 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
2017 psppire_sheet_map (GtkWidget *widget)
2019 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2021 g_return_if_fail (widget != NULL);
2022 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
2024 if (!GTK_WIDGET_MAPPED (widget))
2026 GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
2028 gdk_window_show (widget->window);
2029 gdk_window_show (sheet->sheet_window);
2031 if (sheet->column_titles_visible)
2033 draw_column_title_buttons (sheet);
2034 gdk_window_show (sheet->column_title_window);
2036 if (sheet->row_titles_visible)
2038 draw_row_title_buttons (sheet);
2039 gdk_window_show (sheet->row_title_window);
2042 if (!GTK_WIDGET_MAPPED (sheet->entry_widget)
2043 && sheet->active_cell.row >= 0
2044 && sheet->active_cell.col >= 0 )
2046 gtk_widget_show (sheet->entry_widget);
2047 gtk_widget_map (sheet->entry_widget);
2050 if (!GTK_WIDGET_MAPPED (sheet->button))
2052 gtk_widget_show (sheet->button);
2053 gtk_widget_map (sheet->button);
2056 redraw_range (sheet, NULL);
2057 change_active_cell (sheet,
2058 sheet->active_cell.row,
2059 sheet->active_cell.col);
2064 psppire_sheet_unmap (GtkWidget *widget)
2066 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2068 if (!GTK_WIDGET_MAPPED (widget))
2071 GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
2073 gdk_window_hide (sheet->sheet_window);
2074 if (sheet->column_titles_visible)
2075 gdk_window_hide (sheet->column_title_window);
2076 if (sheet->row_titles_visible)
2077 gdk_window_hide (sheet->row_title_window);
2078 gdk_window_hide (widget->window);
2080 gtk_widget_unmap (sheet->entry_widget);
2081 gtk_widget_unmap (sheet->button);
2082 gtk_widget_unmap (sheet->hover_window->window);
2085 /* get cell attributes of the given cell */
2086 /* TRUE means that the cell is currently allocated */
2087 static gboolean psppire_sheet_get_attributes (const PsppireSheet *sheet,
2089 PsppireSheetCellAttr *attributes);
2094 psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint col)
2096 PangoLayout *layout;
2097 PangoRectangle text;
2098 PangoFontDescription *font_desc = GTK_WIDGET (sheet)->style->font_desc;
2103 PsppireSheetCellAttr attributes;
2106 g_return_if_fail (sheet != NULL);
2108 /* bail now if we aren't yet drawable */
2109 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
2112 row >= psppire_axis_unit_count (sheet->vaxis))
2116 col >= psppire_axis_unit_count (sheet->haxis))
2119 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2121 /* select GC for background rectangle */
2122 gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground);
2123 gdk_gc_set_foreground (sheet->bg_gc, &attributes.background);
2125 rectangle_from_cell (sheet, row, col, &area);
2127 gdk_gc_set_line_attributes (sheet->fg_gc, 1, 0, 0, 0);
2129 if (sheet->show_grid)
2131 gdk_gc_set_foreground (sheet->bg_gc, &sheet->color[GRID_COLOR]);
2133 gdk_draw_rectangle (sheet->sheet_window,
2137 area.width, area.height);
2141 label = psppire_sheet_cell_get_text (sheet, row, col);
2146 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), label);
2147 dispose_string (sheet, label);
2150 pango_layout_set_font_description (layout, font_desc);
2152 pango_layout_get_pixel_extents (layout, NULL, &text);
2154 gdk_gc_set_clip_rectangle (sheet->fg_gc, &area);
2156 font_height = pango_font_description_get_size (font_desc);
2157 if ( !pango_font_description_get_size_is_absolute (font_desc))
2158 font_height /= PANGO_SCALE;
2161 if ( sheet->cell_padding )
2163 area.x += sheet->cell_padding->left;
2164 area.width -= sheet->cell_padding->right
2165 + sheet->cell_padding->left;
2167 area.y += sheet->cell_padding->top;
2168 area.height -= sheet->cell_padding->bottom
2170 sheet->cell_padding->top;
2173 /* Centre the text vertically */
2174 area.y += (area.height - font_height) / 2.0;
2176 switch (attributes.justification)
2178 case GTK_JUSTIFY_RIGHT:
2179 area.x += area.width - text.width;
2181 case GTK_JUSTIFY_CENTER:
2182 area.x += (area.width - text.width) / 2.0;
2184 case GTK_JUSTIFY_LEFT:
2188 g_critical ("Unhandled justification %d in column %d\n",
2189 attributes.justification, col);
2193 gdk_draw_layout (sheet->sheet_window, sheet->fg_gc,
2198 gdk_gc_set_clip_rectangle (sheet->fg_gc, NULL);
2199 g_object_unref (layout);
2204 draw_sheet_region (PsppireSheet *sheet, GdkRegion *region)
2206 PsppireSheetRange range;
2211 PsppireSheetRange drawing_range;
2213 gdk_region_get_clipbox (region, &area);
2215 y = area.y + sheet->vadjustment->value;
2216 x = area.x + sheet->hadjustment->value;
2218 if ( sheet->column_titles_visible)
2219 y -= sheet->column_title_area.height;
2221 if ( sheet->row_titles_visible)
2222 x -= sheet->row_title_area.width;
2224 maximize_int (&x, 0);
2225 maximize_int (&y, 0);
2227 range.row0 = row_from_ypixel (sheet, y);
2228 range.rowi = row_from_ypixel (sheet, y + area.height);
2230 range.col0 = column_from_xpixel (sheet, x);
2231 range.coli = column_from_xpixel (sheet, x + area.width);
2233 g_return_if_fail (sheet != NULL);
2234 g_return_if_fail (PSPPIRE_SHEET (sheet));
2236 if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (sheet))) return;
2237 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2238 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
2241 drawing_range.row0 = MAX (range.row0, min_visible_row (sheet));
2242 drawing_range.col0 = MAX (range.col0, min_visible_column (sheet));
2243 drawing_range.rowi = MIN (range.rowi, max_visible_row (sheet));
2244 drawing_range.coli = MIN (range.coli, max_visible_column (sheet));
2246 g_return_if_fail (drawing_range.rowi >= drawing_range.row0);
2247 g_return_if_fail (drawing_range.coli >= drawing_range.col0);
2249 for (i = drawing_range.row0; i <= drawing_range.rowi; i++)
2251 for (j = drawing_range.col0; j <= drawing_range.coli; j++)
2252 psppire_sheet_cell_draw (sheet, i, j);
2255 if (sheet->select_status == PSPPIRE_SHEET_NORMAL &&
2256 sheet->active_cell.row >= drawing_range.row0 &&
2257 sheet->active_cell.row <= drawing_range.rowi &&
2258 sheet->active_cell.col >= drawing_range.col0 &&
2259 sheet->active_cell.col <= drawing_range.coli)
2260 psppire_sheet_show_entry_widget (sheet);
2266 safe_strcmp (const gchar *s1, const gchar *s2)
2268 if ( !s1 && !s2) return 0;
2269 if ( !s1) return -1;
2270 if ( !s2) return +1;
2271 return strcmp (s1, s2);
2275 psppire_sheet_set_cell (PsppireSheet *sheet, gint row, gint col,
2276 GtkJustification justification,
2279 PsppireSheetModel *model ;
2282 g_return_if_fail (sheet != NULL);
2283 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2285 if (col >= psppire_axis_unit_count (sheet->haxis)
2286 || row >= psppire_axis_unit_count (sheet->vaxis))
2289 if (col < 0 || row < 0) return;
2291 model = psppire_sheet_get_model (sheet);
2293 old_text = psppire_sheet_model_get_string (model, row, col);
2295 if (0 != safe_strcmp (old_text, text))
2297 g_signal_handler_block (sheet->model, sheet->update_handler_id);
2298 psppire_sheet_model_set_string (model, text, row, col);
2299 g_signal_handler_unblock (sheet->model, sheet->update_handler_id);
2302 if ( psppire_sheet_model_free_strings (model))
2308 psppire_sheet_cell_clear (PsppireSheet *sheet, gint row, gint column)
2310 PsppireSheetRange range;
2312 g_return_if_fail (sheet != NULL);
2313 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2314 if (column >= psppire_axis_unit_count (sheet->haxis) ||
2315 row >= psppire_axis_unit_count (sheet->vaxis)) return;
2317 if (column < 0 || row < 0) return;
2321 range.col0 = min_visible_column (sheet);
2322 range.coli = max_visible_column (sheet);
2324 psppire_sheet_real_cell_clear (sheet, row, column);
2326 redraw_range (sheet, &range);
2330 psppire_sheet_real_cell_clear (PsppireSheet *sheet, gint row, gint column)
2332 PsppireSheetModel *model = psppire_sheet_get_model (sheet);
2334 gchar *old_text = psppire_sheet_cell_get_text (sheet, row, column);
2336 if (old_text && strlen (old_text) > 0 )
2338 psppire_sheet_model_datum_clear (model, row, column);
2341 dispose_string (sheet, old_text);
2345 psppire_sheet_cell_get_text (const PsppireSheet *sheet, gint row, gint col)
2347 PsppireSheetModel *model;
2348 g_return_val_if_fail (sheet != NULL, NULL);
2349 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
2351 if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis))
2353 if (col < 0 || row < 0) return NULL;
2355 model = psppire_sheet_get_model (sheet);
2360 return psppire_sheet_model_get_string (model, row, col);
2364 /* Convert X, Y (in pixels) to *ROW, *COLUMN
2365 If the function returns FALSE, then the results will be unreliable.
2368 psppire_sheet_get_pixel_info (PsppireSheet *sheet,
2376 *column = -G_MAXINT;
2378 g_return_val_if_fail (sheet != NULL, 0);
2379 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2381 /* bounds checking, return false if the user clicked
2389 if ( sheet->column_titles_visible)
2390 y -= sheet->column_title_area.height;
2392 y += sheet->vadjustment->value;
2394 if ( y < 0 && sheet->column_titles_visible)
2400 trow = row_from_ypixel (sheet, y);
2401 if (trow > psppire_axis_unit_count (sheet->vaxis))
2407 if ( sheet->row_titles_visible)
2408 x -= sheet->row_title_area.width;
2410 x += sheet->hadjustment->value;
2412 if ( x < 0 && sheet->row_titles_visible)
2418 tcol = column_from_xpixel (sheet, x);
2419 if (tcol > psppire_axis_unit_count (sheet->haxis))
2429 psppire_sheet_get_cell_area (PsppireSheet *sheet,
2434 g_return_val_if_fail (sheet != NULL, 0);
2435 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2437 if (row >= psppire_axis_unit_count (sheet->vaxis) || column >= psppire_axis_unit_count (sheet->haxis))
2440 area->x = (column == -1) ? 0 : psppire_axis_start_pixel (sheet->haxis, column);
2441 area->y = (row == -1) ? 0 : psppire_axis_start_pixel (sheet->vaxis, row);
2443 area->width= (column == -1) ? sheet->row_title_area.width
2444 : psppire_axis_unit_size (sheet->haxis, column);
2446 area->height= (row == -1) ? sheet->column_title_area.height
2447 : psppire_axis_unit_size (sheet->vaxis, row);
2453 psppire_sheet_set_active_cell (PsppireSheet *sheet, gint row, gint col)
2455 g_return_if_fail (sheet != NULL);
2456 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2458 if (row < -1 || col < -1)
2461 if (row >= psppire_axis_unit_count (sheet->vaxis)
2463 col >= psppire_axis_unit_count (sheet->haxis))
2466 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2469 if ( row == -1 || col == -1)
2471 psppire_sheet_hide_entry_widget (sheet);
2475 change_active_cell (sheet, row, col);
2479 psppire_sheet_get_active_cell (PsppireSheet *sheet, gint *row, gint *column)
2481 g_return_if_fail (sheet != NULL);
2482 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2484 if ( row ) *row = sheet->active_cell.row;
2485 if (column) *column = sheet->active_cell.col;
2489 entry_load_text (PsppireSheet *sheet)
2493 GtkJustification justification;
2494 PsppireSheetCellAttr attributes;
2496 if (!GTK_WIDGET_VISIBLE (sheet->entry_widget)) return;
2497 if (sheet->select_status != PSPPIRE_SHEET_NORMAL) return;
2499 row = sheet->active_cell.row;
2500 col = sheet->active_cell.col;
2502 if (row < 0 || col < 0) return;
2504 text = gtk_entry_get_text (psppire_sheet_get_entry (sheet));
2506 if (text && strlen (text) > 0)
2508 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2509 justification = attributes.justification;
2510 psppire_sheet_set_cell (sheet, row, col, justification, text);
2516 psppire_sheet_hide_entry_widget (PsppireSheet *sheet)
2518 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2521 if (sheet->active_cell.row < 0 ||
2522 sheet->active_cell.col < 0) return;
2524 gtk_widget_hide (sheet->entry_widget);
2525 gtk_widget_unmap (sheet->entry_widget);
2527 GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2531 change_active_cell (PsppireSheet *sheet, gint row, gint col)
2533 gint old_row, old_col;
2535 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2537 if (row < 0 || col < 0)
2540 if ( row > psppire_axis_unit_count (sheet->vaxis)
2541 || col > psppire_axis_unit_count (sheet->haxis))
2544 old_row = sheet->active_cell.row;
2545 old_col = sheet->active_cell.col;
2547 entry_load_text (sheet);
2549 /* Erase the old cell border */
2550 psppire_sheet_draw_active_cell (sheet);
2552 sheet->active_cell.row = row;
2553 sheet->active_cell.col = col;
2555 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
2557 GTK_WIDGET_UNSET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2559 psppire_sheet_draw_active_cell (sheet);
2560 psppire_sheet_show_entry_widget (sheet);
2562 GTK_WIDGET_SET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2564 g_signal_emit (sheet, sheet_signals [ACTIVATE], 0,
2565 row, col, old_row, old_col);
2570 psppire_sheet_show_entry_widget (PsppireSheet *sheet)
2572 GtkEntry *sheet_entry;
2573 PsppireSheetCellAttr attributes;
2577 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2579 row = sheet->active_cell.row;
2580 col = sheet->active_cell.col;
2582 /* Don't show the active cell, if there is no active cell: */
2583 if (! (row >= 0 && col >= 0)) /* e.g row or coll == -1. */
2586 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2587 if (sheet->select_status != PSPPIRE_SHEET_NORMAL) return;
2588 if (PSPPIRE_SHEET_IN_SELECTION (sheet)) return;
2590 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2592 sheet_entry = psppire_sheet_get_entry (sheet);
2594 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2596 if (GTK_IS_ENTRY (sheet_entry))
2598 gchar *text = psppire_sheet_cell_get_text (sheet, row, col);
2599 const gchar *old_text = gtk_entry_get_text (GTK_ENTRY (sheet_entry));
2602 text = g_strdup ("");
2604 if (strcmp (old_text, text) != 0)
2605 gtk_entry_set_text (sheet_entry, text);
2607 dispose_string (sheet, text);
2610 switch (attributes.justification)
2612 case GTK_JUSTIFY_RIGHT:
2613 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 1.0);
2615 case GTK_JUSTIFY_CENTER:
2616 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.5);
2618 case GTK_JUSTIFY_LEFT:
2620 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.0);
2626 psppire_sheet_size_allocate_entry (sheet);
2628 gtk_widget_set_sensitive (GTK_WIDGET (sheet_entry),
2629 psppire_sheet_model_is_editable (sheet->model,
2631 gtk_widget_map (sheet->entry_widget);
2635 psppire_sheet_draw_active_cell (PsppireSheet *sheet)
2638 PsppireSheetRange range;
2640 row = sheet->active_cell.row;
2641 col = sheet->active_cell.col;
2643 if (row < 0 || col < 0) return FALSE;
2645 if (!psppire_sheet_cell_isvisible (sheet, row, col))
2648 range.col0 = range.coli = col;
2649 range.row0 = range.rowi = row;
2651 psppire_sheet_draw_border (sheet, range);
2659 psppire_sheet_draw_border (PsppireSheet *sheet, PsppireSheetRange new_range)
2663 rectangle_from_range (sheet, &new_range, &area);
2668 gdk_gc_set_clip_rectangle (sheet->xor_gc, &area);
2670 area.x += sheet->cell_padding->left / 2;
2671 area.y += sheet->cell_padding->top / 2;
2672 area.width -= (sheet->cell_padding->left + sheet->cell_padding->right ) / 2;
2673 area.height -= (sheet->cell_padding->top + sheet->cell_padding->bottom ) / 2;
2675 gdk_draw_rectangle (sheet->sheet_window, sheet->xor_gc,
2682 gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
2687 /* Selection related functions */
2690 psppire_sheet_select_row (PsppireSheet *sheet, gint row)
2693 sheet->select_status = PSPPIRE_SHEET_ROW_SELECTED;
2695 sheet->range.col0 = sheet->range.coli = -1;
2696 sheet->range.row0 = sheet->range.rowi = row;
2698 rectangle_from_range (sheet, &sheet->range, &area);
2702 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
2706 psppire_sheet_select_column (PsppireSheet *sheet, gint column)
2709 sheet->select_status = PSPPIRE_SHEET_COLUMN_SELECTED;
2711 sheet->range.col0 = sheet->range.coli = column;
2712 sheet->range.row0 = sheet->range.rowi = -1;
2714 rectangle_from_range (sheet, &sheet->range, &area);
2718 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
2723 psppire_sheet_select_range (PsppireSheet *sheet, const PsppireSheetRange *range)
2726 sheet->select_status = PSPPIRE_SHEET_RANGE_SELECTED;
2728 sheet->range = *range;
2730 rectangle_from_range (sheet, range, &area);
2733 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
2738 psppire_sheet_unselect_range (PsppireSheet *sheet)
2741 sheet->select_status = PSPPIRE_SHEET_NORMAL;
2743 rectangle_from_range (sheet, &sheet->range, &area);
2746 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
2750 psppire_sheet_real_select_range (PsppireSheet *sheet,
2751 const PsppireSheetRange *range)
2753 g_return_if_fail (sheet != NULL);
2755 if (range == NULL) range = &sheet->range;
2757 memcpy (&sheet->range, range, sizeof (*range));
2759 if (range->row0 < 0 || range->rowi < 0) return;
2760 if (range->col0 < 0 || range->coli < 0) return;
2762 psppire_sheet_update_primary_selection (sheet);
2764 g_signal_emit (sheet, sheet_signals[SELECT_RANGE], 0, &sheet->range);
2769 psppire_sheet_get_selected_range (PsppireSheet *sheet, PsppireSheetRange *range)
2771 g_return_if_fail (sheet != NULL);
2772 *range = sheet->range;
2777 psppire_sheet_expose (GtkWidget *widget, GdkEventExpose *event)
2779 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2781 g_return_val_if_fail (event != NULL, FALSE);
2783 if (!GTK_WIDGET_DRAWABLE (widget))
2786 /* exposure events on the sheet */
2787 if (event->window == sheet->row_title_window &&
2788 sheet->row_titles_visible)
2790 draw_row_title_buttons_range (sheet,
2791 min_visible_row (sheet),
2792 max_visible_row (sheet));
2795 if (event->window == sheet->column_title_window &&
2796 sheet->column_titles_visible)
2798 draw_column_title_buttons_range (sheet,
2799 min_visible_column (sheet),
2800 max_visible_column (sheet));
2803 if (event->window == sheet->sheet_window)
2805 draw_sheet_region (sheet, event->region);
2807 if (sheet->select_status != PSPPIRE_SHEET_NORMAL)
2810 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
2811 psppire_sheet_range_draw (sheet, &sheet->range);
2813 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
2814 psppire_sheet_range_draw (sheet, &sheet->drag_range);
2820 rectangle_from_range (sheet, &sheet->range, &area);
2822 gdk_draw_rectangle (sheet->sheet_window,
2825 area.x + 1, area.y + 1,
2826 area.width, area.height);
2830 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
2831 draw_xor_rectangle (sheet, sheet->drag_range);
2836 if ((!PSPPIRE_SHEET_IN_XDRAG (sheet)) && (!PSPPIRE_SHEET_IN_YDRAG (sheet)))
2839 PsppireSheetRange range;
2840 range.row0 = range.rowi = sheet->active_cell.row;
2841 range.col0 = range.coli = sheet->active_cell.col;
2843 rectangle_from_range (sheet, &range, &rect);
2845 if (GDK_OVERLAP_RECTANGLE_OUT !=
2846 gdk_region_rect_in (event->region, &rect))
2848 psppire_sheet_draw_active_cell (sheet);
2854 (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
2861 psppire_sheet_button_press (GtkWidget *widget, GdkEventButton *event)
2863 PsppireSheet *sheet;
2864 GdkModifierType mods;
2869 g_return_val_if_fail (widget != NULL, FALSE);
2870 g_return_val_if_fail (PSPPIRE_IS_SHEET (widget), FALSE);
2871 g_return_val_if_fail (event != NULL, FALSE);
2873 sheet = PSPPIRE_SHEET (widget);
2875 /* Cancel any pending tooltips */
2876 if (sheet->motion_timer)
2878 g_source_remove (sheet->motion_timer);
2879 sheet->motion_timer = 0;
2882 gtk_widget_get_pointer (widget, &x, &y);
2883 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
2886 if (event->window == sheet->column_title_window)
2888 sheet->x_drag = event->x;
2889 g_signal_emit (sheet,
2890 sheet_signals[BUTTON_EVENT_COLUMN], 0,
2893 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
2895 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
2896 g_signal_emit (sheet,
2897 sheet_signals[DOUBLE_CLICK_COLUMN], 0, column);
2901 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 ( sheet->select_status == PSPPIRE_SHEET_NORMAL)
2974 sheet->range.row0 = row;
2975 sheet->range.col0 = column;
2979 psppire_sheet_unselect_range (sheet);
2981 psppire_sheet_click_cell (sheet, row, column);
2984 if (event->window == sheet->column_title_window)
2986 gtk_widget_get_pointer (widget, &x, &y);
2987 if ( sheet->row_titles_visible)
2988 x -= sheet->row_title_area.width;
2990 x += sheet->hadjustment->value;
2992 column = column_from_xpixel (sheet, x);
2994 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
2996 gtk_grab_add (GTK_WIDGET (sheet));
2997 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3001 if (event->window == sheet->row_title_window)
3003 gtk_widget_get_pointer (widget, &x, &y);
3004 if ( sheet->column_titles_visible)
3005 y -= sheet->column_title_area.height;
3007 y += sheet->vadjustment->value;
3009 row = row_from_ypixel (sheet, y);
3010 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
3012 gtk_grab_add (GTK_WIDGET (sheet));
3013 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3021 psppire_sheet_click_cell (PsppireSheet *sheet, gint row, gint column)
3023 PsppireSheetCell cell;
3024 gboolean forbid_move;
3029 if (row >= psppire_axis_unit_count (sheet->vaxis)
3030 || column >= psppire_axis_unit_count (sheet->haxis))
3035 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
3036 &sheet->active_cell,
3042 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
3045 row = sheet->active_cell.row;
3046 column = sheet->active_cell.col;
3048 change_active_cell (sheet, row, column);
3052 if (row == -1 && column >= 0)
3054 psppire_sheet_select_column (sheet, column);
3058 if (column == -1 && row >= 0)
3060 psppire_sheet_select_row (sheet, row);
3064 if (row == -1 && column == -1)
3066 sheet->range.row0 = 0;
3067 sheet->range.col0 = 0;
3068 sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
3070 psppire_axis_unit_count (sheet->haxis) - 1;
3071 psppire_sheet_select_range (sheet, NULL);
3075 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
3076 change_active_cell (sheet, row, column);
3078 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
3084 psppire_sheet_button_release (GtkWidget *widget,
3085 GdkEventButton *event)
3087 GdkDisplay *display = gtk_widget_get_display (widget);
3089 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3091 /* release on resize windows */
3092 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3095 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
3096 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3098 gdk_display_pointer_ungrab (display, event->time);
3099 draw_xor_vline (sheet);
3102 psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)
3103 + sheet->hadjustment->value;
3105 set_column_width (sheet, sheet->drag_cell.col, width);
3110 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3113 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
3114 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3116 gdk_display_pointer_ungrab (display, event->time);
3117 draw_xor_hline (sheet);
3120 psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row) +
3121 sheet->vadjustment->value;
3123 set_row_height (sheet, sheet->drag_cell.row, height);
3128 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3130 PsppireSheetRange old_range;
3131 draw_xor_rectangle (sheet, sheet->drag_range);
3132 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_DRAG);
3133 gdk_display_pointer_ungrab (display, event->time);
3135 psppire_sheet_unselect_range (sheet);
3137 old_range = sheet->range;
3138 sheet->range = sheet->drag_range;
3139 sheet->drag_range = old_range;
3140 g_signal_emit (sheet, sheet_signals[MOVE_RANGE], 0,
3141 &sheet->drag_range, &sheet->range);
3142 psppire_sheet_select_range (sheet, &sheet->range);
3145 if (PSPPIRE_SHEET_IN_RESIZE (sheet))
3147 PsppireSheetRange old_range;
3148 draw_xor_rectangle (sheet, sheet->drag_range);
3149 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_RESIZE);
3150 gdk_display_pointer_ungrab (display, event->time);
3152 psppire_sheet_unselect_range (sheet);
3154 old_range = sheet->range;
3155 sheet->range = sheet->drag_range;
3156 sheet->drag_range = old_range;
3158 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
3159 sheet->select_status = PSPPIRE_SHEET_RANGE_SELECTED;
3161 g_signal_emit (sheet, sheet_signals[RESIZE_RANGE], 0,
3162 &sheet->drag_range, &sheet->range);
3163 psppire_sheet_select_range (sheet, &sheet->range);
3166 if (PSPPIRE_SHEET_IN_SELECTION (sheet))
3168 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3169 sheet->select_status = PSPPIRE_SHEET_RANGE_SELECTED;
3171 change_active_cell (sheet, sheet->active_cell.row,
3172 sheet->active_cell.col);
3175 gdk_display_pointer_ungrab (display, event->time);
3176 gtk_grab_remove (GTK_WIDGET (sheet));
3178 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3187 /* Shamelessly lifted from gtktooltips */
3189 psppire_sheet_subtitle_paint_window (GtkWidget *tip_window)
3193 gtk_widget_size_request (tip_window, &req);
3194 gtk_paint_flat_box (tip_window->style, tip_window->window,
3195 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
3196 NULL, GTK_WIDGET(tip_window), "tooltip",
3197 0, 0, req.width, req.height);
3203 destroy_hover_window (PsppireSheetHoverTitle *h)
3205 gtk_widget_destroy (h->window);
3209 static PsppireSheetHoverTitle *
3210 create_hover_window (void)
3212 PsppireSheetHoverTitle *hw = g_malloc (sizeof (*hw));
3214 hw->window = gtk_window_new (GTK_WINDOW_POPUP);
3216 #if GTK_CHECK_VERSION (2, 9, 0)
3217 gtk_window_set_type_hint (GTK_WINDOW (hw->window),
3218 GDK_WINDOW_TYPE_HINT_TOOLTIP);
3221 gtk_widget_set_app_paintable (hw->window, TRUE);
3222 gtk_window_set_resizable (GTK_WINDOW (hw->window), FALSE);
3223 gtk_widget_set_name (hw->window, "gtk-tooltips");
3224 gtk_container_set_border_width (GTK_CONTAINER (hw->window), 4);
3226 g_signal_connect (hw->window,
3228 G_CALLBACK (psppire_sheet_subtitle_paint_window),
3231 hw->label = gtk_label_new (NULL);
3234 gtk_label_set_line_wrap (GTK_LABEL (hw->label), TRUE);
3235 gtk_misc_set_alignment (GTK_MISC (hw->label), 0.5, 0.5);
3237 gtk_container_add (GTK_CONTAINER (hw->window), hw->label);
3239 gtk_widget_show (hw->label);
3241 g_signal_connect (hw->window,
3243 G_CALLBACK (gtk_widget_destroyed),
3249 #define HOVER_WINDOW_Y_OFFSET 2
3252 show_subtitle (PsppireSheet *sheet, gint row, gint column,
3253 const gchar *subtitle)
3262 gtk_label_set_text (GTK_LABEL (sheet->hover_window->label),
3266 sheet->hover_window->row = row;
3267 sheet->hover_window->column = column;
3269 gdk_window_get_origin (GTK_WIDGET (sheet)->window, &x, &y);
3271 gtk_widget_get_pointer (GTK_WIDGET (sheet), &px, &py);
3273 gtk_widget_show (sheet->hover_window->window);
3275 width = GTK_WIDGET (sheet->hover_window->label)->allocation.width;
3281 y += sheet->column_title_area.y;
3282 y += sheet->column_title_area.height;
3283 y += HOVER_WINDOW_Y_OFFSET;
3289 x += sheet->row_title_area.x;
3290 x += sheet->row_title_area.width * 2 / 3.0;
3293 gtk_window_move (GTK_WINDOW (sheet->hover_window->window),
3298 motion_timeout_callback (gpointer data)
3300 PsppireSheet *sheet = PSPPIRE_SHEET (data);
3304 gdk_threads_enter ();
3305 gtk_widget_get_pointer (GTK_WIDGET (sheet), &x, &y);
3307 if ( psppire_sheet_get_pixel_info (sheet, x, y, &row, &column) )
3309 if (sheet->row_title_under && row >= 0)
3311 gchar *text = psppire_sheet_model_get_row_subtitle (sheet->model, row);
3313 show_subtitle (sheet, row, -1, text);
3317 if (sheet->column_title_under && column >= 0)
3319 gchar *text = psppire_sheet_model_get_column_subtitle (sheet->model,
3322 show_subtitle (sheet, -1, column, text);
3328 gdk_threads_leave ();
3333 psppire_sheet_motion (GtkWidget *widget, GdkEventMotion *event)
3335 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3336 GdkModifierType mods;
3337 GdkCursorType new_cursor;
3340 GdkDisplay *display;
3342 g_return_val_if_fail (event != NULL, FALSE);
3344 display = gtk_widget_get_display (widget);
3346 /* selections on the sheet */
3350 if (!GTK_WIDGET_VISIBLE (sheet->hover_window->window))
3352 if ( sheet->motion_timer > 0 )
3353 g_source_remove (sheet->motion_timer);
3354 sheet->motion_timer =
3355 g_timeout_add (TIMEOUT_HOVER, motion_timeout_callback, sheet);
3361 gtk_widget_get_pointer (widget, &wx, &wy);
3363 if ( psppire_sheet_get_pixel_info (sheet, wx, wy, &row, &column) )
3365 if ( row != sheet->hover_window->row ||
3366 column != sheet->hover_window->column)
3368 gtk_widget_hide (sheet->hover_window->window);
3373 if (event->window == sheet->column_title_window)
3375 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3376 on_column_boundary (sheet, x, &column))
3378 new_cursor = GDK_SB_H_DOUBLE_ARROW;
3379 if (new_cursor != sheet->cursor_drag->type)
3381 gdk_cursor_unref (sheet->cursor_drag);
3382 sheet->cursor_drag =
3383 gdk_cursor_new_for_display (display, new_cursor);
3385 gdk_window_set_cursor (sheet->column_title_window,
3386 sheet->cursor_drag);
3391 new_cursor = GDK_TOP_LEFT_ARROW;
3392 if (!PSPPIRE_SHEET_IN_XDRAG (sheet) &&
3393 new_cursor != sheet->cursor_drag->type)
3395 gdk_cursor_unref (sheet->cursor_drag);
3396 sheet->cursor_drag =
3397 gdk_cursor_new_for_display (display, new_cursor);
3398 gdk_window_set_cursor (sheet->column_title_window,
3399 sheet->cursor_drag);
3403 else if (event->window == sheet->row_title_window)
3405 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3406 on_row_boundary (sheet, y, &row))
3408 new_cursor = GDK_SB_V_DOUBLE_ARROW;
3409 if (new_cursor != sheet->cursor_drag->type)
3411 gdk_cursor_unref (sheet->cursor_drag);
3412 sheet->cursor_drag =
3413 gdk_cursor_new_for_display (display, new_cursor);
3414 gdk_window_set_cursor (sheet->row_title_window,
3415 sheet->cursor_drag);
3420 new_cursor = GDK_TOP_LEFT_ARROW;
3421 if (!PSPPIRE_SHEET_IN_YDRAG (sheet) &&
3422 new_cursor != sheet->cursor_drag->type)
3424 gdk_cursor_unref (sheet->cursor_drag);
3425 sheet->cursor_drag =
3426 gdk_cursor_new_for_display (display, new_cursor);
3427 gdk_window_set_cursor (sheet->row_title_window,
3428 sheet->cursor_drag);
3433 new_cursor = GDK_PLUS;
3434 if ( event->window == sheet->sheet_window &&
3435 !POSSIBLE_DRAG (sheet, x, y, &row, &column) &&
3436 !PSPPIRE_SHEET_IN_DRAG (sheet) &&
3437 !POSSIBLE_RESIZE (sheet, x, y, &row, &column) &&
3438 !PSPPIRE_SHEET_IN_RESIZE (sheet) &&
3439 new_cursor != sheet->cursor_drag->type)
3441 gdk_cursor_unref (sheet->cursor_drag);
3442 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
3443 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3446 new_cursor = GDK_TOP_LEFT_ARROW;
3447 if ( event->window == sheet->sheet_window &&
3448 ! (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
3449 PSPPIRE_SHEET_IN_RESIZE (sheet)) &&
3450 (POSSIBLE_DRAG (sheet, x, y, &row, &column) ||
3451 PSPPIRE_SHEET_IN_DRAG (sheet)) &&
3452 new_cursor != sheet->cursor_drag->type)
3454 gdk_cursor_unref (sheet->cursor_drag);
3455 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
3456 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3459 gdk_window_get_pointer (widget->window, &x, &y, &mods);
3460 if (! (mods & GDK_BUTTON1_MASK)) return FALSE;
3462 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3464 if (event->x != sheet->x_drag)
3466 draw_xor_vline (sheet);
3467 sheet->x_drag = event->x;
3468 draw_xor_vline (sheet);
3474 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3476 if (event->y != sheet->y_drag)
3478 draw_xor_hline (sheet);
3479 sheet->y_drag = event->y;
3480 draw_xor_hline (sheet);
3486 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3488 PsppireSheetRange aux;
3489 column = column_from_xpixel (sheet, x)- sheet->drag_cell.col;
3490 row = row_from_ypixel (sheet, y) - sheet->drag_cell.row;
3491 if (sheet->select_status == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
3492 if (sheet->select_status == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
3496 if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
3497 aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
3499 aux = sheet->drag_range;
3500 sheet->drag_range.row0 = sheet->range.row0 + row;
3501 sheet->drag_range.col0 = sheet->range.col0 + column;
3502 sheet->drag_range.rowi = sheet->range.rowi + row;
3503 sheet->drag_range.coli = sheet->range.coli + column;
3504 if (aux.row0 != sheet->drag_range.row0 ||
3505 aux.col0 != sheet->drag_range.col0)
3507 draw_xor_rectangle (sheet, aux);
3508 draw_xor_rectangle (sheet, sheet->drag_range);
3514 if (PSPPIRE_SHEET_IN_RESIZE (sheet))
3516 PsppireSheetRange aux;
3517 gint v_h, current_col, current_row, col_threshold, row_threshold;
3519 if (abs (x - psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)) >
3520 abs (y - psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row))) v_h = 2;
3522 current_col = column_from_xpixel (sheet, x);
3523 current_row = row_from_ypixel (sheet, y);
3524 column = current_col - sheet->drag_cell.col;
3525 row = current_row - sheet->drag_cell.row;
3527 /*use half of column width resp. row height as threshold to
3529 col_threshold = psppire_axis_start_pixel (sheet->haxis, current_col) +
3530 psppire_axis_unit_size (sheet->haxis, current_col) / 2;
3533 if (x < col_threshold)
3536 else if (column < 0)
3538 if (x > col_threshold)
3541 row_threshold = psppire_axis_start_pixel (sheet->vaxis, current_row) +
3542 psppire_axis_unit_size (sheet->vaxis, current_row)/2;
3545 if (y < row_threshold)
3550 if (y > row_threshold)
3554 if (sheet->select_status == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
3555 if (sheet->select_status == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
3565 if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
3566 aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
3568 aux = sheet->drag_range;
3569 sheet->drag_range = sheet->range;
3571 if (row < 0) sheet->drag_range.row0 = sheet->range.row0 + row;
3572 if (row > 0) sheet->drag_range.rowi = sheet->range.rowi + row;
3573 if (column < 0) sheet->drag_range.col0 = sheet->range.col0 + column;
3574 if (column > 0) sheet->drag_range.coli = sheet->range.coli + column;
3576 if (aux.row0 != sheet->drag_range.row0 ||
3577 aux.rowi != sheet->drag_range.rowi ||
3578 aux.col0 != sheet->drag_range.col0 ||
3579 aux.coli != sheet->drag_range.coli)
3581 draw_xor_rectangle (sheet, aux);
3582 draw_xor_rectangle (sheet, sheet->drag_range);
3588 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
3590 if (sheet->select_status == PSPPIRE_SHEET_NORMAL && row == sheet->active_cell.row &&
3591 column == sheet->active_cell.col) return TRUE;
3593 if ( mods & GDK_BUTTON1_MASK)
3595 if (PSPPIRE_SHEET_IN_SELECTION (sheet) )
3597 /* Redraw the old range */
3598 psppire_sheet_unselect_range (sheet);
3600 sheet->range.rowi = row;
3601 sheet->range.coli = column;
3603 /* Redraw the new range */
3604 psppire_sheet_select_range (sheet, &sheet->range);
3608 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3616 psppire_sheet_crossing_notify (GtkWidget *widget,
3617 GdkEventCrossing *event)
3619 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3621 if (event->window == sheet->column_title_window)
3622 sheet->column_title_under = event->type == GDK_ENTER_NOTIFY;
3623 else if (event->window == sheet->row_title_window)
3624 sheet->row_title_under = event->type == GDK_ENTER_NOTIFY;
3626 if (event->type == GDK_LEAVE_NOTIFY)
3627 gtk_widget_hide (sheet->hover_window->window);
3634 psppire_sheet_focus_in (GtkWidget *w,
3635 GdkEventFocus *event)
3637 PsppireSheet *sheet = PSPPIRE_SHEET (w);
3639 gtk_widget_grab_focus (sheet->entry_widget);
3647 psppire_sheet_entry_key_press (GtkWidget *widget,
3651 g_signal_emit_by_name (widget, "key_press_event", key, &focus);
3656 /* Number of rows in a step-increment */
3657 #define ROWS_PER_STEP 1
3661 page_vertical (PsppireSheet *sheet, GtkScrollType dir)
3663 gint old_row = sheet->active_cell.row ;
3664 glong vpixel = psppire_axis_start_pixel (sheet->vaxis, old_row);
3668 vpixel -= psppire_axis_start_pixel (sheet->vaxis,
3669 min_visible_row (sheet));
3673 case GTK_SCROLL_PAGE_DOWN:
3674 gtk_adjustment_set_value (sheet->vadjustment,
3675 sheet->vadjustment->value +
3676 sheet->vadjustment->page_increment);
3678 case GTK_SCROLL_PAGE_UP:
3679 gtk_adjustment_set_value (sheet->vadjustment,
3680 sheet->vadjustment->value -
3681 sheet->vadjustment->page_increment);
3685 g_assert_not_reached ();
3690 vpixel += psppire_axis_start_pixel (sheet->vaxis,
3691 min_visible_row (sheet));
3693 new_row = row_from_ypixel (sheet, vpixel);
3695 change_active_cell (sheet, new_row,
3696 sheet->active_cell.col);
3701 step_sheet (PsppireSheet *sheet, GtkScrollType dir)
3703 gint current_row = sheet->active_cell.row;
3704 gint current_col = sheet->active_cell.col;
3705 PsppireSheetCell new_cell ;
3706 gboolean forbidden = FALSE;
3708 new_cell.row = current_row;
3709 new_cell.col = current_col;
3713 case GTK_SCROLL_STEP_DOWN:
3716 case GTK_SCROLL_STEP_UP:
3719 case GTK_SCROLL_STEP_RIGHT:
3722 case GTK_SCROLL_STEP_LEFT:
3725 case GTK_SCROLL_STEP_FORWARD:
3728 psppire_sheet_model_get_column_count (sheet->model))
3734 case GTK_SCROLL_STEP_BACKWARD:
3736 if (new_cell.col < 0)
3739 psppire_sheet_model_get_column_count (sheet->model) - 1;
3744 g_assert_not_reached ();
3748 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
3749 &sheet->active_cell,
3757 maximize_int (&new_cell.row, 0);
3758 maximize_int (&new_cell.col, 0);
3760 minimize_int (&new_cell.row,
3761 psppire_axis_unit_count (sheet->vaxis) - 1);
3763 minimize_int (&new_cell.col,
3764 psppire_axis_unit_count (sheet->haxis) - 1);
3766 change_active_cell (sheet, new_cell.row, new_cell.col);
3769 if ( new_cell.col > max_fully_visible_column (sheet))
3772 psppire_axis_start_pixel (sheet->haxis,
3774 hpos -= sheet->hadjustment->page_size;
3776 gtk_adjustment_set_value (sheet->hadjustment,
3779 else if ( new_cell.col < min_fully_visible_column (sheet))
3782 psppire_axis_start_pixel (sheet->haxis,
3785 gtk_adjustment_set_value (sheet->hadjustment,
3790 if ( new_cell.row > max_fully_visible_row (sheet))
3793 psppire_axis_start_pixel (sheet->vaxis,
3795 vpos -= sheet->vadjustment->page_size;
3797 gtk_adjustment_set_value (sheet->vadjustment,
3800 else if ( new_cell.row < min_fully_visible_row (sheet))
3803 psppire_axis_start_pixel (sheet->vaxis,
3806 gtk_adjustment_set_value (sheet->vadjustment,
3810 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
3815 psppire_sheet_key_press (GtkWidget *widget,
3818 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3820 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3822 switch (key->keyval)
3825 step_sheet (sheet, GTK_SCROLL_STEP_FORWARD);
3828 step_sheet (sheet, GTK_SCROLL_STEP_RIGHT);
3830 case GDK_ISO_Left_Tab:
3831 step_sheet (sheet, GTK_SCROLL_STEP_BACKWARD);
3834 step_sheet (sheet, GTK_SCROLL_STEP_LEFT);
3838 step_sheet (sheet, GTK_SCROLL_STEP_DOWN);
3841 step_sheet (sheet, GTK_SCROLL_STEP_UP);
3845 page_vertical (sheet, GTK_SCROLL_PAGE_DOWN);
3848 page_vertical (sheet, GTK_SCROLL_PAGE_UP);
3852 gtk_adjustment_set_value (sheet->vadjustment,
3853 sheet->vadjustment->lower);
3855 change_active_cell (sheet, 0,
3856 sheet->active_cell.col);
3861 gtk_adjustment_set_value (sheet->vadjustment,
3862 sheet->vadjustment->upper -
3863 sheet->vadjustment->page_size -
3864 sheet->vadjustment->page_increment);
3867 change_active_cellx (sheet,
3868 psppire_axis_unit_count (sheet->vaxis) - 1,
3869 sheet->active_cell.col);
3873 psppire_sheet_real_cell_clear (sheet, sheet->active_cell.row, sheet->active_cell.col);
3884 psppire_sheet_size_request (GtkWidget *widget,
3885 GtkRequisition *requisition)
3887 PsppireSheet *sheet;
3889 g_return_if_fail (widget != NULL);
3890 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
3891 g_return_if_fail (requisition != NULL);
3893 sheet = PSPPIRE_SHEET (widget);
3895 requisition->width = 3 * DEFAULT_COLUMN_WIDTH;
3896 requisition->height = 3 * DEFAULT_ROW_HEIGHT;
3898 /* compute the size of the column title area */
3899 if (sheet->column_titles_visible)
3900 requisition->height += sheet->column_title_area.height;
3902 /* compute the size of the row title area */
3903 if (sheet->row_titles_visible)
3904 requisition->width += sheet->row_title_area.width;
3909 psppire_sheet_size_allocate (GtkWidget *widget,
3910 GtkAllocation *allocation)
3912 PsppireSheet *sheet;
3913 GtkAllocation sheet_allocation;
3916 g_return_if_fail (widget != NULL);
3917 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
3918 g_return_if_fail (allocation != NULL);
3920 sheet = PSPPIRE_SHEET (widget);
3921 widget->allocation = *allocation;
3922 border_width = GTK_CONTAINER (widget)->border_width;
3924 if (GTK_WIDGET_REALIZED (widget))
3925 gdk_window_move_resize (widget->window,
3926 allocation->x + border_width,
3927 allocation->y + border_width,
3928 allocation->width - 2 * border_width,
3929 allocation->height - 2 * border_width);
3931 sheet_allocation.x = 0;
3932 sheet_allocation.y = 0;
3933 sheet_allocation.width = allocation->width - 2 * border_width;
3934 sheet_allocation.height = allocation->height - 2 * border_width;
3936 if (GTK_WIDGET_REALIZED (widget))
3937 gdk_window_move_resize (sheet->sheet_window,
3940 sheet_allocation.width,
3941 sheet_allocation.height);
3943 /* position the window which holds the column title buttons */
3944 sheet->column_title_area.x = 0;
3945 sheet->column_title_area.y = 0;
3946 sheet->column_title_area.width = sheet_allocation.width ;
3949 /* position the window which holds the row title buttons */
3950 sheet->row_title_area.x = 0;
3951 sheet->row_title_area.y = 0;
3952 sheet->row_title_area.height = sheet_allocation.height;
3954 if (sheet->row_titles_visible)
3955 sheet->column_title_area.x += sheet->row_title_area.width;
3957 if (sheet->column_titles_visible)
3958 sheet->row_title_area.y += sheet->column_title_area.height;
3960 if (GTK_WIDGET_REALIZED (widget) && sheet->column_titles_visible)
3961 gdk_window_move_resize (sheet->column_title_window,
3962 sheet->column_title_area.x,
3963 sheet->column_title_area.y,
3964 sheet->column_title_area.width,
3965 sheet->column_title_area.height);
3968 if (GTK_WIDGET_REALIZED (widget) && sheet->row_titles_visible)
3969 gdk_window_move_resize (sheet->row_title_window,
3970 sheet->row_title_area.x,
3971 sheet->row_title_area.y,
3972 sheet->row_title_area.width,
3973 sheet->row_title_area.height);
3975 size_allocate_global_button (sheet);
3979 gint width = sheet->column_title_area.width;
3981 if ( sheet->row_titles_visible)
3982 width -= sheet->row_title_area.width;
3984 g_object_set (sheet->haxis,
3985 "minimum-extent", width,
3992 gint height = sheet->row_title_area.height;
3994 if ( sheet->column_titles_visible)
3995 height -= sheet->column_title_area.height;
3997 g_object_set (sheet->vaxis,
3998 "minimum-extent", height,
4003 /* set the scrollbars adjustments */
4004 adjust_scrollbars (sheet);
4008 draw_column_title_buttons (PsppireSheet *sheet)
4012 if (!sheet->column_titles_visible) return;
4013 if (!GTK_WIDGET_REALIZED (sheet))
4016 gdk_drawable_get_size (sheet->sheet_window, &width, NULL);
4019 if (sheet->row_titles_visible)
4021 x = sheet->row_title_area.width;
4024 if (sheet->column_title_area.width != width || sheet->column_title_area.x != x)
4026 sheet->column_title_area.width = width;
4027 sheet->column_title_area.x = x;
4028 gdk_window_move_resize (sheet->column_title_window,
4029 sheet->column_title_area.x,
4030 sheet->column_title_area.y,
4031 sheet->column_title_area.width,
4032 sheet->column_title_area.height);
4035 if (max_visible_column (sheet) ==
4036 psppire_axis_unit_count (sheet->haxis) - 1)
4037 gdk_window_clear_area (sheet->column_title_window,
4039 sheet->column_title_area.width,
4040 sheet->column_title_area.height);
4042 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
4044 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
4045 max_visible_column (sheet));
4049 draw_row_title_buttons (PsppireSheet *sheet)
4054 if (!sheet->row_titles_visible) return;
4055 if (!GTK_WIDGET_REALIZED (sheet))
4058 gdk_drawable_get_size (sheet->sheet_window, NULL, &height);
4060 if (sheet->column_titles_visible)
4062 y = sheet->column_title_area.height;
4065 if (sheet->row_title_area.height != height || sheet->row_title_area.y != y)
4067 sheet->row_title_area.y = y;
4068 sheet->row_title_area.height = height;
4069 gdk_window_move_resize (sheet->row_title_window,
4070 sheet->row_title_area.x,
4071 sheet->row_title_area.y,
4072 sheet->row_title_area.width,
4073 sheet->row_title_area.height);
4076 if (max_visible_row (sheet) == psppire_axis_unit_count (sheet->vaxis) - 1)
4077 gdk_window_clear_area (sheet->row_title_window,
4079 sheet->row_title_area.width,
4080 sheet->row_title_area.height);
4082 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
4084 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
4085 max_visible_row (sheet));
4090 psppire_sheet_size_allocate_entry (PsppireSheet *sheet)
4092 GtkAllocation entry_alloc;
4093 PsppireSheetCellAttr attributes = { 0 };
4094 GtkEntry *sheet_entry;
4096 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4097 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
4099 sheet_entry = psppire_sheet_get_entry (sheet);
4101 if ( ! psppire_sheet_get_attributes (sheet, sheet->active_cell.row,
4102 sheet->active_cell.col,
4106 if ( GTK_WIDGET_REALIZED (sheet->entry_widget) )
4108 GtkStyle *style = GTK_WIDGET (sheet_entry)->style;
4110 style->bg[GTK_STATE_NORMAL] = attributes.background;
4111 style->fg[GTK_STATE_NORMAL] = attributes.foreground;
4112 style->text[GTK_STATE_NORMAL] = attributes.foreground;
4113 style->bg[GTK_STATE_ACTIVE] = attributes.background;
4114 style->fg[GTK_STATE_ACTIVE] = attributes.foreground;
4115 style->text[GTK_STATE_ACTIVE] = attributes.foreground;
4118 rectangle_from_cell (sheet, sheet->active_cell.row,
4119 sheet->active_cell.col, &entry_alloc);
4121 entry_alloc.x += sheet->cell_padding->left;
4122 entry_alloc.y += sheet->cell_padding->right;
4123 entry_alloc.width -= sheet->cell_padding->left + sheet->cell_padding->right;
4124 entry_alloc.height -= sheet->cell_padding->top + sheet->cell_padding->bottom;
4127 gtk_widget_set_size_request (sheet->entry_widget, entry_alloc.width,
4128 entry_alloc.height);
4129 gtk_widget_size_allocate (sheet->entry_widget, &entry_alloc);
4133 /* Copy the sheet's font to the entry widget */
4135 set_entry_widget_font (PsppireSheet *sheet)
4137 GtkRcStyle *style = gtk_widget_get_modifier_style (sheet->entry_widget);
4139 pango_font_description_free (style->font_desc);
4140 style->font_desc = pango_font_description_copy (GTK_WIDGET (sheet)->style->font_desc);
4142 gtk_widget_modify_style (sheet->entry_widget, style);
4146 create_sheet_entry (PsppireSheet *sheet)
4148 if (sheet->entry_widget)
4150 gtk_widget_unparent (sheet->entry_widget);
4153 sheet->entry_widget = g_object_new (sheet->entry_type, NULL);
4154 g_object_ref_sink (sheet->entry_widget);
4156 gtk_widget_size_request (sheet->entry_widget, NULL);
4158 if ( GTK_IS_ENTRY (sheet->entry_widget))
4160 g_object_set (sheet->entry_widget,
4165 if (GTK_WIDGET_REALIZED (sheet))
4167 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
4168 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
4169 gtk_widget_realize (sheet->entry_widget);
4172 g_signal_connect_swapped (sheet->entry_widget, "key_press_event",
4173 G_CALLBACK (psppire_sheet_entry_key_press),
4176 set_entry_widget_font (sheet);
4178 gtk_widget_show (sheet->entry_widget);
4182 /* Finds the last child widget that happens to be of type GtkEntry */
4184 find_entry (GtkWidget *w, gpointer user_data)
4186 GtkWidget **entry = user_data;
4187 if ( GTK_IS_ENTRY (w))
4195 psppire_sheet_get_entry (PsppireSheet *sheet)
4197 GtkWidget *w = sheet->entry_widget;
4199 g_return_val_if_fail (sheet != NULL, NULL);
4200 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
4201 g_return_val_if_fail (sheet->entry_widget != NULL, NULL);
4203 while (! GTK_IS_ENTRY (w))
4205 GtkWidget *entry = NULL;
4207 if (GTK_IS_CONTAINER (w))
4209 gtk_container_forall (GTK_CONTAINER (w), find_entry, &entry);
4218 return GTK_ENTRY (w);
4223 draw_button (PsppireSheet *sheet, GdkWindow *window,
4224 PsppireSheetButton *button, gboolean is_sensitive,
4225 GdkRectangle allocation)
4227 GtkShadowType shadow_type;
4228 gint text_width = 0, text_height = 0;
4229 PangoAlignment align = PANGO_ALIGN_LEFT;
4235 g_return_if_fail (sheet != NULL);
4236 g_return_if_fail (button != NULL);
4239 rtl = gtk_widget_get_direction (GTK_WIDGET (sheet)) == GTK_TEXT_DIR_RTL;
4241 gdk_window_clear_area (window,
4242 allocation.x, allocation.y,
4243 allocation.width, allocation.height);
4245 gtk_widget_ensure_style (sheet->button);
4247 gtk_paint_box (sheet->button->style, window,
4248 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
4250 GTK_WIDGET (sheet->button),
4252 allocation.x, allocation.y,
4253 allocation.width, allocation.height);
4255 state = button->state;
4256 if (!is_sensitive) state = GTK_STATE_INSENSITIVE;
4258 if (state == GTK_STATE_ACTIVE)
4259 shadow_type = GTK_SHADOW_IN;
4261 shadow_type = GTK_SHADOW_OUT;
4263 if (state != GTK_STATE_NORMAL && state != GTK_STATE_INSENSITIVE)
4264 gtk_paint_box (sheet->button->style, window,
4265 button->state, shadow_type,
4266 &allocation, GTK_WIDGET (sheet->button),
4268 allocation.x, allocation.y,
4269 allocation.width, allocation.height);
4271 if ( button->overstruck)
4273 GdkPoint points[2] = {
4274 {allocation.x, allocation.y},
4275 {allocation.x + allocation.width,
4276 allocation.y + allocation.height}
4279 gtk_paint_polygon (sheet->button->style,
4291 if (button->label_visible)
4293 text_height = DEFAULT_ROW_HEIGHT -
4294 2 * COLUMN_TITLES_HEIGHT;
4296 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4298 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc,
4301 allocation.y += 2 * sheet->button->style->ythickness;
4303 if (button->label && strlen (button->label) > 0)
4305 PangoRectangle rect;
4306 gchar *line = button->label;
4308 PangoLayout *layout = NULL;
4309 gint real_x = allocation.x;
4310 gint real_y = allocation.y;
4312 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), line);
4313 pango_layout_get_extents (layout, NULL, &rect);
4315 text_width = PANGO_PIXELS (rect.width);
4316 switch (button->justification)
4318 case GTK_JUSTIFY_LEFT:
4319 real_x = allocation.x + COLUMN_TITLES_HEIGHT;
4320 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4322 case GTK_JUSTIFY_RIGHT:
4323 real_x = allocation.x + allocation.width - text_width - COLUMN_TITLES_HEIGHT;
4324 align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
4326 case GTK_JUSTIFY_CENTER:
4328 real_x = allocation.x + (allocation.width - text_width)/2;
4329 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4330 pango_layout_set_justify (layout, TRUE);
4332 pango_layout_set_alignment (layout, align);
4333 gtk_paint_layout (GTK_WIDGET (sheet)->style,
4342 g_object_unref (layout);
4345 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4347 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc, NULL);
4351 psppire_sheet_button_free (button);
4355 /* Draw the column title buttons FIRST through to LAST */
4357 draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4361 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4363 if (!sheet->column_titles_visible) return;
4365 g_return_if_fail (first >= min_visible_column (sheet));
4366 g_return_if_fail (last <= max_visible_column (sheet));
4369 rect.height = sheet->column_title_area.height;
4370 rect.x = psppire_axis_start_pixel (sheet->haxis, first) + CELL_SPACING;
4371 rect.width = psppire_axis_start_pixel (sheet->haxis, last) + CELL_SPACING
4372 + psppire_axis_unit_size (sheet->haxis, last);
4374 rect.x -= sheet->hadjustment->value;
4376 minimize_int (&rect.width, sheet->column_title_area.width);
4377 maximize_int (&rect.x, 0);
4379 gdk_window_begin_paint_rect (sheet->column_title_window, &rect);
4381 for (col = first ; col <= last ; ++col)
4383 GdkRectangle allocation;
4384 gboolean is_sensitive = FALSE;
4386 PsppireSheetButton *
4387 button = psppire_sheet_model_get_column_button (sheet->model, col);
4389 allocation.x = psppire_axis_start_pixel (sheet->haxis, col)
4391 allocation.x -= sheet->hadjustment->value;
4393 allocation.height = sheet->column_title_area.height;
4394 allocation.width = psppire_axis_unit_size (sheet->haxis, col);
4395 is_sensitive = psppire_sheet_model_get_column_sensitivity (sheet->model, col);
4397 draw_button (sheet, sheet->column_title_window,
4398 button, is_sensitive, allocation);
4401 gdk_window_end_paint (sheet->column_title_window);
4406 draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4410 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4412 if (!sheet->row_titles_visible) return;
4414 g_return_if_fail (first >= min_visible_row (sheet));
4415 g_return_if_fail (last <= max_visible_row (sheet));
4418 rect.width = sheet->row_title_area.width;
4419 rect.y = psppire_axis_start_pixel (sheet->vaxis, first) + CELL_SPACING;
4420 rect.height = psppire_axis_start_pixel (sheet->vaxis, last) + CELL_SPACING
4421 + psppire_axis_unit_size (sheet->vaxis, last);
4423 rect.y -= sheet->vadjustment->value;
4425 minimize_int (&rect.height, sheet->row_title_area.height);
4426 maximize_int (&rect.y, 0);
4428 gdk_window_begin_paint_rect (sheet->row_title_window, &rect);
4429 for (row = first; row <= last; ++row)
4431 GdkRectangle allocation;
4433 gboolean is_sensitive = FALSE;
4435 PsppireSheetButton *button =
4436 psppire_sheet_model_get_row_button (sheet->model, row);
4438 allocation.y = psppire_axis_start_pixel (sheet->vaxis, row)
4440 allocation.y -= sheet->vadjustment->value;
4442 allocation.width = sheet->row_title_area.width;
4443 allocation.height = psppire_axis_unit_size (sheet->vaxis, row);
4444 is_sensitive = psppire_sheet_model_get_row_sensitivity (sheet->model, row);
4446 draw_button (sheet, sheet->row_title_window,
4447 button, is_sensitive, allocation);
4450 gdk_window_end_paint (sheet->row_title_window);
4457 * vadjustment_value_changed
4458 * hadjustment_value_changed */
4462 update_adjustment (GtkAdjustment *adj, PsppireAxis *axis, gint page_size)
4465 (adj->value + adj->page_size)
4467 (adj->upper - adj->lower);
4469 const glong last_item = psppire_axis_unit_count (axis) - 1;
4471 if (isnan (position) || position < 0)
4475 psppire_axis_start_pixel (axis, last_item)
4477 psppire_axis_unit_size (axis, last_item)
4481 adj->page_size = page_size;
4484 adj->value = position * (adj->upper - adj->lower) - adj->page_size;
4486 if ( adj->value < adj->lower)
4487 adj->value = adj->lower;
4490 gtk_adjustment_changed (adj);
4495 adjust_scrollbars (PsppireSheet *sheet)
4499 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4502 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
4504 if ( sheet->row_titles_visible)
4505 width -= sheet->row_title_area.width;
4507 if (sheet->column_titles_visible)
4508 height -= sheet->column_title_area.height;
4510 if (sheet->vadjustment)
4512 glong last_row = psppire_axis_unit_count (sheet->vaxis) - 1;
4514 sheet->vadjustment->step_increment =
4516 psppire_axis_unit_size (sheet->vaxis, last_row);
4518 sheet->vadjustment->page_increment =
4520 sheet->column_title_area.height -
4521 psppire_axis_unit_size (sheet->vaxis, last_row);
4523 update_adjustment (sheet->vadjustment, sheet->vaxis, height);
4526 if (sheet->hadjustment)
4528 gint last_col = psppire_axis_unit_count (sheet->haxis) - 1;
4529 sheet->hadjustment->step_increment = 1;
4531 sheet->hadjustment->page_increment = width;
4533 sheet->hadjustment->upper =
4534 psppire_axis_start_pixel (sheet->haxis, last_col)
4536 psppire_axis_unit_size (sheet->haxis, last_col)
4539 update_adjustment (sheet->hadjustment, sheet->haxis, width);
4543 /* Subtracts the region of WIDGET from REGION */
4545 subtract_widget_region (GdkRegion *region, GtkWidget *widget)
4548 GdkRectangle intersect;
4551 gdk_region_get_clipbox (region, &rect);
4552 gtk_widget_intersect (widget,
4556 region2 = gdk_region_rectangle (&intersect);
4557 gdk_region_subtract (region, region2);
4558 gdk_region_destroy (region2);
4562 vadjustment_value_changed (GtkAdjustment *adjustment,
4566 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4568 g_return_if_fail (adjustment != NULL);
4570 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
4572 gtk_widget_hide (sheet->entry_widget);
4575 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
4577 subtract_widget_region (region, sheet->button);
4578 gdk_window_begin_paint_region (sheet->sheet_window, region);
4580 draw_sheet_region (sheet, region);
4582 draw_row_title_buttons (sheet);
4583 psppire_sheet_draw_active_cell (sheet);
4585 gdk_window_end_paint (sheet->sheet_window);
4586 gdk_region_destroy (region);
4591 hadjustment_value_changed (GtkAdjustment *adjustment,
4595 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4597 g_return_if_fail (adjustment != NULL);
4599 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
4601 gtk_widget_hide (sheet->entry_widget);
4605 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
4607 subtract_widget_region (region, sheet->button);
4608 gdk_window_begin_paint_region (sheet->sheet_window, region);
4610 draw_sheet_region (sheet, region);
4612 draw_column_title_buttons (sheet);
4614 psppire_sheet_draw_active_cell (sheet);
4616 gdk_window_end_paint (sheet->sheet_window);
4618 gdk_region_destroy (region);
4622 /* COLUMN RESIZING */
4624 draw_xor_vline (PsppireSheet *sheet)
4627 gint xpos = sheet->x_drag;
4628 gdk_drawable_get_size (sheet->sheet_window,
4631 if (sheet->row_titles_visible)
4632 xpos += sheet->row_title_area.width;
4634 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
4636 sheet->column_title_area.height,
4638 height + CELL_SPACING);
4643 draw_xor_hline (PsppireSheet *sheet)
4647 gint ypos = sheet->y_drag;
4649 gdk_drawable_get_size (sheet->sheet_window,
4653 if (sheet->column_titles_visible)
4654 ypos += sheet->column_title_area.height;
4656 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
4657 sheet->row_title_area.width,
4659 width + CELL_SPACING,
4663 /* SELECTED RANGE */
4665 draw_xor_rectangle (PsppireSheet *sheet, PsppireSheetRange range)
4668 GdkRectangle clip_area, area;
4671 area.x = psppire_axis_start_pixel (sheet->haxis, range.col0);
4672 area.y = psppire_axis_start_pixel (sheet->vaxis, range.row0);
4673 area.width = psppire_axis_start_pixel (sheet->haxis, range.coli)- area.x+
4674 psppire_axis_unit_size (sheet->haxis, range.coli);
4675 area.height = psppire_axis_start_pixel (sheet->vaxis, range.rowi)- area.y +
4676 psppire_axis_unit_size (sheet->vaxis, range.rowi);
4678 clip_area.x = sheet->row_title_area.width;
4679 clip_area.y = sheet->column_title_area.height;
4681 gdk_drawable_get_size (sheet->sheet_window,
4682 &clip_area.width, &clip_area.height);
4684 if (!sheet->row_titles_visible) clip_area.x = 0;
4685 if (!sheet->column_titles_visible) clip_area.y = 0;
4689 area.width = area.width + area.x;
4692 if (area.width > clip_area.width) area.width = clip_area.width + 10;
4695 area.height = area.height + area.y;
4698 if (area.height > clip_area.height) area.height = clip_area.height + 10;
4702 clip_area.width += 3;
4703 clip_area.height += 3;
4705 gdk_gc_get_values (sheet->xor_gc, &values);
4707 gdk_gc_set_clip_rectangle (sheet->xor_gc, &clip_area);
4709 gdk_draw_rectangle (sheet->sheet_window,
4712 area.x + i, area.y + i,
4713 area.width - 2 * i, area.height - 2 * i);
4716 gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
4718 gdk_gc_set_foreground (sheet->xor_gc, &values.foreground);
4723 set_column_width (PsppireSheet *sheet,
4727 g_return_if_fail (sheet != NULL);
4728 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
4730 if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis))
4736 psppire_axis_resize (sheet->haxis, column,
4737 width - sheet->cell_padding->left -
4738 sheet->cell_padding->right);
4740 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4742 draw_column_title_buttons (sheet);
4743 adjust_scrollbars (sheet);
4744 psppire_sheet_size_allocate_entry (sheet);
4745 redraw_range (sheet, NULL);
4750 set_row_height (PsppireSheet *sheet,
4754 g_return_if_fail (sheet != NULL);
4755 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
4757 if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis))
4763 psppire_axis_resize (sheet->vaxis, row,
4764 height - sheet->cell_padding->top -
4765 sheet->cell_padding->bottom);
4767 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) )
4769 draw_row_title_buttons (sheet);
4770 adjust_scrollbars (sheet);
4771 psppire_sheet_size_allocate_entry (sheet);
4772 redraw_range (sheet, NULL);
4777 psppire_sheet_get_attributes (const PsppireSheet *sheet, gint row, gint col,
4778 PsppireSheetCellAttr *attr)
4781 const GtkJustification *j ;
4782 GdkColormap *colormap;
4784 g_return_val_if_fail (sheet != NULL, FALSE);
4785 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), FALSE);
4787 if (row < 0 || col < 0) return FALSE;
4789 attr->foreground = GTK_WIDGET (sheet)->style->black;
4790 attr->background = sheet->color[BG_COLOR];
4792 attr->border.width = 0;
4793 attr->border.line_style = GDK_LINE_SOLID;
4794 attr->border.cap_style = GDK_CAP_NOT_LAST;
4795 attr->border.join_style = GDK_JOIN_MITER;
4796 attr->border.mask = 0;
4797 attr->border.color = GTK_WIDGET (sheet)->style->black;
4799 colormap = gtk_widget_get_colormap (GTK_WIDGET (sheet));
4800 fg = psppire_sheet_model_get_foreground (sheet->model, row, col);
4803 gdk_colormap_alloc_color (colormap, fg, TRUE, TRUE);
4804 attr->foreground = *fg;
4807 bg = psppire_sheet_model_get_background (sheet->model, row, col);
4810 gdk_colormap_alloc_color (colormap, bg, TRUE, TRUE);
4811 attr->background = *bg;
4814 attr->justification =
4815 psppire_sheet_model_get_column_justification (sheet->model, col);
4817 j = psppire_sheet_model_get_justification (sheet->model, row, col);
4819 attr->justification = *j;
4825 psppire_sheet_button_size_request (PsppireSheet *sheet,
4826 const PsppireSheetButton *button,
4827 GtkRequisition *button_requisition)
4829 GtkRequisition requisition;
4830 GtkRequisition label_requisition;
4832 label_requisition.height = DEFAULT_ROW_HEIGHT;
4833 label_requisition.width = COLUMN_MIN_WIDTH;
4835 requisition.height = DEFAULT_ROW_HEIGHT;
4836 requisition.width = COLUMN_MIN_WIDTH;
4839 *button_requisition = requisition;
4840 button_requisition->width = MAX (requisition.width, label_requisition.width);
4841 button_requisition->height = MAX (requisition.height, label_requisition.height);
4846 psppire_sheet_forall (GtkContainer *container,
4847 gboolean include_internals,
4848 GtkCallback callback,
4849 gpointer callback_data)
4851 PsppireSheet *sheet = PSPPIRE_SHEET (container);
4853 g_return_if_fail (callback != NULL);
4855 if (sheet->button && sheet->button->parent)
4856 (* callback) (sheet->button, callback_data);
4858 if (sheet->entry_widget && GTK_IS_CONTAINER (sheet->entry_widget))
4859 (* callback) (sheet->entry_widget, callback_data);
4864 psppire_sheet_get_model (const PsppireSheet *sheet)
4866 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
4868 return sheet->model;
4872 PsppireSheetButton *
4873 psppire_sheet_button_new (void)
4875 PsppireSheetButton *button = g_malloc (sizeof (PsppireSheetButton));
4877 button->state = GTK_STATE_NORMAL;
4878 button->label = NULL;
4879 button->label_visible = TRUE;
4880 button->justification = GTK_JUSTIFY_FILL;
4881 button->overstruck = FALSE;
4888 psppire_sheet_button_free (PsppireSheetButton *button)
4890 if (!button) return ;
4892 g_free (button->label);
4897 append_cell_text (GString *string, const PsppireSheet *sheet, gint r, gint c)
4899 gchar *celltext = psppire_sheet_cell_get_text (sheet, r, c);
4901 if ( NULL == celltext)
4904 g_string_append (string, celltext);
4910 range_to_text (const PsppireSheet *sheet)
4915 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
4918 string = g_string_sized_new (80);
4920 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
4922 for (c = sheet->range.col0; c < sheet->range.coli; ++c)
4924 append_cell_text (string, sheet, r, c);
4925 g_string_append (string, "\t");
4927 append_cell_text (string, sheet, r, c);
4928 if ( r < sheet->range.rowi)
4929 g_string_append (string, "\n");
4936 range_to_html (const PsppireSheet *sheet)
4941 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
4944 string = g_string_sized_new (480);
4946 g_string_append (string, "<html>\n");
4947 g_string_append (string, "<body>\n");
4948 g_string_append (string, "<table>\n");
4949 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
4951 g_string_append (string, "<tr>\n");
4952 for (c = sheet->range.col0; c <= sheet->range.coli; ++c)
4954 g_string_append (string, "<td>");
4955 append_cell_text (string, sheet, r, c);
4956 g_string_append (string, "</td>\n");
4958 g_string_append (string, "</tr>\n");
4960 g_string_append (string, "</table>\n");
4961 g_string_append (string, "</body>\n");
4962 g_string_append (string, "</html>\n");
4974 primary_get_cb (GtkClipboard *clipboard,
4975 GtkSelectionData *selection_data,
4979 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4980 GString *string = NULL;
4984 case SELECT_FMT_TEXT:
4985 string = range_to_text (sheet);
4987 case SELECT_FMT_HTML:
4988 string = range_to_html (sheet);
4991 g_assert_not_reached ();
4994 gtk_selection_data_set (selection_data, selection_data->target,
4996 (const guchar *) string->str, string->len);
4997 g_string_free (string, TRUE);
5001 primary_clear_cb (GtkClipboard *clipboard,
5004 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5005 if ( ! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
5008 psppire_sheet_unselect_range (sheet);
5012 psppire_sheet_update_primary_selection (PsppireSheet *sheet)
5014 static const GtkTargetEntry targets[] = {
5015 { "UTF8_STRING", 0, SELECT_FMT_TEXT },
5016 { "STRING", 0, SELECT_FMT_TEXT },
5017 { "TEXT", 0, SELECT_FMT_TEXT },
5018 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
5019 { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
5020 { "text/plain", 0, SELECT_FMT_TEXT },
5021 { "text/html", 0, SELECT_FMT_HTML }
5024 GtkClipboard *clipboard;
5026 if (!GTK_WIDGET_REALIZED (sheet))
5029 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (sheet),
5030 GDK_SELECTION_PRIMARY);
5032 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
5034 if (!gtk_clipboard_set_with_owner (clipboard, targets,
5035 G_N_ELEMENTS (targets),
5036 primary_get_cb, primary_clear_cb,
5038 primary_clear_cb (clipboard, sheet);
5042 if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (sheet))
5043 gtk_clipboard_clear (clipboard);