1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Gnumeric's extended canvas used to display a pane
6 * Miguel de Icaza (miguel@kernel.org)
7 * Jody Goldberg (jody@gnome.org)
9 #include <gnumeric-config.h>
12 #include "gnm-pane-impl.h"
15 #include "sheet-control-gui-priv.h"
19 #include "selection.h"
20 #include "parse-util.h"
23 #include "sheet-view.h"
24 #include "application.h"
25 #include "workbook-view.h"
26 #include "wbc-gtk-impl.h"
28 #include "workbook-cmd-format.h"
31 #include "clipboard.h"
32 #include "sheet-filter-combo.h"
33 #include "widgets/gnm-cell-combo-view.h"
35 #include "item-cursor.h"
36 #include "item-edit.h"
37 #include "item-grid.h"
38 #include "gnumeric-conf.h"
40 #include <gsf/gsf-impl-utils.h>
43 #include <gdk/gdkkeysyms.h>
47 #define SCROLL_LOCK_MASK GDK_MOD5_MASK
49 typedef GocCanvasClass GnmPaneClass
;
50 static GocCanvasClass
*parent_klass
;
52 static void cb_pane_popup_menu (GnmPane
*pane
);
53 static void gnm_pane_clear_obj_size_tip (GnmPane
*pane
);
54 static void gnm_pane_display_obj_size_tip (GnmPane
*pane
, GocItem
*ctrl_pt
);
57 * For now, application/x-gnumeric is disabled. It handles neither
58 * images nor graphs correctly.
60 static GtkTargetEntry
const drag_types_in
[] = {
61 {(char *) "GNUMERIC_SAME_PROC", GTK_TARGET_SAME_APP
, 0},
62 /* {(char *) "application/x-gnumeric", 0, 0}, */
65 static GtkTargetEntry
const drag_types_out
[] = {
66 {(char *) "GNUMERIC_SAME_PROC", GTK_TARGET_SAME_APP
, 0},
67 {(char *) "application/x-gnumeric", 0, 0},
71 gnm_pane_guru_key (WBCGtk
const *wbcg
, GdkEvent
*event
)
73 GtkWidget
*entry
, *guru
= wbc_gtk_get_guru (wbcg
);
78 entry
= wbcg_get_entry_underlying (wbcg
);
79 gtk_widget_event (entry
? entry
: guru
, event
);
84 gnm_pane_object_key_press (GnmPane
*pane
, GdkEventKey
*ev
)
86 SheetControlGUI
*scg
= pane
->simple
.scg
;
87 SheetControl
*sc
= GNM_SHEET_CONTROL (scg
);
88 gboolean
const shift
= 0 != (ev
->state
& GDK_SHIFT_MASK
);
89 gboolean
const control
= 0 != (ev
->state
& GDK_CONTROL_MASK
);
90 gboolean
const alt
= 0 != (ev
->state
& GDK_MOD1_MASK
);
91 gboolean
const symmetric
= control
&& alt
;
92 double const delta
= 1.0 / GOC_CANVAS (pane
)->pixels_per_unit
;
97 gnm_app_clipboard_unant ();
100 case GDK_KEY_BackSpace
: /* Ick! */
101 case GDK_KEY_KP_Delete
:
103 if (scg
->selected_objects
!= NULL
) {
104 cmd_objects_delete (sc
->wbc
,
105 go_hash_keys (scg
->selected_objects
), NULL
);
112 case GDK_KEY_ISO_Left_Tab
:
114 if ((scg_sheet (scg
))->sheet_objects
!= NULL
) {
115 scg_object_select_next (scg
, (ev
->state
& GDK_SHIFT_MASK
) != 0);
120 case GDK_KEY_KP_Left
: case GDK_KEY_Left
:
121 scg_objects_nudge (scg
, pane
, (alt
? 4 : (control
? 3 : 8)), -delta
, 0, symmetric
, shift
);
122 gnm_pane_display_obj_size_tip (pane
, NULL
);
124 case GDK_KEY_KP_Right
: case GDK_KEY_Right
:
125 scg_objects_nudge (scg
, pane
, (alt
? 4 : (control
? 3 : 8)), delta
, 0, symmetric
, shift
);
126 gnm_pane_display_obj_size_tip (pane
, NULL
);
128 case GDK_KEY_KP_Up
: case GDK_KEY_Up
:
129 scg_objects_nudge (scg
, pane
, (alt
? 6 : (control
? 1 : 8)), 0, -delta
, symmetric
, shift
);
130 gnm_pane_display_obj_size_tip (pane
, NULL
);
132 case GDK_KEY_KP_Down
: case GDK_KEY_Down
:
133 scg_objects_nudge (scg
, pane
, (alt
? 6 : (control
? 1 : 8)), 0, delta
, symmetric
, shift
);
134 gnm_pane_display_obj_size_tip (pane
, NULL
);
144 gnm_pane_key_mode_sheet (GnmPane
*pane
, GdkEventKey
*kevent
,
145 gboolean allow_rangesel
)
147 GdkEvent
*event
= (GdkEvent
*)kevent
;
148 SheetControlGUI
*scg
= pane
->simple
.scg
;
149 SheetControl
*sc
= (SheetControl
*) scg
;
150 SheetView
*sv
= sc
->view
;
151 Sheet
*sheet
= sv
->sheet
;
152 WBCGtk
*wbcg
= scg
->wbcg
;
153 WorkbookControl
* wbc
= scg_wbc(scg
);
154 Workbook
* wb
= wb_control_get_workbook(wbc
);
155 gboolean delayed_movement
= FALSE
;
156 gboolean jump_to_bounds
;
157 gboolean is_enter
= FALSE
;
160 void (*movefn
) (SheetControlGUI
*, int n
, gboolean jump
, gboolean horiz
);
161 gboolean transition_keys
= gnm_conf_get_core_gui_editing_transitionkeys ();
162 gboolean
const end_mode
= wbcg
->last_key_was_end
;
163 GdkModifierType event_state
;
165 (void)gdk_event_get_state (event
, &event_state
);
166 state
= gnm_filter_modifiers (event_state
);
167 jump_to_bounds
= (event_state
& GDK_CONTROL_MASK
) != 0;
169 /* Update end-mode for magic end key stuff. */
170 if (kevent
->keyval
!= GDK_KEY_End
&& kevent
->keyval
!= GDK_KEY_KP_End
)
171 wbcg_set_end_mode (wbcg
, FALSE
);
174 movefn
= (event_state
& GDK_SHIFT_MASK
)
175 ? scg_rangesel_extend
178 movefn
= (event_state
& GDK_SHIFT_MASK
)
182 switch (kevent
->keyval
) {
184 scg_select_all (scg
);
186 case GDK_KEY_KP_Left
:
188 if (event_state
& GDK_MOD1_MASK
)
189 return TRUE
; /* Alt is used for accelerators */
191 if (event_state
& SCROLL_LOCK_MASK
)
192 scg_set_left_col (scg
, pane
->first
.col
- 1);
193 else if (transition_keys
&& jump_to_bounds
) {
194 delayed_movement
= TRUE
;
195 scg_queue_movement (scg
, movefn
,
196 -(pane
->last_visible
.col
- pane
->first
.col
),
199 (*movefn
) (scg
, sheet
->text_is_rtl
? 1 : -1,
200 jump_to_bounds
|| end_mode
, TRUE
);
203 case GDK_KEY_KP_Right
:
205 if (event_state
& GDK_MOD1_MASK
)
206 return TRUE
; /* Alt is used for accelerators */
208 if (event_state
& SCROLL_LOCK_MASK
)
209 scg_set_left_col (scg
, pane
->first
.col
+ 1);
210 else if (transition_keys
&& jump_to_bounds
) {
211 delayed_movement
= TRUE
;
212 scg_queue_movement (scg
, movefn
,
213 pane
->last_visible
.col
- pane
->first
.col
,
216 (*movefn
) (scg
, sheet
->text_is_rtl
? -1 : 1,
217 jump_to_bounds
|| end_mode
, TRUE
);
222 if (event_state
& SCROLL_LOCK_MASK
)
223 scg_set_top_row (scg
, pane
->first
.row
- 1);
224 else if (transition_keys
&& jump_to_bounds
) {
225 delayed_movement
= TRUE
;
226 scg_queue_movement (scg
, movefn
,
227 -(pane
->last_visible
.row
- pane
->first
.row
),
230 (*movefn
) (scg
, -1, jump_to_bounds
|| end_mode
, FALSE
);
233 case GDK_KEY_KP_Down
:
235 if (gnm_filter_modifiers (event_state
) == GDK_MOD1_MASK
) {
236 /* 1) Any in cell combos ? */
237 SheetObject
*so
= sv_wbv (sv
)->in_cell_combo
;
239 /* 2) How about any autofilters ? */
242 GSList
*objs
= sheet_objects_get (sheet
,
243 range_init_cellpos (&r
, &sv
->edit_pos
),
244 GNM_FILTER_COMBO_TYPE
);
246 so
= objs
->data
, g_slist_free (objs
);
250 SheetObjectView
*sov
= sheet_object_get_view (so
,
251 (SheetObjectViewContainer
*)pane
);
252 gnm_cell_combo_view_popdown
254 gdk_event_get_time (event
));
259 if (event_state
& SCROLL_LOCK_MASK
)
260 scg_set_top_row (scg
, pane
->first
.row
+ 1);
261 else if (transition_keys
&& jump_to_bounds
) {
262 delayed_movement
= TRUE
;
263 scg_queue_movement (scg
, movefn
,
264 pane
->last_visible
.row
- pane
->first
.row
,
267 (*movefn
) (scg
, 1, jump_to_bounds
|| end_mode
, FALSE
);
270 case GDK_KEY_KP_Page_Up
:
271 case GDK_KEY_Page_Up
:
272 if (event_state
& GDK_CONTROL_MASK
) {
273 if (event_state
& GDK_SHIFT_MASK
) {
274 WorkbookSheetState
* old_state
= workbook_sheet_state_new(wb
);
275 int old_pos
= sheet
->index_in_wb
;
278 workbook_sheet_move(sheet
, -1);
279 cmd_reorganize_sheets (wbc
, old_state
, sheet
);
282 gnm_notebook_prev_page (wbcg
->bnotebook
);
284 } else if ((event_state
& GDK_MOD1_MASK
) == 0) {
285 delayed_movement
= TRUE
;
286 scg_queue_movement (scg
, movefn
,
287 -(pane
->last_visible
.row
- pane
->first
.row
),
290 delayed_movement
= TRUE
;
291 scg_queue_movement (scg
, movefn
,
292 -(pane
->last_visible
.col
- pane
->first
.col
),
297 case GDK_KEY_KP_Page_Down
:
298 case GDK_KEY_Page_Down
:
300 if ((event_state
& GDK_CONTROL_MASK
) != 0){
301 if ((event_state
& GDK_SHIFT_MASK
) != 0){
302 WorkbookSheetState
* old_state
= workbook_sheet_state_new(wb
);
303 int num_sheets
= workbook_sheet_count(wb
);
304 gint old_pos
= sheet
->index_in_wb
;
306 if (old_pos
< num_sheets
- 1){
307 workbook_sheet_move(sheet
, 1);
308 cmd_reorganize_sheets (wbc
, old_state
, sheet
);
311 gnm_notebook_next_page (wbcg
->bnotebook
);
313 } else if ((event_state
& GDK_MOD1_MASK
) == 0) {
314 delayed_movement
= TRUE
;
315 scg_queue_movement (scg
, movefn
,
316 pane
->last_visible
.row
- pane
->first
.row
,
319 delayed_movement
= TRUE
;
320 scg_queue_movement (scg
, movefn
,
321 pane
->last_visible
.col
- pane
->first
.col
,
326 case GDK_KEY_KP_Home
:
328 if (event_state
& SCROLL_LOCK_MASK
) {
329 scg_set_left_col (scg
, sv
->edit_pos
.col
);
330 scg_set_top_row (scg
, sv
->edit_pos
.row
);
331 } else if (end_mode
) {
332 /* Same as ctrl-end. */
333 GnmRange r
= sheet_get_extent (sheet
, FALSE
, TRUE
);
334 (*movefn
) (scg
, r
.end
.col
- sv
->edit_pos
.col
, FALSE
, TRUE
);
335 (*movefn
)(scg
, r
.end
.row
- sv
->edit_pos
.row
, FALSE
, FALSE
);
337 /* do the ctrl-home jump to A1 in 2 steps */
338 (*movefn
)(scg
, -gnm_sheet_get_max_cols (sheet
), FALSE
, TRUE
);
339 if ((event_state
& GDK_CONTROL_MASK
) || transition_keys
)
340 (*movefn
)(scg
, -gnm_sheet_get_max_rows (sheet
), FALSE
, FALSE
);
346 if (event_state
& SCROLL_LOCK_MASK
) {
347 int new_col
= sv
->edit_pos
.col
- (pane
->last_full
.col
- pane
->first
.col
);
348 int new_row
= sv
->edit_pos
.row
- (pane
->last_full
.row
- pane
->first
.row
);
349 scg_set_left_col (scg
, new_col
);
350 scg_set_top_row (scg
, new_row
);
351 } else if ((event_state
& GDK_CONTROL_MASK
)) {
352 GnmRange r
= sheet_get_extent (sheet
, FALSE
, TRUE
);
354 /* do the ctrl-end jump to the extent in 2 steps */
355 (*movefn
)(scg
, r
.end
.col
- sv
->edit_pos
.col
, FALSE
, TRUE
);
356 (*movefn
)(scg
, r
.end
.row
- sv
->edit_pos
.row
, FALSE
, FALSE
);
357 } else /* toggle end mode */
358 wbcg_set_end_mode (wbcg
, !end_mode
);
361 case GDK_KEY_KP_Insert
:
363 if (gnm_pane_guru_key (wbcg
, event
))
365 if (state
== GDK_CONTROL_MASK
)
366 gnm_sheet_view_selection_copy (sv
, GNM_WBC (wbcg
));
367 else if (state
== GDK_SHIFT_MASK
)
368 cmd_paste_to_selection (GNM_WBC (wbcg
), sv
, PASTE_DEFAULT
);
371 case GDK_KEY_BackSpace
:
372 if (wbcg_is_editing (wbcg
))
374 else if (!wbcg_is_editing (wbcg
) && (event_state
& GDK_CONTROL_MASK
) != 0) {
375 /* Re-center the view on the active cell */
376 scg_make_cell_visible (scg
, sv
->edit_pos
.col
,
377 sv
->edit_pos
.row
, FALSE
, TRUE
);
382 case GDK_KEY_KP_Delete
:
384 if (wbcg_is_editing (wbcg
)) {
385 /* stop auto-completion. then do a quick and cheesy update */
386 wbcg_auto_complete_destroy (wbcg
);
387 SCG_FOREACH_PANE (scg
, pane
, {
389 goc_item_invalidate (GOC_ITEM (pane
->editor
));
393 if (gnm_pane_guru_key (wbcg
, event
))
395 if (state
== GDK_SHIFT_MASK
) {
397 gnm_sheet_view_selection_cut (sv
, GNM_WBC (wbcg
));
399 cmd_selection_clear (GNM_WBC (wbcg
), CLEAR_VALUES
);
403 * NOTE : Keep these in sync with the condition
406 case GDK_KEY_KP_Enter
:
408 if (wbcg_is_editing (wbcg
) &&
409 (state
== GDK_CONTROL_MASK
||
410 state
== (GDK_CONTROL_MASK
|GDK_SHIFT_MASK
) ||
411 gnm_filter_modifiers (event_state
) == GDK_MOD1_MASK
))
412 /* Forward the keystroke to the input line */
413 return gtk_widget_event (
414 wbcg_get_entry_underlying (wbcg
), (GdkEvent
*) event
);
419 case GDK_KEY_ISO_Left_Tab
:
421 if (gnm_pane_guru_key (wbcg
, event
))
424 /* Be careful to restore the editing sheet if we are editing */
425 if (wbcg_is_editing (wbcg
))
426 sheet
= wbcg
->editing_sheet
;
428 /* registering the cmd clears it, restore it afterwards */
429 first_tab_col
= sv
->first_tab_col
;
431 if (wbcg_edit_finish (wbcg
, WBC_EDIT_ACCEPT
, NULL
)) {
432 GODirection dir
= gnm_conf_get_core_gui_editing_enter_moves_dir ();
434 sv
->first_tab_col
= first_tab_col
;
436 if ((event_state
& GDK_MOD1_MASK
) &&
437 (event_state
& GDK_CONTROL_MASK
) &&
439 if (event_state
& GDK_SHIFT_MASK
)
440 workbook_cmd_dec_indent (sc
->wbc
);
442 workbook_cmd_inc_indent (sc
->wbc
);
443 } else if (!is_enter
|| dir
!= GO_DIRECTION_NONE
) {
444 gboolean forward
= TRUE
;
445 gboolean horizontal
= TRUE
;
447 horizontal
= go_direction_is_horizontal (dir
);
448 forward
= go_direction_is_forward (dir
);
449 } else if ((event_state
& GDK_CONTROL_MASK
) &&
450 ((sc_sheet (sc
))->sheet_objects
!= NULL
)) {
451 scg_object_select_next
452 (scg
, (event_state
& GDK_SHIFT_MASK
) != 0);
456 if (event_state
& GDK_SHIFT_MASK
)
459 sv_selection_walk_step (sv
, forward
, horizontal
);
461 /* invalidate, in case Enter direction changes */
463 sv
->first_tab_col
= -1;
469 wbcg_edit_finish (wbcg
, WBC_EDIT_REJECT
, NULL
);
470 gnm_app_clipboard_unant ();
474 if (wbcg_is_editing (wbcg
))
475 return gtk_widget_event (
476 wbcg_get_entry_underlying (wbcg
), (GdkEvent
*) event
);
480 if (gnm_pane_guru_key (wbcg
, event
))
483 if (wbcg_is_editing (wbcg
)) {
484 GtkWidget
*entry
= (GtkWidget
*) wbcg_get_entry (wbcg
);
485 GtkWindow
*top
= wbcg_toplevel (wbcg
);
486 if (entry
!= gtk_window_get_focus (top
)) {
487 gtk_window_set_focus (top
, entry
);
491 if (!wbcg_edit_start (wbcg
, FALSE
, FALSE
))
492 return FALSE
; /* attempt to edit failed */
496 if (!wbcg_is_editing (wbcg
)) {
497 if ((event_state
& (GDK_MOD1_MASK
|GDK_CONTROL_MASK
)) != 0)
500 /* If the character is not printable do not start editing */
501 if (kevent
->length
== 0)
504 if (!wbcg_edit_start (wbcg
, TRUE
, TRUE
))
505 return FALSE
; /* attempt to edit failed */
507 scg_rangesel_stop (scg
, FALSE
);
510 /* Forward the keystroke to the input line */
511 return gtk_widget_event (wbcg_get_entry_underlying (wbcg
),
515 if (!delayed_movement
) {
516 if (wbcg_is_editing (wbcg
))
517 sheet_update_only_grid (sheet
);
519 sheet_update (sheet
);
526 gnm_pane_colrow_key_press (SheetControlGUI
*scg
, GdkEventKey
*event
,
527 gboolean allow_rangesel
)
529 SheetControl
*sc
= (SheetControl
*) scg
;
530 SheetView
*sv
= sc
->view
;
533 if (allow_rangesel
) {
534 if (scg
->rangesel
.active
)
535 target
= scg
->rangesel
.displayed
;
537 target
.start
= target
.end
= sv
->edit_pos_real
;
539 GnmRange
const *r
= selection_first_range (sv
, NULL
, NULL
);
545 if (event
->state
& GDK_SHIFT_MASK
) {
546 if (event
->state
& GDK_CONTROL_MASK
) /* full sheet */
547 /* TODO : How to handle ctrl-A too ? */
548 range_init_full_sheet (&target
, sv
->sheet
);
549 else { /* full row */
550 target
.start
.col
= 0;
551 target
.end
.col
= gnm_sheet_get_last_col (sv
->sheet
);
553 } else if (event
->state
& GDK_CONTROL_MASK
) { /* full col */
554 target
.start
.row
= 0;
555 target
.end
.row
= gnm_sheet_get_last_row (sv
->sheet
);
559 /* Accept during rangesel */
561 scg_rangesel_bound (scg
,
562 target
.start
.col
, target
.start
.row
,
563 target
.end
.col
, target
.end
.row
);
564 /* actually want the ctrl/shift space keys handled by the input module
565 * filters during an edit */
566 else if (!wbcg_is_editing (scg
->wbcg
))
567 sv_selection_set (sv
, &sv
->edit_pos
,
568 target
.start
.col
, target
.start
.row
,
569 target
.end
.col
, target
.end
.row
);
577 gnm_pane_key_press (GtkWidget
*widget
, GdkEventKey
*event
)
579 GnmPane
*pane
= GNM_PANE (widget
);
580 SheetControlGUI
*scg
= pane
->simple
.scg
;
581 gboolean allow_rangesel
;
583 switch (event
->keyval
) {
584 case GDK_KEY_Shift_L
: case GDK_KEY_Shift_R
:
585 case GDK_KEY_Alt_L
: case GDK_KEY_Alt_R
:
586 case GDK_KEY_Control_L
: case GDK_KEY_Control_R
:
587 return (*GTK_WIDGET_CLASS (parent_klass
)->key_press_event
) (widget
, event
);
590 /* Object manipulation */
591 if (scg
->selected_objects
!= NULL
||
592 scg
->wbcg
->new_object
!= NULL
) {
593 if (wbc_gtk_get_guru (scg
->wbcg
) == NULL
&&
594 gnm_pane_object_key_press (pane
, event
))
598 /* handle grabs after object keys to allow Esc to cancel, and arrows to
599 * fine tune position even while dragging */
600 if (scg
->grab_stack
> 0)
603 allow_rangesel
= wbcg_rangesel_possible (scg
->wbcg
);
605 /* handle ctrl/shift space before input-method filter steals it */
606 if (event
->keyval
== GDK_KEY_space
&&
607 gnm_pane_colrow_key_press (scg
, event
, allow_rangesel
))
610 pane
->insert_decimal
=
611 event
->keyval
== GDK_KEY_KP_Decimal
||
612 event
->keyval
== GDK_KEY_KP_Separator
;
614 if (gtk_im_context_filter_keypress (pane
->im_context
, event
))
617 gtk_im_context_reset (pane
->im_context
);
619 if (gnm_pane_key_mode_sheet (pane
, event
, allow_rangesel
))
622 return (*GTK_WIDGET_CLASS (parent_klass
)->key_press_event
) (widget
, event
);
626 gnm_pane_key_release (GtkWidget
*widget
, GdkEventKey
*event
)
628 GnmPane
*pane
= GNM_PANE (widget
);
629 SheetControl
*sc
= (SheetControl
*) pane
->simple
.scg
;
631 if (pane
->simple
.scg
->grab_stack
> 0)
634 if (gtk_im_context_filter_keypress (pane
->im_context
, event
))
637 * The status_region normally displays the current edit_pos
638 * When we extend the selection it changes to displaying the size of
639 * the selected region while we are selecting. When the shift key
640 * is released, or the mouse button is release we need to reset
641 * to displaying the edit pos.
643 if (pane
->simple
.scg
->selected_objects
== NULL
&&
644 (event
->keyval
== GDK_KEY_Shift_L
|| event
->keyval
== GDK_KEY_Shift_R
))
645 wb_view_selection_desc (wb_control_view (sc
->wbc
), TRUE
, NULL
);
647 return (*GTK_WIDGET_CLASS (parent_klass
)->key_release_event
) (widget
, event
);
651 gnm_pane_focus_in (GtkWidget
*widget
, GdkEventFocus
*event
)
653 GnmPane
*pane
= GNM_PANE (widget
);
654 gtk_im_context_focus_in (pane
->im_context
);
655 return (*GTK_WIDGET_CLASS (parent_klass
)->focus_in_event
) (widget
, event
);
659 gnm_pane_focus_out (GtkWidget
*widget
, GdkEventFocus
*event
)
661 gnm_pane_clear_obj_size_tip (GNM_PANE (widget
));
662 gtk_im_context_focus_out (GNM_PANE (widget
)->im_context
);
663 return (*GTK_WIDGET_CLASS (parent_klass
)->focus_out_event
) (widget
, event
);
667 gnm_pane_realize (GtkWidget
*w
)
669 if (GTK_WIDGET_CLASS (parent_klass
)->realize
)
670 (*GTK_WIDGET_CLASS (parent_klass
)->realize
) (w
);
672 gtk_im_context_set_client_window
673 (GNM_PANE (w
)->im_context
,
674 gtk_widget_get_window (gtk_widget_get_toplevel (w
)));
678 gnm_pane_unrealize (GtkWidget
*widget
)
682 pane
= GNM_PANE (widget
);
683 g_return_if_fail (pane
!= NULL
);
685 if (pane
->im_context
) {
686 gtk_im_context_set_client_window (pane
->im_context
, NULL
);
689 (*GTK_WIDGET_CLASS (parent_klass
)->unrealize
)(widget
);
693 gnm_pane_size_allocate (GtkWidget
*w
, GtkAllocation
*allocation
)
695 GnmPane
*pane
= GNM_PANE (w
);
696 (*GTK_WIDGET_CLASS (parent_klass
)->size_allocate
) (w
, allocation
);
697 gnm_pane_compute_visible_region (pane
, TRUE
);
701 gnm_pane_get_editable (GnmPane
const *pane
)
703 GnmExprEntry
*gee
= wbcg_get_entry_logical (pane
->simple
.scg
->wbcg
);
704 GtkEntry
*entry
= gnm_expr_entry_get_entry (gee
);
705 return GTK_EDITABLE (entry
);
709 cb_gnm_pane_commit (GtkIMContext
*context
, char const *str
, GnmPane
*pane
)
711 gint tmp_pos
, length
;
712 WBCGtk
*wbcg
= pane
->simple
.scg
->wbcg
;
713 GtkEditable
*editable
= gnm_pane_get_editable (pane
);
715 if (!wbcg_is_editing (wbcg
) && !wbcg_edit_start (wbcg
, TRUE
, TRUE
))
718 if (pane
->insert_decimal
) {
719 GString
const *s
= go_locale_get_decimal ();
723 length
= strlen (str
);
725 if (gtk_editable_get_selection_bounds (editable
, NULL
, NULL
))
726 gtk_editable_delete_selection (editable
);
728 tmp_pos
= gtk_editable_get_position (editable
);
729 if (gtk_entry_get_overwrite_mode (GTK_ENTRY (editable
)))
730 gtk_editable_delete_text (editable
,tmp_pos
,tmp_pos
+1);
733 tmp_pos
= gtk_editable_get_position (editable
);
734 gtk_editable_insert_text (editable
, str
, length
, &tmp_pos
);
735 gtk_editable_set_position (editable
, tmp_pos
);
739 cb_gnm_pane_preedit_start (GtkIMContext
*context
, GnmPane
*pane
)
741 WBCGtk
*wbcg
= pane
->simple
.scg
->wbcg
;
742 pane
->im_preedit_started
= TRUE
;
743 if (!wbcg_is_editing (wbcg
))
744 wbcg_edit_start (wbcg
, TRUE
, TRUE
);
748 cb_gnm_pane_preedit_changed (GtkIMContext
*context
, GnmPane
*pane
)
750 gchar
*preedit_string
;
753 WBCGtk
*wbcg
= pane
->simple
.scg
->wbcg
;
754 GtkEditable
*editable
= gnm_pane_get_editable (pane
);
755 if (!pane
->im_preedit_started
)
758 tmp_pos
= gtk_editable_get_position (editable
);
759 if (pane
->preedit_attrs
)
760 pango_attr_list_unref (pane
->preedit_attrs
);
761 gtk_im_context_get_preedit_string (pane
->im_context
, &preedit_string
, &pane
->preedit_attrs
, &cursor_pos
);
763 if (!wbcg_is_editing (wbcg
) && !wbcg_edit_start (wbcg
, FALSE
, TRUE
)) {
764 gtk_im_context_reset (pane
->im_context
);
765 pane
->preedit_length
= 0;
766 if (pane
->preedit_attrs
)
767 pango_attr_list_unref (pane
->preedit_attrs
);
768 pane
->preedit_attrs
= NULL
;
769 g_free (preedit_string
);
773 if (pane
->preedit_length
)
774 gtk_editable_delete_text (editable
,tmp_pos
,tmp_pos
+pane
->preedit_length
);
775 pane
->preedit_length
= strlen (preedit_string
);
777 if (pane
->preedit_length
)
778 gtk_editable_insert_text (editable
, preedit_string
, pane
->preedit_length
, &tmp_pos
);
779 g_free (preedit_string
);
783 cb_gnm_pane_preedit_end (GtkIMContext
*context
, GnmPane
*pane
)
785 pane
->im_preedit_started
= FALSE
;
789 cb_gnm_pane_retrieve_surrounding (GtkIMContext
*context
, GnmPane
*pane
)
791 GtkEditable
*editable
= gnm_pane_get_editable (pane
);
792 gchar
*surrounding
= gtk_editable_get_chars (editable
, 0, -1);
793 gint cur_pos
= gtk_editable_get_position (editable
);
795 gtk_im_context_set_surrounding (context
,
796 surrounding
, strlen (surrounding
),
797 g_utf8_offset_to_pointer (surrounding
, cur_pos
) - surrounding
);
799 g_free (surrounding
);
804 cb_gnm_pane_delete_surrounding (GtkIMContext
*context
,
809 GtkEditable
*editable
= gnm_pane_get_editable (pane
);
810 gint cur_pos
= gtk_editable_get_position (editable
);
811 gtk_editable_delete_text (editable
,
813 cur_pos
+ offset
+ n_chars
);
818 /* create views for the sheet objects now that we exist */
820 cb_pane_init_objs (GnmPane
*pane
)
822 Sheet
*sheet
= scg_sheet (pane
->simple
.scg
);
826 /* List is stored in reverse stacking order. Top of stack is
827 * first. On creation new foocanvas item get added to
828 * the front, so we need to create the views in reverse order */
829 list
= g_slist_reverse (g_slist_copy (sheet
->sheet_objects
));
830 for (ptr
= list
; ptr
!= NULL
; ptr
= ptr
->next
)
831 sheet_object_new_view (ptr
->data
,
832 (SheetObjectViewContainer
*)pane
);
838 cb_ctrl_pts_free (GocItem
**ctrl_pts
)
842 if (ctrl_pts
[i
] != NULL
)
843 g_object_unref (ctrl_pts
[i
]);
848 gnm_pane_dispose (GObject
*obj
)
850 GnmPane
*pane
= GNM_PANE (obj
);
852 if (pane
->col
.canvas
!= NULL
) {
853 gtk_widget_destroy (GTK_WIDGET (pane
->col
.canvas
));
854 g_object_unref (pane
->col
.canvas
);
855 pane
->col
.canvas
= NULL
;
858 if (pane
->row
.canvas
!= NULL
) {
859 gtk_widget_destroy (GTK_WIDGET (pane
->row
.canvas
));
860 g_object_unref (pane
->row
.canvas
);
861 pane
->row
.canvas
= NULL
;
864 if (pane
->im_context
) {
865 GtkIMContext
*imc
= pane
->im_context
;
867 pane
->im_context
= NULL
;
868 g_signal_handlers_disconnect_by_func
869 (imc
, cb_gnm_pane_commit
, pane
);
870 g_signal_handlers_disconnect_by_func
871 (imc
, cb_gnm_pane_preedit_start
, pane
);
872 g_signal_handlers_disconnect_by_func
873 (imc
, cb_gnm_pane_preedit_changed
, pane
);
874 g_signal_handlers_disconnect_by_func
875 (imc
, cb_gnm_pane_preedit_end
, pane
);
876 g_signal_handlers_disconnect_by_func
877 (imc
, cb_gnm_pane_retrieve_surrounding
, pane
);
878 g_signal_handlers_disconnect_by_func
879 (imc
, cb_gnm_pane_delete_surrounding
, pane
);
880 gtk_im_context_set_client_window (imc
, NULL
);
881 g_object_unref (imc
);
884 g_slist_free (pane
->cursor
.animated
);
885 pane
->cursor
.animated
= NULL
;
886 g_slist_free_full (pane
->cursor
.expr_range
, g_object_unref
);
887 pane
->cursor
.expr_range
= NULL
;
889 g_clear_object (&pane
->mouse_cursor
);
890 gnm_pane_clear_obj_size_tip (pane
);
892 if (pane
->drag
.ctrl_pts
) {
893 g_hash_table_destroy (pane
->drag
.ctrl_pts
);
894 pane
->drag
.ctrl_pts
= NULL
;
897 /* Be anal just in case we somehow manage to remove a pane
901 pane
->cursor
.std
= pane
->cursor
.rangesel
= pane
->cursor
.special
= NULL
;
902 pane
->size_guide
.guide
= NULL
;
903 pane
->size_guide
.start
= NULL
;
904 pane
->size_guide
.points
= NULL
;
906 G_OBJECT_CLASS (parent_klass
)->dispose (obj
);
910 gnm_pane_init (GnmPane
*pane
)
912 GocCanvas
*canvas
= GOC_CANVAS (pane
);
913 GocGroup
*root_group
= goc_canvas_get_root (canvas
);
915 pane
->grid_items
= goc_group_new (root_group
);
916 pane
->object_views
= goc_group_new (root_group
);
917 pane
->action_items
= goc_group_new (root_group
);
919 pane
->first
.col
= pane
->last_full
.col
= pane
->last_visible
.col
= 0;
920 pane
->first
.row
= pane
->last_full
.row
= pane
->last_visible
.row
= 0;
921 pane
->first_offset
.x
= 0;
922 pane
->first_offset
.y
= 0;
925 pane
->mouse_cursor
= NULL
;
926 pane
->cursor
.rangesel
= NULL
;
927 pane
->cursor
.special
= NULL
;
928 pane
->cursor
.expr_range
= NULL
;
929 pane
->cursor
.animated
= NULL
;
930 pane
->size_tip
= NULL
;
932 pane
->slide_handler
= NULL
;
933 pane
->slide_data
= NULL
;
934 pane
->sliding_timer
= 0;
935 pane
->sliding_x
= pane
->sliding_dx
= -1;
936 pane
->sliding_y
= pane
->sliding_dy
= -1;
937 pane
->sliding_adjacent_h
= pane
->sliding_adjacent_v
= FALSE
;
939 pane
->drag
.button
= 0;
940 pane
->drag
.ctrl_pts
= g_hash_table_new_full (g_direct_hash
, g_direct_equal
,
941 NULL
, (GDestroyNotify
) cb_ctrl_pts_free
);
943 pane
->im_context
= gtk_im_multicontext_new ();
944 pane
->preedit_length
= 0;
945 pane
->preedit_attrs
= NULL
;
946 pane
->im_preedit_started
= FALSE
;
948 gtk_widget_set_can_focus (GTK_WIDGET (canvas
), TRUE
);
949 gtk_widget_set_can_default (GTK_WIDGET (canvas
), TRUE
);
951 g_signal_connect (G_OBJECT (pane
->im_context
), "commit",
952 G_CALLBACK (cb_gnm_pane_commit
), pane
);
953 g_signal_connect (G_OBJECT (pane
->im_context
), "preedit_start",
954 G_CALLBACK (cb_gnm_pane_preedit_start
), pane
);
955 g_signal_connect (G_OBJECT (pane
->im_context
), "preedit_changed",
956 G_CALLBACK (cb_gnm_pane_preedit_changed
), pane
);
957 g_signal_connect (G_OBJECT (pane
->im_context
), "preedit_end",
958 G_CALLBACK (cb_gnm_pane_preedit_end
), pane
);
959 g_signal_connect (G_OBJECT (pane
->im_context
), "retrieve_surrounding",
960 G_CALLBACK (cb_gnm_pane_retrieve_surrounding
),
962 g_signal_connect (G_OBJECT (pane
->im_context
), "delete_surrounding",
963 G_CALLBACK (cb_gnm_pane_delete_surrounding
),
968 gnm_pane_class_init (GnmPaneClass
*klass
)
970 GObjectClass
*gobject_class
= G_OBJECT_CLASS (klass
);
971 GtkWidgetClass
*widget_class
= (GtkWidgetClass
*) klass
;
973 parent_klass
= g_type_class_peek_parent (klass
);
975 gobject_class
->dispose
= gnm_pane_dispose
;
977 widget_class
->realize
= gnm_pane_realize
;
978 widget_class
->unrealize
= gnm_pane_unrealize
;
979 widget_class
->size_allocate
= gnm_pane_size_allocate
;
980 widget_class
->key_press_event
= gnm_pane_key_press
;
981 widget_class
->key_release_event
= gnm_pane_key_release
;
982 widget_class
->focus_in_event
= gnm_pane_focus_in
;
983 widget_class
->focus_out_event
= gnm_pane_focus_out
;
985 gtk_widget_class_install_style_property
987 g_param_spec_int ("function-indicator-size",
988 P_("Function Indicator Size"),
989 P_("Size of function indicator"),
995 gtk_widget_class_install_style_property
997 g_param_spec_int ("comment-indicator-size",
998 P_("comment Indicator Size"),
999 P_("Size of comment indicator"),
1005 gtk_widget_class_install_style_property
1007 g_param_spec_int ("resize-guide-width",
1008 P_("Resize Guide Width"),
1009 P_("With of the guides used for resizing columns and rows"),
1015 gtk_widget_class_install_style_property
1017 g_param_spec_int ("pane-resize-guide-width",
1018 P_("Pane Resize Guide Width"),
1019 P_("With of the guides used for resizing panes"),
1025 gtk_widget_class_install_style_property
1027 g_param_spec_int ("control-circle-size",
1028 P_("Control Circle Size"),
1029 P_("Size of control circle for sizing sheet objects"),
1035 gtk_widget_class_install_style_property
1037 g_param_spec_int ("control-circle-outline",
1038 P_("Control Circle Outline"),
1039 P_("Width of outline of control circle for sizing sheet objects"),
1046 GSF_CLASS (GnmPane
, gnm_pane
,
1047 gnm_pane_class_init
, gnm_pane_init
,
1048 GNM_SIMPLE_CANVAS_TYPE
)
1054 gnm_pane_header_init (GnmPane
*pane
, SheetControlGUI
*scg
,
1055 gboolean is_col_header
)
1057 Sheet
*sheet
= scg_sheet (scg
);
1058 GocCanvas
*canvas
= gnm_simple_canvas_new (scg
);
1059 GocGroup
*group
= goc_canvas_get_root (canvas
);
1060 GocItem
*item
= goc_item_new (group
,
1061 gnm_item_bar_get_type (),
1063 "IsColHeader", is_col_header
,
1066 /* give a non-constraining default in case something scrolls before we
1068 if (is_col_header
) {
1069 if (sheet
&& sheet
->text_is_rtl
)
1070 goc_canvas_set_direction (canvas
, GOC_DIRECTION_RTL
);
1071 pane
->col
.canvas
= g_object_ref_sink (canvas
);
1072 pane
->col
.item
= GNM_ITEM_BAR (item
);
1074 pane
->row
.canvas
= g_object_ref_sink (canvas
);
1075 pane
->row
.item
= GNM_ITEM_BAR (item
);
1078 pane
->size_guide
.points
= NULL
;
1079 pane
->size_guide
.start
= NULL
;
1080 pane
->size_guide
.guide
= NULL
;
1084 fabs (1. - sheet
->last_zoom_factor_used
) > 1e-6)
1085 goc_canvas_set_pixels_per_unit (canvas
, sheet
->last_zoom_factor_used
);
1089 cb_pane_drag_data_received (GtkWidget
*widget
, GdkDragContext
*context
,
1090 gint x
, gint y
, GtkSelectionData
*selection_data
,
1091 guint info
, guint time
, GnmPane
*pane
)
1095 if (gnm_debug_flag ("dnd")) {
1096 gchar
*target_name
= gdk_atom_name (gtk_selection_data_get_target (selection_data
));
1097 g_printerr ("drag-data-received - %s\n", target_name
);
1098 g_free (target_name
);
1101 goc_canvas_w2c (GOC_CANVAS (pane
), x
, y
, &wx
, &wy
);
1102 scg_drag_data_received (pane
->simple
.scg
,
1103 gtk_drag_get_source_widget (context
),
1104 wx
, wy
, selection_data
);
1108 cb_pane_drag_data_get (GtkWidget
*widget
, GdkDragContext
*context
,
1109 GtkSelectionData
*selection_data
,
1110 guint info
, guint time
,
1111 SheetControlGUI
*scg
)
1113 if (gnm_debug_flag ("dnd")) {
1114 gchar
*target_name
= gdk_atom_name (gtk_selection_data_get_target (selection_data
));
1115 g_printerr ("drag-data-get - %s \n", target_name
);
1116 g_free (target_name
);
1119 scg_drag_data_get (scg
, selection_data
);
1122 /* Move the rubber bands if we are the source */
1124 cb_pane_drag_motion (GtkWidget
*widget
, GdkDragContext
*context
,
1125 int x
, int y
, guint32 time
, GnmPane
*pane
)
1127 GtkWidget
*source_widget
= gtk_drag_get_source_widget (context
);
1128 SheetControlGUI
*scg
= GNM_PANE (widget
)->simple
.scg
;
1130 if ((GNM_IS_PANE (source_widget
) &&
1131 GNM_PANE (source_widget
)->simple
.scg
== scg
)) {
1133 GocCanvas
*canvas
= GOC_CANVAS (widget
);
1134 GdkModifierType mask
;
1135 GdkWindow
*window
= gtk_widget_get_parent_window (source_widget
);
1138 g_object_set_data (G_OBJECT (context
),
1139 "wbcg", scg_wbcg (scg
));
1140 goc_canvas_w2c (canvas
, x
, y
, &wx
, &wy
);
1141 wx
*= goc_canvas_get_pixels_per_unit (canvas
);
1142 wy
*= goc_canvas_get_pixels_per_unit (canvas
);
1144 gdk_window_get_device_position (window
,
1145 gdk_device_manager_get_client_pointer (gdk_display_get_device_manager (gdk_window_get_display (window
))),
1147 gnm_pane_objects_drag (GNM_PANE (source_widget
), NULL
,
1148 wx
, wy
, 8, FALSE
, (mask
& GDK_SHIFT_MASK
) != 0);
1149 gdk_drag_status (context
,
1150 (mask
& GDK_CONTROL_MASK
) != 0 ? GDK_ACTION_COPY
: GDK_ACTION_MOVE
,
1157 cb_pane_drag_end (GtkWidget
*widget
, GdkDragContext
*context
,
1158 GnmPane
*source_pane
)
1160 /* ungrab any grabbed item */
1161 GocItem
*item
= goc_canvas_get_grabbed_item (GOC_CANVAS (source_pane
));
1163 gnm_simple_canvas_ungrab (item
);
1164 /* sync the ctrl-pts with the object in case the drag was canceled. */
1165 gnm_pane_objects_drag (source_pane
, NULL
,
1166 source_pane
->drag
.origin_x
,
1167 source_pane
->drag
.origin_y
,
1169 source_pane
->drag
.had_motion
= FALSE
;
1170 source_pane
->drag
.button
= 0;
1174 * Move the rubber bands back to original position when curser leaves
1175 * the scg, but not when it moves to another pane. We use object data,
1176 * and rely on gtk sending drag_move to the new widget before sending
1177 * drag_leave to the old one.
1180 cb_pane_drag_leave (GtkWidget
*widget
, GdkDragContext
*context
,
1181 guint32 time
, GnmPane
*pane
)
1183 GtkWidget
*source_widget
= gtk_drag_get_source_widget (context
);
1184 GnmPane
*source_pane
;
1187 if (!source_widget
|| !GNM_IS_PANE (source_widget
)) return;
1189 source_pane
= GNM_PANE (source_widget
);
1191 wbcg
= scg_wbcg (source_pane
->simple
.scg
);
1192 if (wbcg
== g_object_get_data (G_OBJECT (context
), "wbcg"))
1195 gnm_pane_objects_drag (source_pane
, NULL
,
1196 source_pane
->drag
.origin_x
,
1197 source_pane
->drag
.origin_y
,
1199 source_pane
->drag
.had_motion
= FALSE
;
1203 gnm_pane_drag_dest_init (GnmPane
*pane
, SheetControlGUI
*scg
)
1205 GtkWidget
*widget
= GTK_WIDGET (pane
);
1207 gtk_drag_dest_set (widget
, GTK_DEST_DEFAULT_ALL
,
1208 drag_types_in
, G_N_ELEMENTS (drag_types_in
),
1209 GDK_ACTION_COPY
| GDK_ACTION_MOVE
);
1210 gtk_drag_dest_add_uri_targets (widget
);
1211 gtk_drag_dest_add_image_targets (widget
);
1212 gtk_drag_dest_add_text_targets (widget
);
1214 g_object_connect (G_OBJECT (widget
),
1215 "signal::drag-data-received", G_CALLBACK (cb_pane_drag_data_received
), pane
,
1216 "signal::drag-data-get", G_CALLBACK (cb_pane_drag_data_get
), scg
,
1217 "signal::drag-motion", G_CALLBACK (cb_pane_drag_motion
), pane
,
1218 "signal::drag-leave", G_CALLBACK (cb_pane_drag_leave
), pane
,
1219 "signal::drag-end", G_CALLBACK (cb_pane_drag_end
), pane
,
1224 gnm_pane_new (SheetControlGUI
*scg
,
1225 gboolean col_headers
, gboolean row_headers
, int index
)
1231 g_return_val_if_fail (GNM_IS_SCG (scg
), NULL
);
1233 pane
= g_object_new (GNM_PANE_TYPE
, NULL
);
1234 pane
->index
= index
;
1235 pane
->simple
.scg
= scg
;
1237 goc_canvas_set_document (GOC_CANVAS (pane
), wb_control_get_doc (scg_wbc (scg
)));
1238 if (NULL
!= (sheet
= scg_sheet (scg
)) &&
1239 fabs (1. - sheet
->last_zoom_factor_used
) > 1e-6)
1240 goc_canvas_set_pixels_per_unit (GOC_CANVAS (pane
),
1241 sheet
->last_zoom_factor_used
);
1243 gnm_pane_drag_dest_init (pane
, scg
);
1245 item
= goc_item_new (pane
->grid_items
,
1246 gnm_item_grid_get_type (),
1247 "SheetControlGUI", scg
,
1249 pane
->grid
= GNM_ITEM_GRID (item
);
1251 item
= goc_item_new (pane
->grid_items
,
1252 gnm_item_cursor_get_type (),
1253 "SheetControlGUI", scg
,
1255 pane
->cursor
.std
= GNM_ITEM_CURSOR (item
);
1257 gnm_pane_header_init (pane
, scg
, TRUE
);
1259 pane
->col
.canvas
= NULL
;
1261 gnm_pane_header_init (pane
, scg
, FALSE
);
1263 pane
->row
.canvas
= NULL
;
1265 g_signal_connect_swapped (pane
, "popup-menu",
1266 G_CALLBACK (cb_pane_popup_menu
), pane
);
1267 g_signal_connect_swapped (G_OBJECT (pane
), "realize",
1268 G_CALLBACK (cb_pane_init_objs
), pane
);
1274 * gnm_pane_find_col:
1276 * @x: In canvas coords
1277 * @col_origin: optionally return the canvas coord of the col
1279 * Returns the column containing canvas coord @x
1282 gnm_pane_find_col (GnmPane
const *pane
, gint64 x
, gint64
*col_origin
)
1284 Sheet
const *sheet
= scg_sheet (pane
->simple
.scg
);
1285 int col
= pane
->first
.col
;
1286 gint64 pixel
= pane
->first_offset
.x
;
1290 ColRowInfo
const *ci
= sheet_col_get_info (sheet
, --col
);
1292 pixel
-= ci
->size_pixels
;
1295 *col_origin
= pixel
;
1306 ColRowInfo
const *ci
= sheet_col_get_info (sheet
, col
);
1308 int const tmp
= ci
->size_pixels
;
1309 if (x
<= pixel
+ tmp
) {
1311 *col_origin
= pixel
;
1316 } while (++col
< gnm_sheet_get_last_col (sheet
));
1319 *col_origin
= pixel
;
1320 return gnm_sheet_get_last_col (sheet
);
1324 * gnm_pane_find_row:
1326 * @y: In canvas coords
1327 * @row_origin: optionally return the canvas coord of the row
1329 * Returns the column containing canvas coord @y
1332 gnm_pane_find_row (GnmPane
const *pane
, gint64 y
, gint64
*row_origin
)
1334 Sheet
const *sheet
= scg_sheet (pane
->simple
.scg
);
1335 int row
= pane
->first
.row
;
1336 gint64 pixel
= pane
->first_offset
.y
;
1340 ColRowInfo
const *ri
= sheet_row_get_info (sheet
, --row
);
1342 pixel
-= ri
->size_pixels
;
1345 *row_origin
= pixel
;
1356 ColRowInfo
const *ri
= sheet_row_get_info (sheet
, row
);
1358 int const tmp
= ri
->size_pixels
;
1359 if (pixel
<= y
&& y
<= pixel
+ tmp
) {
1361 *row_origin
= pixel
;
1366 } while (++row
< gnm_sheet_get_last_row (sheet
));
1368 *row_origin
= pixel
;
1369 return gnm_sheet_get_last_row (sheet
);
1373 * gnm_pane_compute_visible_region : Keeps the top left col/row the same and
1374 * recalculates the visible boundaries.
1377 * if TRUE recompute the pixel offsets of the top left row/col
1378 * else assumes that the pixel offsets of the top left have not changed.
1381 gnm_pane_compute_visible_region (GnmPane
*pane
,
1382 gboolean
const full_recompute
)
1384 SheetControlGUI
const * const scg
= pane
->simple
.scg
;
1385 Sheet
const *sheet
= scg_sheet (scg
);
1386 GocCanvas
*canvas
= GOC_CANVAS (pane
);
1388 int col
, row
, width
, height
;
1391 gtk_widget_get_allocation (GTK_WIDGET (canvas
), &ca
);
1393 /* When col/row sizes change we need to do a full recompute */
1394 if (full_recompute
) {
1395 gint64 col_offset
= pane
->first_offset
.x
= scg_colrow_distance_get (scg
,
1396 TRUE
, 0, pane
->first
.col
);
1397 if (NULL
!= pane
->col
.canvas
)
1398 goc_canvas_scroll_to (pane
->col
.canvas
, col_offset
/ canvas
->pixels_per_unit
, 0);
1400 pane
->first_offset
.y
= scg_colrow_distance_get (scg
,
1401 FALSE
, 0, pane
->first
.row
);
1402 if (NULL
!= pane
->row
.canvas
)
1403 goc_canvas_scroll_to (pane
->row
.canvas
,
1404 0, pane
->first_offset
.y
/ canvas
->pixels_per_unit
);
1406 goc_canvas_scroll_to (GOC_CANVAS (pane
),
1407 col_offset
/ canvas
->pixels_per_unit
, pane
->first_offset
.y
/ canvas
->pixels_per_unit
);
1410 /* Find out the last visible col and the last full visible column */
1412 col
= pane
->first
.col
;
1416 ColRowInfo
const * const ci
= sheet_col_get_info (sheet
, col
);
1418 int const bound
= pixels
+ ci
->size_pixels
;
1420 if (bound
== width
) {
1421 pane
->last_visible
.col
= col
;
1422 pane
->last_full
.col
= col
;
1425 if (bound
> width
) {
1426 pane
->last_visible
.col
= col
;
1427 if (col
== pane
->first
.col
)
1428 pane
->last_full
.col
= pane
->first
.col
;
1430 pane
->last_full
.col
= col
- 1;
1436 } while (pixels
< width
&& col
< gnm_sheet_get_max_cols (sheet
));
1438 if (col
>= gnm_sheet_get_max_cols (sheet
)) {
1439 pane
->last_visible
.col
= gnm_sheet_get_last_col (sheet
);
1440 pane
->last_full
.col
= gnm_sheet_get_last_col (sheet
);
1443 /* Find out the last visible row and the last fully visible row */
1445 row
= pane
->first
.row
;
1448 ColRowInfo
const * const ri
= sheet_row_get_info (sheet
, row
);
1450 int const bound
= pixels
+ ri
->size_pixels
;
1452 if (bound
== height
) {
1453 pane
->last_visible
.row
= row
;
1454 pane
->last_full
.row
= row
;
1457 if (bound
> height
) {
1458 pane
->last_visible
.row
= row
;
1459 if (row
== pane
->first
.row
)
1460 pane
->last_full
.row
= pane
->first
.row
;
1462 pane
->last_full
.row
= row
- 1;
1468 } while (pixels
< height
&& row
< gnm_sheet_get_max_rows (sheet
));
1470 if (row
>= gnm_sheet_get_max_rows (sheet
)) {
1471 pane
->last_visible
.row
= gnm_sheet_get_last_row (sheet
);
1472 pane
->last_full
.row
= gnm_sheet_get_last_row (sheet
);
1475 /* Update the scrollbar sizes for the primary pane */
1476 if (pane
->index
== 0)
1477 sc_scrollbar_config (GNM_SHEET_CONTROL (scg
));
1479 /* Force the cursor to update its bounds relative to the new visible region */
1480 gnm_pane_reposition_cursors (pane
);
1484 gnm_pane_redraw_range (GnmPane
*pane
, GnmRange
const *r
)
1486 SheetControlGUI
*scg
;
1487 gint64 x1
, y1
, x2
, y2
;
1490 double scale
= goc_canvas_get_pixels_per_unit (GOC_CANVAS (pane
));
1492 g_return_if_fail (GNM_IS_PANE (pane
));
1494 scg
= pane
->simple
.scg
;
1495 sheet
= scg_sheet (scg
);
1497 if ((r
->end
.col
< pane
->first
.col
) ||
1498 (r
->end
.row
< pane
->first
.row
) ||
1499 (r
->start
.col
> pane
->last_visible
.col
) ||
1500 (r
->start
.row
> pane
->last_visible
.row
))
1503 /* Only draw those regions that are visible */
1504 tmp
.start
.col
= MAX (pane
->first
.col
, r
->start
.col
);
1505 tmp
.start
.row
= MAX (pane
->first
.row
, r
->start
.row
);
1506 tmp
.end
.col
= MIN (pane
->last_visible
.col
, r
->end
.col
);
1507 tmp
.end
.row
= MIN (pane
->last_visible
.row
, r
->end
.row
);
1509 /* redraw a border of 2 pixels around the region to handle thick borders
1510 * NOTE the 2nd coordinates are excluded so add 1 extra (+2border +1include)
1512 x1
= scg_colrow_distance_get (scg
, TRUE
, pane
->first
.col
, tmp
.start
.col
) +
1513 pane
->first_offset
.x
;
1514 y1
= scg_colrow_distance_get (scg
, FALSE
, pane
->first
.row
, tmp
.start
.row
) +
1515 pane
->first_offset
.y
;
1516 x2
= (tmp
.end
.col
< gnm_sheet_get_last_col (sheet
))
1517 ? 4 + 1 + x1
+ scg_colrow_distance_get (scg
, TRUE
,
1518 tmp
.start
.col
, tmp
.end
.col
+1)
1520 y2
= (tmp
.end
.row
< gnm_sheet_get_last_row (sheet
))
1521 ? 4 + 1 + y1
+ scg_colrow_distance_get (scg
, FALSE
,
1522 tmp
.start
.row
, tmp
.end
.row
+1)
1525 goc_canvas_invalidate (&pane
->simple
.canvas
, (x1
-2) / scale
, (y1
-2) / scale
, x2
/ scale
, y2
/ scale
);
1528 /*****************************************************************************/
1531 gnm_pane_slide_stop (GnmPane
*pane
)
1533 if (pane
->sliding_timer
== 0)
1536 g_source_remove (pane
->sliding_timer
);
1537 pane
->slide_handler
= NULL
;
1538 pane
->slide_data
= NULL
;
1539 pane
->sliding_timer
= 0;
1543 col_scroll_step (int dx
, Sheet
*sheet
)
1545 /* FIXME: get from gdk. */
1546 int dpi_x_this_screen
= 90;
1547 int start_x
= dpi_x_this_screen
/ 3;
1548 double double_dx
= dpi_x_this_screen
/ 3.0;
1549 double step
= pow (2.0, (dx
- start_x
) / double_dx
);
1551 return (int) (CLAMP (step
, 1.0, gnm_sheet_get_max_cols (sheet
) / 15.0));
1555 row_scroll_step (int dy
, Sheet
*sheet
)
1557 /* FIXME: get from gdk. */
1558 int dpi_y_this_screen
= 90;
1559 int start_y
= dpi_y_this_screen
/ 4;
1560 double double_dy
= dpi_y_this_screen
/ 8.0;
1561 double step
= pow (2.0, (dy
- start_y
) / double_dy
);
1563 return (int) (CLAMP (step
, 1.0, gnm_sheet_get_max_rows (sheet
) / 15.0));
1567 cb_pane_sliding (GnmPane
*pane
)
1569 int const pane_index
= pane
->index
;
1570 GnmPane
*pane0
= scg_pane (pane
->simple
.scg
, 0);
1571 GnmPane
*pane1
= scg_pane (pane
->simple
.scg
, 1);
1572 GnmPane
*pane3
= scg_pane (pane
->simple
.scg
, 3);
1573 gboolean slide_x
= FALSE
, slide_y
= FALSE
;
1574 int col
= -1, row
= -1;
1575 Sheet
*sheet
= scg_sheet (pane
->simple
.scg
);
1576 GnmPaneSlideInfo info
;
1579 gtk_widget_get_allocation (GTK_WIDGET (pane
), &pa
);
1581 if (pane
->sliding_dx
> 0) {
1582 GnmPane
*target_pane
= pane
;
1585 if (pane_index
== 1 || pane_index
== 2) {
1586 if (!pane
->sliding_adjacent_h
) {
1587 int width
= pa
.width
;
1588 int x
= pane
->first_offset
.x
+ width
+ pane
->sliding_dx
;
1590 /* in case pane is narrow */
1591 col
= gnm_pane_find_col (pane
, x
, NULL
);
1592 if (col
> pane0
->last_full
.col
) {
1593 pane
->sliding_adjacent_h
= TRUE
;
1594 pane
->sliding_dx
= 1; /* good enough */
1598 target_pane
= pane0
;
1600 pane
->sliding_adjacent_h
= FALSE
;
1603 col
= target_pane
->last_full
.col
+
1604 col_scroll_step (pane
->sliding_dx
, sheet
);
1605 if (col
>= gnm_sheet_get_last_col (sheet
)) {
1606 col
= gnm_sheet_get_last_col (sheet
);
1610 } else if (pane
->sliding_dx
< 0) {
1612 col
= pane0
->first
.col
- col_scroll_step (-pane
->sliding_dx
, sheet
);
1614 if (pane1
!= NULL
) {
1615 if (pane_index
== 0 || pane_index
== 3) {
1619 gtk_widget_get_allocation (GTK_WIDGET (pane1
),
1623 if (pane
->sliding_dx
> (-width
) &&
1624 col
<= pane1
->last_visible
.col
) {
1625 int x
= pane1
->first_offset
.x
+ width
+ pane
->sliding_dx
;
1626 col
= gnm_pane_find_col (pane
, x
, NULL
);
1631 if (col
<= pane1
->first
.col
) {
1632 col
= pane1
->first
.col
;
1635 } else if (col
<= 0) {
1641 if (pane
->sliding_dy
> 0) {
1642 GnmPane
*target_pane
= pane
;
1645 if (pane_index
== 3 || pane_index
== 2) {
1646 if (!pane
->sliding_adjacent_v
) {
1647 int height
= pa
.height
;
1648 int y
= pane
->first_offset
.y
+ height
+ pane
->sliding_dy
;
1650 /* in case pane is short */
1651 row
= gnm_pane_find_row (pane
, y
, NULL
);
1652 if (row
> pane0
->last_full
.row
) {
1653 pane
->sliding_adjacent_v
= TRUE
;
1654 pane
->sliding_dy
= 1; /* good enough */
1658 target_pane
= pane0
;
1660 pane
->sliding_adjacent_v
= FALSE
;
1663 row
= target_pane
->last_full
.row
+
1664 row_scroll_step (pane
->sliding_dy
, sheet
);
1665 if (row
>= gnm_sheet_get_last_row (sheet
)) {
1666 row
= gnm_sheet_get_last_row (sheet
);
1670 } else if (pane
->sliding_dy
< 0) {
1672 row
= pane0
->first
.row
- row_scroll_step (-pane
->sliding_dy
, sheet
);
1674 if (pane3
!= NULL
) {
1675 if (pane_index
== 0 || pane_index
== 1) {
1679 gtk_widget_get_allocation (GTK_WIDGET (pane3
),
1682 height
= p3a
.height
;
1683 if (pane
->sliding_dy
> (-height
) &&
1684 row
<= pane3
->last_visible
.row
) {
1685 int y
= pane3
->first_offset
.y
+ height
+ pane
->sliding_dy
;
1686 row
= gnm_pane_find_row (pane3
, y
, NULL
);
1691 if (row
<= pane3
->first
.row
) {
1692 row
= pane3
->first
.row
;
1695 } else if (row
<= 0) {
1701 if (col
< 0 && row
< 0) {
1702 gnm_pane_slide_stop (pane
);
1707 col
= gnm_pane_find_col (pane
, pane
->sliding_x
, NULL
);
1709 row
= gnm_pane_find_row (pane
, pane
->sliding_y
, NULL
);
1713 info
.user_data
= pane
->slide_data
;
1714 if (pane
->slide_handler
== NULL
||
1715 (*pane
->slide_handler
) (pane
, &info
))
1716 scg_make_cell_visible (pane
->simple
.scg
, col
, row
, FALSE
, TRUE
);
1718 if (!slide_x
&& !slide_y
)
1719 gnm_pane_slide_stop (pane
);
1720 else if (pane
->sliding_timer
== 0)
1721 pane
->sliding_timer
= g_timeout_add (300, (GSourceFunc
)cb_pane_sliding
, pane
);
1727 * gnm_pane_handle_motion:
1728 * @pane: The GnmPane managing the scroll
1729 * @canvas: The Canvas the event comes from
1731 * @handler: (scope call): The handler when sliding
1732 * @user_data: closure data
1734 * Handle a motion event from a @canvas and scroll the @pane
1735 * depending on how far outside the bounds of @pane the @event is.
1736 * Usually @canvas == @pane however as long as the canvases share a basis
1737 * space they can be different.
1740 gnm_pane_handle_motion (GnmPane
*pane
,
1741 GocCanvas
*canvas
, gint64 x
, gint64 y
,
1742 GnmPaneSlideFlags slide_flags
,
1743 GnmPaneSlideHandler slide_handler
,
1746 GnmPane
*pane0
, *pane1
, *pane3
;
1747 int pindex
, width
, height
;
1748 gint64 dx
= 0, dy
= 0, left
, top
;
1749 GtkAllocation pa
, p0a
, p1a
, p3a
;
1751 g_return_val_if_fail (GNM_IS_PANE (pane
), FALSE
);
1752 g_return_val_if_fail (GOC_IS_CANVAS (canvas
), FALSE
);
1753 g_return_val_if_fail (slide_handler
!= NULL
, FALSE
);
1755 pindex
= pane
->index
;
1756 left
= pane
->first_offset
.x
;
1757 top
= pane
->first_offset
.y
;
1758 gtk_widget_get_allocation (GTK_WIDGET (pane
), &pa
);
1762 pane0
= scg_pane (pane
->simple
.scg
, 0);
1763 gtk_widget_get_allocation (GTK_WIDGET (pane0
), &p0a
);
1765 pane1
= scg_pane (pane
->simple
.scg
, 1);
1766 if (pane1
) gtk_widget_get_allocation (GTK_WIDGET (pane1
), &p1a
);
1768 pane3
= scg_pane (pane
->simple
.scg
, 3);
1769 if (pane3
) gtk_widget_get_allocation (GTK_WIDGET (pane3
), &p3a
);
1771 if (slide_flags
& GNM_PANE_SLIDE_X
) {
1774 else if (x
>= left
+ width
)
1775 dx
= x
- width
- left
;
1778 if (slide_flags
& GNM_PANE_SLIDE_Y
) {
1781 else if (y
>= top
+ height
)
1782 dy
= y
- height
- top
;
1785 if (pane
->sliding_adjacent_h
) {
1786 if (pindex
== 0 || pindex
== 3) {
1788 x
= pane1
->first_offset
.x
;
1794 pane
->sliding_adjacent_h
= FALSE
;
1797 x
= pane0
->first_offset
.x
+ dx
;
1801 } else if (dx
== 0) {
1802 /* initiate a reverse scroll of panes 0,3 */
1803 if ((pane1
->last_visible
.col
+1) != pane0
->first
.col
)
1804 dx
= x
- (left
+ width
);
1810 if (pane
->sliding_adjacent_v
) {
1811 if (pindex
== 0 || pindex
== 1) {
1813 y
= pane3
->first_offset
.y
;
1819 pane
->sliding_adjacent_v
= FALSE
;
1822 y
= pane0
->first_offset
.y
+ dy
;
1826 } else if (dy
== 0) {
1827 /* initiate a reverse scroll of panes 0,1 */
1828 if ((pane3
->last_visible
.row
+1) != pane0
->first
.row
)
1829 dy
= y
- (top
+ height
);
1835 /* Movement is inside the visible region */
1836 if (dx
== 0 && dy
== 0) {
1837 if (!(slide_flags
& GNM_PANE_SLIDE_EXTERIOR_ONLY
)) {
1838 GnmPaneSlideInfo info
;
1839 info
.row
= gnm_pane_find_row (pane
, y
, NULL
);
1840 info
.col
= gnm_pane_find_col (pane
, x
, NULL
);
1841 info
.user_data
= user_data
;
1842 (*slide_handler
) (pane
, &info
);
1844 gnm_pane_slide_stop (pane
);
1848 pane
->sliding_x
= x
;
1849 pane
->sliding_dx
= dx
;
1850 pane
->sliding_y
= y
;
1851 pane
->sliding_dy
= dy
;
1852 pane
->slide_handler
= slide_handler
;
1853 pane
->slide_data
= user_data
;
1855 if (pane
->sliding_timer
== 0)
1856 cb_pane_sliding (pane
);
1860 /* TODO : All the slide_* members of GnmPane really ought to be in
1861 * SheetControlGUI, most of these routines also belong there. However, since
1862 * the primary point of access is via GnmPane and SCG is very large
1863 * already I'm leaving them here for now. Move them when we return to
1864 * investigate how to do reverse scrolling for pseudo-adjacent panes.
1867 gnm_pane_slide_init (GnmPane
*pane
)
1869 GnmPane
*pane0
, *pane1
, *pane3
;
1871 g_return_if_fail (GNM_IS_PANE (pane
));
1873 pane0
= scg_pane (pane
->simple
.scg
, 0);
1874 pane1
= scg_pane (pane
->simple
.scg
, 1);
1875 pane3
= scg_pane (pane
->simple
.scg
, 3);
1877 pane
->sliding_adjacent_h
= (pane1
!= NULL
)
1878 ? (pane1
->last_full
.col
== (pane0
->first
.col
- 1))
1880 pane
->sliding_adjacent_v
= (pane3
!= NULL
)
1881 ? (pane3
->last_full
.row
== (pane0
->first
.row
- 1))
1886 cb_obj_autoscroll (GnmPane
*pane
, GnmPaneSlideInfo
const *info
)
1888 SheetControlGUI
*scg
= pane
->simple
.scg
;
1889 GdkModifierType mask
;
1890 GdkWindow
*window
= gtk_widget_get_parent_window (GTK_WIDGET (pane
));
1892 /* Cheesy hack calculate distance we move the screen, this loses the
1894 double dx
= pane
->first_offset
.x
;
1895 double dy
= pane
->first_offset
.y
;
1896 scg_make_cell_visible (scg
, info
->col
, info
->row
, FALSE
, TRUE
);
1897 dx
= pane
->first_offset
.x
- dx
;
1898 dy
= pane
->first_offset
.y
- dy
;
1900 pane
->drag
.had_motion
= TRUE
;
1901 gdk_window_get_device_position (window
,
1902 gdk_device_manager_get_client_pointer (gdk_display_get_device_manager (gdk_window_get_display (window
))),
1904 scg_objects_drag (pane
->simple
.scg
, pane
,
1905 NULL
, &dx
, &dy
, 8, FALSE
, (mask
& GDK_SHIFT_MASK
) != 0, TRUE
);
1907 pane
->drag
.last_x
+= dx
;
1908 pane
->drag
.last_y
+= dy
;
1913 gnm_pane_object_autoscroll (GnmPane
*pane
, GdkDragContext
*context
,
1914 gint x
, gint y
, guint time
)
1916 int const pane_index
= pane
->index
;
1917 SheetControlGUI
*scg
= pane
->simple
.scg
;
1918 GnmPane
*pane0
= scg_pane (scg
, 0);
1919 GnmPane
*pane1
= scg_pane (scg
, 1);
1920 GnmPane
*pane3
= scg_pane (scg
, 3);
1921 GtkWidget
*w
= GTK_WIDGET (pane
);
1925 gtk_widget_get_allocation (w
, &wa
);
1928 if (pane_index
< 2 && pane3
!= NULL
) {
1929 w
= GTK_WIDGET (pane3
);
1930 gtk_widget_get_allocation (w
, &wa
);
1933 g_return_if_fail (dy
<= 0);
1934 } else if (y
>= (wa
.y
+ wa
.height
)) {
1935 if (pane_index
>= 2) {
1936 w
= GTK_WIDGET (pane0
);
1937 gtk_widget_get_allocation (w
, &wa
);
1939 dy
= y
- (wa
.y
+ wa
.height
);
1940 g_return_if_fail (dy
>= 0);
1944 if ((pane_index
== 0 || pane_index
== 3) && pane1
!= NULL
) {
1945 w
= GTK_WIDGET (pane1
);
1946 gtk_widget_get_allocation (w
, &wa
);
1949 g_return_if_fail (dx
<= 0);
1950 } else if (x
>= (wa
.x
+ wa
.width
)) {
1951 if (pane_index
>= 2) {
1952 w
= GTK_WIDGET (pane0
);
1953 gtk_widget_get_allocation (w
, &wa
);
1955 dx
= x
- (wa
.x
+ wa
.width
);
1956 g_return_if_fail (dx
>= 0);
1960 g_object_set_data (G_OBJECT (context
),
1961 "wbcg", scg_wbcg (scg
));
1962 pane
->sliding_dx
= dx
;
1963 pane
->sliding_dy
= dy
;
1964 pane
->slide_handler
= &cb_obj_autoscroll
;
1965 pane
->slide_data
= NULL
;
1966 pane
->sliding_x
= x
;
1967 pane
->sliding_y
= y
;
1968 if (pane
->sliding_timer
== 0)
1969 cb_pane_sliding (pane
);
1973 * gnm_pane_object_group:
1976 * Returns: (transfer none): the #GocGroup including all #SheetObjectView
1977 * instances in @pane.
1980 gnm_pane_object_group (GnmPane
*pane
)
1982 return pane
->object_views
;
1986 gnm_pane_clear_obj_size_tip (GnmPane
*pane
)
1988 if (pane
->size_tip
) {
1989 gtk_widget_destroy (gtk_widget_get_toplevel (pane
->size_tip
));
1990 pane
->size_tip
= NULL
;
1995 gnm_pane_display_obj_size_tip (GnmPane
*pane
, GocItem
*ctrl_pt
)
1997 SheetControlGUI
*scg
= pane
->simple
.scg
;
1998 double const *coords
;
2001 SheetObjectAnchor anchor
;
2003 if (pane
->size_tip
== NULL
) {
2004 GtkWidget
*cw
= GTK_WIDGET (pane
);
2008 if (ctrl_pt
== NULL
) {
2010 * Keyboard navigation when we are not displaying
2011 * a tooltip already.
2016 pane
->size_tip
= gnm_create_tooltip (cw
);
2017 top
= gtk_widget_get_toplevel (pane
->size_tip
);
2019 gnm_canvas_get_screen_position (ctrl_pt
->canvas
,
2020 ctrl_pt
->x1
, ctrl_pt
->y1
,
2022 gtk_window_move (GTK_WINDOW (top
), x
+ 10, y
+ 10);
2023 gtk_widget_show_all (top
);
2026 g_return_if_fail (pane
->cur_object
!= NULL
);
2027 g_return_if_fail (pane
->size_tip
!= NULL
);
2029 coords
= g_hash_table_lookup (scg
->selected_objects
, pane
->cur_object
);
2030 anchor
= *sheet_object_get_anchor (pane
->cur_object
);
2031 scg_object_coords_to_anchor (scg
, coords
, &anchor
);
2032 sheet_object_anchor_to_pts (&anchor
, scg_sheet (scg
), pts
);
2033 msg
= g_strdup_printf (_("%.1f x %.1f pts\n%d x %d pixels"),
2034 MAX (fabs (pts
[2] - pts
[0]), 0),
2035 MAX (fabs (pts
[3] - pts
[1]), 0),
2036 MAX ((int)floor (fabs (coords
[2] - coords
[0]) + 0.5), 0),
2037 MAX ((int)floor (fabs (coords
[3] - coords
[1]) + 0.5), 0));
2038 gtk_label_set_text (GTK_LABEL (pane
->size_tip
), msg
);
2043 gnm_pane_bound_set (GnmPane
*pane
,
2044 int start_col
, int start_row
,
2045 int end_col
, int end_row
)
2049 g_return_if_fail (pane
!= NULL
);
2051 range_init (&r
, start_col
, start_row
, end_col
, end_row
);
2052 goc_item_set (GOC_ITEM (pane
->grid
),
2057 /****************************************************************************/
2060 gnm_pane_size_guide_start (GnmPane
*pane
,
2061 gboolean vert
, int colrow
, gboolean is_colrow_resize
)
2063 SheetControlGUI
const *scg
;
2064 double x0
, y0
, x1
, y1
, pos
;
2068 GtkStyleContext
*context
;
2069 const char *guide_class
= is_colrow_resize
? "resize-guide" : "pane-resize-guide";
2070 const char *colrow_class
= vert
? "col" : "row";
2071 const char *width_prop_name
= is_colrow_resize
? "resize-guide-width" : "pane-resize-guide-width";
2074 g_return_if_fail (pane
!= NULL
);
2075 g_return_if_fail (pane
->size_guide
.guide
== NULL
);
2076 g_return_if_fail (pane
->size_guide
.start
== NULL
);
2077 g_return_if_fail (pane
->size_guide
.points
== NULL
);
2079 zoom
= GOC_CANVAS (pane
)->pixels_per_unit
;
2080 scg
= pane
->simple
.scg
;
2082 pos
= scg_colrow_distance_get (scg
, vert
, 0, colrow
) / zoom
;
2085 y0
= scg_colrow_distance_get (scg
, FALSE
,
2086 0, pane
->first
.row
) / zoom
;
2088 y1
= scg_colrow_distance_get (scg
, FALSE
,
2089 0, pane
->last_visible
.row
+1) / zoom
;
2091 x0
= scg_colrow_distance_get (scg
, TRUE
,
2092 0, pane
->first
.col
) / zoom
;
2094 x1
= scg_colrow_distance_get (scg
, TRUE
,
2095 0, pane
->last_visible
.col
+1) / zoom
;
2099 gtk_widget_style_get (GTK_WIDGET (pane
), width_prop_name
, &width
, NULL
);
2101 /* Guideline positioning is done in gnm_pane_size_guide_motion */
2102 pane
->size_guide
.guide
= goc_item_new (pane
->action_items
,
2107 style
= go_styled_object_get_style (GO_STYLED_OBJECT (pane
->size_guide
.guide
));
2108 style
->line
.width
= width
;
2109 context
= goc_item_get_style_context (pane
->size_guide
.guide
);
2110 gtk_style_context_add_class (context
, guide_class
);
2111 gtk_style_context_add_class (context
, colrow_class
);
2112 if (is_colrow_resize
)
2113 gtk_style_context_add_class (context
, "end");
2114 gnm_style_context_get_color (context
, GTK_STATE_FLAG_SELECTED
, &rgba
);
2115 go_color_from_gdk_rgba (&rgba
, &style
->line
.color
);
2117 if (is_colrow_resize
) {
2118 pane
->size_guide
.start
= goc_item_new (pane
->action_items
,
2123 style
= go_styled_object_get_style (GO_STYLED_OBJECT (pane
->size_guide
.start
));
2124 context
= goc_item_get_style_context (pane
->size_guide
.start
);
2125 gtk_style_context_add_class (context
, guide_class
);
2126 gtk_style_context_add_class (context
, colrow_class
);
2127 gtk_style_context_add_class (context
, "start");
2128 gnm_style_context_get_color (context
, GTK_STATE_FLAG_SELECTED
, &rgba
);
2129 go_color_from_gdk_rgba (&rgba
, &style
->line
.color
);
2130 style
->line
.width
= width
;
2135 gnm_pane_size_guide_stop (GnmPane
*pane
)
2137 g_return_if_fail (pane
!= NULL
);
2139 g_clear_object (&pane
->size_guide
.start
);
2140 g_clear_object (&pane
->size_guide
.guide
);
2144 * gnm_pane_size_guide_motion:
2146 * @vert: TRUE for a vertical guide, FALSE for horizontal
2147 * @guide_pos: in unscaled sheet pixel coords
2149 * Moves the guide line to @guide_pos.
2150 * NOTE : gnm_pane_size_guide_start must be called before any calls to
2151 * gnm_pane_size_guide_motion
2154 gnm_pane_size_guide_motion (GnmPane
*pane
, gboolean vert
, gint64 guide_pos
)
2156 GocItem
*resize_guide
= GOC_ITEM (pane
->size_guide
.guide
);
2157 double const scale
= 1. / resize_guide
->canvas
->pixels_per_unit
;
2160 x
= scale
* (guide_pos
- .5);
2162 goc_item_set (resize_guide
, "x0", x
, "x1", x
, NULL
);
2164 goc_item_set (resize_guide
, "y0", x
, "y1", x
, NULL
);
2167 /****************************************************************************/
2170 cb_update_ctrl_pts (SheetObject
*so
, GocItem
**ctrl_pts
, GnmPane
*pane
)
2172 double *coords
= g_hash_table_lookup (
2173 pane
->simple
.scg
->selected_objects
, so
);
2174 scg_object_anchor_to_coords (pane
->simple
.scg
, sheet_object_get_anchor (so
), coords
);
2175 gnm_pane_object_update_bbox (pane
, so
);
2178 /* Called when the zoom changes */
2180 gnm_pane_reposition_cursors (GnmPane
*pane
)
2184 gnm_item_cursor_reposition (pane
->cursor
.std
);
2185 if (NULL
!= pane
->cursor
.rangesel
)
2186 gnm_item_cursor_reposition (pane
->cursor
.rangesel
);
2187 if (NULL
!= pane
->cursor
.special
)
2188 gnm_item_cursor_reposition (pane
->cursor
.special
);
2189 for (l
= pane
->cursor
.expr_range
; l
; l
= l
->next
)
2190 gnm_item_cursor_reposition (GNM_ITEM_CURSOR (l
->data
));
2191 for (l
= pane
->cursor
.animated
; l
; l
= l
->next
)
2192 gnm_item_cursor_reposition (GNM_ITEM_CURSOR (l
->data
));
2194 /* ctrl pts do not scale with the zoom, compensate */
2195 if (pane
->drag
.ctrl_pts
!= NULL
)
2196 g_hash_table_foreach (pane
->drag
.ctrl_pts
,
2197 (GHFunc
) cb_update_ctrl_pts
, pane
);
2201 gnm_pane_cursor_bound_set (GnmPane
*pane
, GnmRange
const *r
)
2203 return gnm_item_cursor_bound_set (pane
->cursor
.std
, r
);
2206 /****************************************************************************/
2209 gnm_pane_rangesel_bound_set (GnmPane
*pane
, GnmRange
const *r
)
2211 return gnm_item_cursor_bound_set (pane
->cursor
.rangesel
, r
);
2214 gnm_pane_rangesel_start (GnmPane
*pane
, GnmRange
const *r
)
2217 SheetControlGUI
*scg
= pane
->simple
.scg
;
2219 g_return_if_fail (pane
->cursor
.rangesel
== NULL
);
2221 /* Hide the primary cursor while the range selection cursor is visible
2222 * and we are selecting on a different sheet than the expr being edited */
2223 if (scg_sheet (scg
) != wb_control_cur_sheet (scg_wbc (scg
)))
2224 gnm_item_cursor_set_visibility (pane
->cursor
.std
, FALSE
);
2225 item
= goc_item_new (pane
->grid_items
,
2226 gnm_item_cursor_get_type (),
2227 "SheetControlGUI", scg
,
2228 "style", GNM_ITEM_CURSOR_ANTED
,
2230 pane
->cursor
.rangesel
= GNM_ITEM_CURSOR (item
);
2231 gnm_item_cursor_bound_set (pane
->cursor
.rangesel
, r
);
2235 gnm_pane_rangesel_stop (GnmPane
*pane
)
2237 g_return_if_fail (pane
->cursor
.rangesel
!= NULL
);
2239 g_clear_object (&pane
->cursor
.rangesel
);
2241 /* Make the primary cursor visible again */
2242 gnm_item_cursor_set_visibility (pane
->cursor
.std
, TRUE
);
2244 gnm_pane_slide_stop (pane
);
2247 /****************************************************************************/
2250 gnm_pane_special_cursor_bound_set (GnmPane
*pane
, GnmRange
const *r
)
2252 return gnm_item_cursor_bound_set (pane
->cursor
.special
, r
);
2256 gnm_pane_special_cursor_start (GnmPane
*pane
, int style
, int button
)
2259 GocCanvas
*canvas
= GOC_CANVAS (pane
);
2261 g_return_if_fail (pane
->cursor
.special
== NULL
);
2262 item
= goc_item_new (
2263 GOC_GROUP (canvas
->root
),
2264 gnm_item_cursor_get_type (),
2265 "SheetControlGUI", pane
->simple
.scg
,
2269 pane
->cursor
.special
= GNM_ITEM_CURSOR (item
);
2273 gnm_pane_special_cursor_stop (GnmPane
*pane
)
2275 g_return_if_fail (pane
->cursor
.special
!= NULL
);
2277 g_clear_object (&pane
->cursor
.special
);
2281 gnm_pane_mouse_cursor_set (GnmPane
*pane
, GdkCursor
*c
)
2284 if (pane
->mouse_cursor
)
2285 g_object_unref (pane
->mouse_cursor
);
2286 pane
->mouse_cursor
= c
;
2289 /****************************************************************************/
2293 gnm_pane_expr_cursor_bound_set (GnmPane
*pane
, GnmRange
const *r
,
2296 GnmItemCursor
*cursor
;
2298 cursor
= (GnmItemCursor
*) goc_item_new
2299 (GOC_GROUP (GOC_CANVAS (pane
)->root
),
2300 gnm_item_cursor_get_type (),
2301 "SheetControlGUI", pane
->simple
.scg
,
2302 "style", GNM_ITEM_CURSOR_EXPR_RANGE
,
2306 gnm_item_cursor_bound_set (cursor
, r
);
2307 pane
->cursor
.expr_range
= g_slist_prepend
2308 (pane
->cursor
.expr_range
, cursor
);
2312 gnm_pane_expr_cursor_stop (GnmPane
*pane
)
2314 g_slist_free_full (pane
->cursor
.expr_range
, g_object_unref
);
2315 pane
->cursor
.expr_range
= NULL
;
2318 /****************************************************************************/
2321 gnm_pane_edit_start (GnmPane
*pane
)
2323 GocCanvas
*canvas
= GOC_CANVAS (pane
);
2325 g_return_if_fail (pane
->editor
== NULL
);
2327 /* edit item handles visibility checks */
2328 pane
->editor
= (GnmItemEdit
*) goc_item_new (
2329 GOC_GROUP (canvas
->root
),
2330 gnm_item_edit_get_type (),
2331 "SheetControlGUI", pane
->simple
.scg
,
2336 gnm_pane_edit_stop (GnmPane
*pane
)
2338 g_clear_object (&pane
->editor
);
2342 gnm_pane_objects_drag (GnmPane
*pane
, SheetObject
*so
,
2343 gdouble new_x
, gdouble new_y
, int drag_type
,
2344 gboolean symmetric
,gboolean snap_to_grid
)
2347 dx
= new_x
- pane
->drag
.last_x
;
2348 dy
= new_y
- pane
->drag
.last_y
;
2349 pane
->drag
.had_motion
= TRUE
;
2350 scg_objects_drag (pane
->simple
.scg
, pane
,
2351 so
, &dx
, &dy
, drag_type
, symmetric
, snap_to_grid
, TRUE
);
2353 pane
->drag
.last_x
+= dx
;
2354 pane
->drag
.last_y
+= dy
;
2357 /* new_x and new_y are in world coords */
2359 gnm_pane_object_move (GnmPane
*pane
, GObject
*ctrl_pt
,
2360 gdouble new_x
, gdouble new_y
,
2362 gboolean snap_to_grid
)
2364 int const idx
= GPOINTER_TO_INT (g_object_get_data (ctrl_pt
, "index"));
2365 pane
->cur_object
= g_object_get_data (G_OBJECT (ctrl_pt
), "so");
2367 gnm_pane_objects_drag (pane
, pane
->cur_object
, new_x
, new_y
, idx
,
2368 symmetric
, snap_to_grid
);
2370 gnm_pane_display_obj_size_tip (pane
, GOC_ITEM (ctrl_pt
));
2374 cb_slide_handler (GnmPane
*pane
, GnmPaneSlideInfo
const *info
)
2377 SheetControlGUI
const *scg
= pane
->simple
.scg
;
2378 double const scale
= 1. / GOC_CANVAS (pane
)->pixels_per_unit
;
2380 x
= scg_colrow_distance_get (scg
, TRUE
, pane
->first
.col
, info
->col
);
2381 x
+= pane
->first_offset
.x
;
2382 y
= scg_colrow_distance_get (scg
, FALSE
, pane
->first
.row
, info
->row
);
2383 y
+= pane
->first_offset
.y
;
2385 gnm_pane_object_move (pane
, info
->user_data
,
2386 x
* scale
, y
* scale
, FALSE
, FALSE
);
2392 cb_ptr_array_free (GPtrArray
*actions
)
2394 g_ptr_array_free (actions
, TRUE
);
2397 /* event and so can be NULL */
2399 gnm_pane_display_object_menu (GnmPane
*pane
, SheetObject
*so
, GdkEvent
*event
)
2401 SheetControlGUI
*scg
= pane
->simple
.scg
;
2402 GPtrArray
*actions
= g_ptr_array_new ();
2406 if (NULL
!= so
&& (!scg
->selected_objects
||
2407 NULL
== g_hash_table_lookup (scg
->selected_objects
, so
)))
2408 scg_object_select (scg
, so
);
2410 sheet_object_populate_menu (so
, actions
);
2412 if (actions
->len
== 0) {
2413 g_ptr_array_free (actions
, TRUE
);
2417 menu
= sheet_object_build_menu
2418 (sheet_object_get_view (so
, (SheetObjectViewContainer
*) pane
),
2420 g_object_set_data_full (G_OBJECT (menu
), "actions", actions
,
2421 (GDestroyNotify
)cb_ptr_array_free
);
2422 gtk_widget_show_all (menu
);
2423 gnumeric_popup_menu (GTK_MENU (menu
), event
);
2427 cb_collect_selected_objs (SheetObject
*so
, double *coords
, GSList
**accum
)
2429 *accum
= g_slist_prepend (*accum
, so
);
2433 cb_pane_popup_menu (GnmPane
*pane
)
2435 SheetControlGUI
*scg
= pane
->simple
.scg
;
2437 /* ignore new_object, it is not visible, and should not create a
2439 if (NULL
!= scg
->selected_objects
) {
2440 GSList
*accum
= NULL
;
2441 g_hash_table_foreach (scg
->selected_objects
,
2442 (GHFunc
) cb_collect_selected_objs
, &accum
);
2443 if (NULL
!= accum
&& NULL
== accum
->next
)
2444 gnm_pane_display_object_menu (pane
, accum
->data
, NULL
);
2445 g_slist_free (accum
);
2447 /* the popup-menu signal is a binding. the grid almost always
2448 * has focus we need to cheat to find out if the user
2449 * realllllly wants a col/row header menu */
2450 gboolean is_col
= FALSE
;
2451 gboolean is_row
= FALSE
;
2452 GdkWindow
*gdk_win
= gdk_device_get_window_at_position (
2453 gtk_get_current_event_device (),
2456 if (gdk_win
!= NULL
) {
2457 gpointer gtk_win_void
= NULL
;
2458 GtkWindow
*gtk_win
= NULL
;
2459 gdk_window_get_user_data (gdk_win
, >k_win_void
);
2460 gtk_win
= gtk_win_void
;
2461 if (gtk_win
!= NULL
) {
2462 if (gtk_win
== (GtkWindow
*)pane
->col
.canvas
)
2464 else if (gtk_win
== (GtkWindow
*)pane
->row
.canvas
)
2469 scg_context_menu (scg
, NULL
, is_col
, is_row
);
2474 control_point_set_cursor (SheetControlGUI
const *scg
, GocItem
*ctrl_pt
)
2476 SheetObject
*so
= g_object_get_data (G_OBJECT (ctrl_pt
), "so");
2477 int idx
= GPOINTER_TO_INT (g_object_get_data (G_OBJECT (ctrl_pt
), "index"));
2478 double const *coords
= g_hash_table_lookup (scg
->selected_objects
, so
);
2479 gboolean invert_h
= coords
[0] > coords
[2];
2480 gboolean invert_v
= coords
[1] > coords
[3];
2481 GdkCursorType cursor
;
2483 if (goc_canvas_get_direction (ctrl_pt
->canvas
) == GOC_DIRECTION_RTL
)
2484 invert_h
= !invert_h
;
2487 case 1: invert_v
= !invert_v
;
2489 case 6: cursor
= invert_v
? GDK_TOP_SIDE
: GDK_BOTTOM_SIDE
;
2492 case 3: invert_h
= !invert_h
;
2494 case 4: cursor
= invert_h
? GDK_LEFT_SIDE
: GDK_RIGHT_SIDE
;
2497 case 2: invert_h
= !invert_h
;
2499 case 0: cursor
= invert_v
2500 ? (invert_h
? GDK_BOTTOM_RIGHT_CORNER
: GDK_BOTTOM_LEFT_CORNER
)
2501 : (invert_h
? GDK_TOP_RIGHT_CORNER
: GDK_TOP_LEFT_CORNER
);
2504 case 7: invert_h
= !invert_h
;
2506 case 5: cursor
= invert_v
2507 ? (invert_h
? GDK_TOP_RIGHT_CORNER
: GDK_TOP_LEFT_CORNER
)
2508 : (invert_h
? GDK_BOTTOM_RIGHT_CORNER
: GDK_BOTTOM_LEFT_CORNER
);
2515 gnm_widget_set_cursor_type (GTK_WIDGET (ctrl_pt
->canvas
), cursor
);
2519 target_list_add_list (GtkTargetList
*targets
, GtkTargetList
*added_targets
)
2522 GtkTargetEntry
*gte
;
2524 g_return_if_fail (targets
!= NULL
);
2526 if (added_targets
== NULL
)
2529 gte
= gtk_target_table_new_from_list (added_targets
, &n
);
2530 gtk_target_list_add_table (targets
, gte
, n
);
2531 gtk_target_table_free (gte
, n
);
2535 * Drag one or more sheet objects using GTK drag and drop, to the same
2536 * sheet, another workbook, another gnumeric or a different application.
2539 gnm_pane_drag_begin (GnmPane
*pane
, SheetObject
*so
, GdkEvent
*event
)
2541 GtkTargetList
*targets
, *im_targets
;
2542 GocCanvas
*canvas
= GOC_CANVAS (pane
);
2543 SheetControlGUI
*scg
= pane
->simple
.scg
;
2545 SheetObject
*imageable
= NULL
, *exportable
= NULL
;
2547 SheetObject
*candidate
;
2549 targets
= gtk_target_list_new (drag_types_out
,
2550 G_N_ELEMENTS (drag_types_out
));
2551 objects
= go_hash_keys (scg
->selected_objects
);
2552 for (ptr
= objects
; ptr
!= NULL
; ptr
= ptr
->next
) {
2553 candidate
= GNM_SO (ptr
->data
);
2555 if (exportable
== NULL
&&
2556 GNM_IS_SO_EXPORTABLE (candidate
))
2557 exportable
= candidate
;
2558 if (imageable
== NULL
&&
2559 GNM_IS_SO_IMAGEABLE (candidate
))
2560 imageable
= candidate
;
2564 im_targets
= sheet_object_exportable_get_target_list (exportable
);
2565 if (im_targets
!= NULL
) {
2566 target_list_add_list (targets
, im_targets
);
2567 gtk_target_list_unref (im_targets
);
2571 im_targets
= sheet_object_get_target_list (imageable
);
2572 if (im_targets
!= NULL
) {
2573 target_list_add_list (targets
, im_targets
);
2574 gtk_target_list_unref (im_targets
);
2579 if (gnm_debug_flag ("dnd")) {
2581 GtkTargetEntry
*gte
= gtk_target_table_new_from_list (targets
, &n
);
2582 g_printerr ("%u offered formats:\n", n
);
2583 for (i
= 0; i
< n
; i
++)
2584 g_printerr ("%s\n", gte
[n
].target
);
2585 gtk_target_table_free (gte
, n
);
2588 gtk_drag_begin (GTK_WIDGET (canvas
), targets
,
2589 GDK_ACTION_COPY
| GDK_ACTION_MOVE
,
2590 pane
->drag
.button
, event
);
2591 gtk_target_list_unref (targets
);
2592 g_slist_free (objects
);
2596 gnm_pane_object_start_resize (GnmPane
*pane
, int button
, guint64 x
, gint64 y
,
2597 SheetObject
*so
, int drag_type
, gboolean is_creation
)
2601 g_return_if_fail (GNM_IS_SO (so
));
2602 g_return_if_fail (0 <= drag_type
);
2603 g_return_if_fail (drag_type
< 9);
2605 ctrl_pts
= g_hash_table_lookup (pane
->drag
.ctrl_pts
, so
);
2607 g_return_if_fail (NULL
!= ctrl_pts
);
2609 if (is_creation
&& !sheet_object_can_resize (so
)) {
2610 scg_objects_drag_commit (pane
->simple
.scg
, 9, TRUE
,
2614 gnm_simple_canvas_grab (ctrl_pts
[drag_type
]);
2615 pane
->drag
.created_objects
= is_creation
;
2616 pane
->drag
.button
= button
;
2617 pane
->drag
.last_x
= pane
->drag
.origin_x
= x
;
2618 pane
->drag
.last_y
= pane
->drag
.origin_y
= y
;
2619 pane
->drag
.had_motion
= FALSE
;
2620 gnm_pane_slide_init (pane
);
2621 gnm_widget_set_cursor_type (GTK_WIDGET (pane
), GDK_HAND2
);
2625 GnmControlCircleItem
2627 typedef GocCircle GnmControlCircle
;
2628 typedef GocCircleClass GnmControlCircleClass
;
2630 #define CONTROL_TYPE_CIRCLE (control_circle_get_type ())
2631 #define CONTROL_CIRCLE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CONTROL_TYPE_CIRCLE, GnmControlCircle))
2632 #define CONTROL_IS_CIRCLE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CONTROL_TYPE_CIRCLE))
2634 static GType
control_circle_get_type (void);
2637 control_point_button_pressed (GocItem
*item
, int button
, double x
, double y
)
2639 GnmPane
*pane
= GNM_PANE (item
->canvas
);
2640 GdkEventButton
*event
= (GdkEventButton
*) goc_canvas_get_cur_event (item
->canvas
);
2644 if (0 != pane
->drag
.button
)
2647 x
*= goc_canvas_get_pixels_per_unit (item
->canvas
);
2648 y
*= goc_canvas_get_pixels_per_unit (item
->canvas
);
2649 so
= g_object_get_data (G_OBJECT (item
), "so");
2650 idx
= GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item
), "index"));
2651 switch (event
->button
) {
2653 case 2: gnm_pane_object_start_resize (pane
, button
, x
, y
, so
, idx
, FALSE
);
2655 case 3: gnm_pane_display_object_menu (pane
, so
, (GdkEvent
*) event
);
2657 default: /* Ignore mouse wheel events */
2664 control_point_button_released (GocItem
*item
, int button
, G_GNUC_UNUSED
double x
, G_GNUC_UNUSED
double y
)
2666 GnmPane
*pane
= GNM_PANE (item
->canvas
);
2667 SheetControlGUI
*scg
= pane
->simple
.scg
;
2671 if (pane
->drag
.button
!= button
)
2673 so
= g_object_get_data (G_OBJECT (item
), "so");
2674 idx
= GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item
), "index"));
2675 pane
->drag
.button
= 0;
2676 gnm_simple_canvas_ungrab (item
);
2677 gnm_pane_slide_stop (pane
);
2678 control_point_set_cursor (scg
, item
);
2680 ; /* ignore fake event generated by the dnd code */
2681 else if (pane
->drag
.had_motion
)
2682 scg_objects_drag_commit (scg
, idx
,
2683 pane
->drag
.created_objects
,
2685 else if (pane
->drag
.created_objects
&& idx
== 7) {
2687 sheet_object_default_size (so
, &w
, &h
);
2688 scg_objects_drag (scg
, NULL
, NULL
, &w
, &h
, 7, FALSE
, FALSE
, FALSE
);
2689 scg_objects_drag_commit (scg
, 7, TRUE
,
2692 gnm_pane_clear_obj_size_tip (pane
);
2697 control_point_motion (GocItem
*item
, double x
, double y
)
2699 GnmPane
*pane
= GNM_PANE (item
->canvas
);
2700 GdkEventMotion
*event
= (GdkEventMotion
*) goc_canvas_get_cur_event (item
->canvas
);
2704 if (0 == pane
->drag
.button
)
2707 x
*= goc_canvas_get_pixels_per_unit (item
->canvas
);
2708 y
*= goc_canvas_get_pixels_per_unit (item
->canvas
);
2709 /* TODO : motion is still too granular along the internal axis
2710 * when the other axis is external.
2711 * eg drag from middle of sheet down. The x axis is still internal
2712 * onlt the y is external, however, since we are autoscrolling
2713 * we are limited to moving with col/row coords, not x,y.
2714 * Possible solution would be to change the EXTERIOR_ONLY flag
2715 * to something like USE_PIXELS_INSTEAD_OF_COLROW and change
2716 * the semantics of the col,row args in the callback. However,
2717 * that is more work than I want to do right now.
2719 so
= g_object_get_data (G_OBJECT (item
), "so");
2720 idx
= GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item
), "index"));
2722 gnm_pane_drag_begin (pane
, so
, (GdkEvent
*) event
);
2723 else if (gnm_pane_handle_motion (pane
,
2725 GNM_PANE_SLIDE_X
| GNM_PANE_SLIDE_Y
|
2726 GNM_PANE_SLIDE_EXTERIOR_ONLY
,
2727 cb_slide_handler
, item
))
2728 gnm_pane_object_move (pane
, G_OBJECT (item
),
2730 (event
->state
& GDK_CONTROL_MASK
) != 0,
2731 (event
->state
& GDK_SHIFT_MASK
) != 0);
2736 control_point_button2_pressed (GocItem
*item
, int button
, G_GNUC_UNUSED
double x
, G_GNUC_UNUSED
double y
)
2738 GnmPane
*pane
= GNM_PANE (item
->canvas
);
2739 SheetControlGUI
*scg
= pane
->simple
.scg
;
2742 so
= g_object_get_data (G_OBJECT (item
), "so");
2743 if (pane
->drag
.button
== 1)
2744 sheet_object_get_editor (so
, GNM_SHEET_CONTROL (scg
));
2749 update_control_point_colors (GocItem
*item
, GtkStateFlags flags
)
2751 GtkStyleContext
*context
= goc_item_get_style_context (item
);
2752 GOStyle
*style
= go_styled_object_get_style (GO_STYLED_OBJECT (item
));
2753 GdkRGBA
*fore
, *back
;
2755 gtk_style_context_get (context
, flags
,
2757 "background-color", &back
,
2759 go_color_from_gdk_rgba (fore
, &style
->line
.color
);
2760 go_color_from_gdk_rgba (back
, &style
->fill
.pattern
.back
);
2761 gdk_rgba_free (fore
);
2762 gdk_rgba_free (back
);
2763 goc_item_invalidate (item
);
2767 control_point_enter_notify (GocItem
*item
, G_GNUC_UNUSED
double x
, G_GNUC_UNUSED
double y
)
2769 GnmPane
*pane
= GNM_PANE (item
->canvas
);
2770 SheetControlGUI
*scg
= pane
->simple
.scg
;
2773 control_point_set_cursor (scg
, item
);
2775 pane
->cur_object
= g_object_get_data (G_OBJECT (item
), "so");
2776 idx
= GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item
), "index"));
2778 update_control_point_colors (item
, GTK_STATE_FLAG_PRELIGHT
);
2779 gnm_pane_display_obj_size_tip (pane
, item
);
2785 control_point_leave_notify (GocItem
*item
, G_GNUC_UNUSED
double x
, G_GNUC_UNUSED
double y
)
2787 GnmPane
*pane
= GNM_PANE (item
->canvas
);
2788 SheetControlGUI
*scg
= pane
->simple
.scg
;
2791 control_point_set_cursor (scg
, item
);
2793 idx
= GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item
), "index"));
2795 update_control_point_colors (item
, GTK_STATE_FLAG_NORMAL
);
2796 gnm_pane_clear_obj_size_tip (pane
);
2798 pane
->cur_object
= NULL
;
2803 control_circle_class_init (GocItemClass
*item_klass
)
2805 item_klass
->button_pressed
= control_point_button_pressed
;
2806 item_klass
->button_released
= control_point_button_released
;
2807 item_klass
->motion
= control_point_motion
;
2808 item_klass
->button2_pressed
= control_point_button2_pressed
;
2809 item_klass
->enter_notify
= control_point_enter_notify
;
2810 item_klass
->leave_notify
= control_point_leave_notify
;
2813 static GSF_CLASS (GnmControlCircle
, control_circle
,
2814 control_circle_class_init
, NULL
,
2817 #define GNM_ITEM_ACETATE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), item_acetate_get_type (), ItemAcetate))
2818 #define GNM_IS_ITEM_ACETATE(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), item_acetate_get_type ()))
2822 static GType
item_acetate_get_type (void);
2824 typedef GocRectangle ItemAcetate
;
2825 typedef GocRectangleClass ItemAcetateClass
;
2828 item_acetate_distance (GocItem
*item
, double x
, double y
, GocItem
**actual_item
)
2830 if (x
< (item
->x0
- MARGIN
) ||
2831 x
> (item
->x1
+ MARGIN
) ||
2832 y
< (item
->y0
- MARGIN
) ||
2833 y
> (item
->y1
+ MARGIN
))
2835 *actual_item
= item
;
2840 item_acetate_class_init (GocItemClass
*item_class
)
2842 item_class
->distance
= item_acetate_distance
;
2843 item_class
->button_pressed
= control_point_button_pressed
;
2844 item_class
->button_released
= control_point_button_released
;
2845 item_class
->motion
= control_point_motion
;
2846 item_class
->button2_pressed
= control_point_button2_pressed
;
2847 item_class
->enter_notify
= control_point_enter_notify
;
2848 item_class
->leave_notify
= control_point_leave_notify
;
2851 static GSF_CLASS (ItemAcetate
, item_acetate
,
2852 item_acetate_class_init
, NULL
,
2856 * new_control_point:
2858 * @idx: control point index to be created
2859 * @x: x coordinate of control point
2860 * @y: y coordinate of control point
2862 * This is used to create a number of control points in a sheet
2863 * object, the meaning of them is used in other parts of the code
2864 * to belong to the following locations:
2866 * 0 -------- 1 -------- 2
2870 * 5 -------- 6 -------- 7
2872 * 8 == a clear overlay that extends slightly beyond the region
2873 * 9 == an optional stippled rectangle for moving/resizing expensive
2877 new_control_point (GnmPane
*pane
, SheetObject
*so
, int idx
, double x
, double y
)
2881 int radius
, outline
;
2882 double scale
= GOC_CANVAS (pane
)->pixels_per_unit
;
2884 gtk_widget_style_get (GTK_WIDGET (pane
),
2885 "control-circle-size", &radius
,
2886 "control-circle-outline", &outline
,
2889 style
= go_style_new ();
2890 style
->line
.width
= outline
;
2891 style
->line
.auto_color
= FALSE
;
2892 style
->line
.dash_type
= GO_LINE_SOLID
; /* anything but 0 */
2893 style
->line
.pattern
= GO_PATTERN_SOLID
;
2894 item
= goc_item_new (
2896 CONTROL_TYPE_CIRCLE
,
2899 "radius", radius
/ scale
,
2901 g_object_unref (style
);
2903 update_control_point_colors (item
, GTK_STATE_FLAG_NORMAL
);
2905 g_object_set_data (G_OBJECT (item
), "index", GINT_TO_POINTER (idx
));
2906 g_object_set_data (G_OBJECT (item
), "so", so
);
2913 * Changes the x and y position of the idx-th control point,
2914 * creating the control point if necessary.
2917 set_item_x_y (GnmPane
*pane
, SheetObject
*so
, GocItem
**ctrl_pts
,
2918 int idx
, double x
, double y
, gboolean visible
)
2920 double scale
= GOC_CANVAS (pane
)->pixels_per_unit
;
2921 if (ctrl_pts
[idx
] == NULL
)
2922 ctrl_pts
[idx
] = new_control_point (pane
, so
, idx
, x
/ scale
, y
/ scale
);
2924 goc_item_set (ctrl_pts
[idx
], "x", x
/ scale
, "y", y
/ scale
, NULL
);
2926 goc_item_show (ctrl_pts
[idx
]);
2928 goc_item_hide (ctrl_pts
[idx
]);
2931 #define normalize_high_low(d1,d2) if (d1<d2) { double tmp=d1; d1=d2; d2=tmp;}
2934 set_acetate_coords (GnmPane
*pane
, SheetObject
*so
, GocItem
**ctrl_pts
,
2935 double l
, double t
, double r
, double b
)
2937 double scale
= goc_canvas_get_pixels_per_unit (GOC_CANVAS (pane
));
2938 int radius
, outline
;
2940 if (!sheet_object_rubber_band_directly (so
)) {
2941 if (NULL
== ctrl_pts
[9]) {
2942 GOStyle
*style
= go_style_new ();
2943 GtkStyleContext
*context
;
2947 ctrl_pts
[9] = item
= goc_item_new (pane
->action_items
,
2950 context
= goc_item_get_style_context (item
);
2951 gtk_style_context_add_class (context
, "object-size");
2952 gtk_style_context_add_class (context
, "rubber-band");
2954 style
->fill
.auto_type
= FALSE
;
2955 style
->fill
.type
= GO_STYLE_FILL_PATTERN
;
2956 style
->fill
.auto_back
= FALSE
;
2957 style
->fill
.pattern
.back
= 0;
2958 style
->fill
.auto_fore
= FALSE
;
2959 style
->fill
.pattern
.fore
= 0;
2960 style
->line
.pattern
= GO_PATTERN_FOREGROUND_SOLID
;
2961 style
->line
.width
= 0.;
2962 style
->line
.auto_color
= FALSE
;
2963 style
->line
.color
= 0;
2964 gnm_style_context_get_color (context
, GTK_STATE_FLAG_NORMAL
, &rgba
);
2965 go_color_from_gdk_rgba (&rgba
, &style
->line
.fore
);
2966 go_styled_object_set_style (GO_STYLED_OBJECT (item
),
2968 g_object_unref (style
);
2969 goc_item_lower_to_bottom (item
);
2971 normalize_high_low (r
, l
);
2972 normalize_high_low (b
, t
);
2973 goc_item_set (ctrl_pts
[9],
2974 "x", l
/ scale
, "y", t
/ scale
,
2975 "width", (r
- l
) / scale
, "height", (b
- t
) / scale
,
2979 SheetObjectView
*sov
= sheet_object_get_view (so
, (SheetObjectViewContainer
*)pane
);
2981 sov
= sheet_object_new_view (so
, (SheetObjectViewContainer
*)pane
);
2983 coords
[0] = l
; coords
[2] = r
; coords
[1] = t
; coords
[3] = b
;
2985 sheet_object_view_set_bounds (sov
, coords
, TRUE
);
2986 normalize_high_low (r
, l
);
2987 normalize_high_low (b
, t
);
2990 gtk_widget_style_get (GTK_WIDGET (pane
),
2991 "control-circle-size", &radius
,
2992 "control-circle-outline", &outline
,
2995 l
-= (radius
+ outline
) / 2 - 1;
2996 r
+= (radius
+ outline
) / 2;
2997 t
-= (radius
+ outline
) / 2 - 1;
2998 b
+= (radius
+ outline
) / 2;
3000 if (NULL
== ctrl_pts
[8]) {
3001 GOStyle
*style
= go_style_new ();
3004 style
->fill
.auto_type
= FALSE
;
3005 style
->fill
.type
= GO_STYLE_FILL_PATTERN
;
3006 style
->fill
.auto_back
= FALSE
;
3007 go_pattern_set_solid (&style
->fill
.pattern
, 0);
3008 style
->line
.auto_dash
= FALSE
;
3009 style
->line
.dash_type
= GO_LINE_NONE
;
3010 /* work around the screwup in shapes that adds a large
3011 * border to anything that uses miter (is this required for
3012 * a rectangle in goc-canvas? */
3013 style
->line
.join
= CAIRO_LINE_JOIN_ROUND
;
3014 item
= goc_item_new (
3016 item_acetate_get_type (),
3019 g_object_unref (style
);
3020 g_object_set_data (G_OBJECT (item
), "index",
3021 GINT_TO_POINTER (8));
3022 g_object_set_data (G_OBJECT (item
), "so", so
);
3026 goc_item_set (ctrl_pts
[8],
3029 "width", (r
- l
) / scale
,
3030 "height", (b
- t
) / scale
,
3035 gnm_pane_object_unselect (GnmPane
*pane
, SheetObject
*so
)
3037 gnm_pane_clear_obj_size_tip (pane
);
3038 g_hash_table_remove (pane
->drag
.ctrl_pts
, so
);
3042 * gnm_pane_object_update_bbox:
3046 * Updates the position and potentially creates control points
3047 * for manipulating the size/position of @so.
3050 gnm_pane_object_update_bbox (GnmPane
*pane
, SheetObject
*so
)
3052 GocItem
**ctrl_pts
= g_hash_table_lookup (pane
->drag
.ctrl_pts
, so
);
3053 double const *pts
= g_hash_table_lookup (
3054 pane
->simple
.scg
->selected_objects
, so
);
3055 int radius
, outline
, total_size
;
3057 if (ctrl_pts
== NULL
) {
3058 ctrl_pts
= g_new0 (GocItem
*, 10);
3059 g_hash_table_insert (pane
->drag
.ctrl_pts
, so
, ctrl_pts
);
3062 g_return_if_fail (ctrl_pts
!= NULL
);
3064 gtk_widget_style_get (GTK_WIDGET (pane
),
3065 "control-circle-size", &radius
,
3066 "control-circle-outline", &outline
,
3068 /* space for 2 halves and a full */
3069 total_size
= radius
* 4 + outline
* 2;
3071 /* set the acetate 1st so that the other points will override it */
3072 set_acetate_coords (pane
, so
, ctrl_pts
, pts
[0], pts
[1], pts
[2], pts
[3]);
3073 if (sheet_object_can_resize (so
)) {
3074 set_item_x_y (pane
, so
, ctrl_pts
, 0, pts
[0], pts
[1], TRUE
);
3075 set_item_x_y (pane
, so
, ctrl_pts
, 1, (pts
[0] + pts
[2]) / 2, pts
[1],
3076 fabs (pts
[2]-pts
[0]) >= total_size
);
3077 set_item_x_y (pane
, so
, ctrl_pts
, 2, pts
[2], pts
[1], TRUE
);
3078 set_item_x_y (pane
, so
, ctrl_pts
, 3, pts
[0], (pts
[1] + pts
[3]) / 2,
3079 fabs (pts
[3]-pts
[1]) >= total_size
);
3080 set_item_x_y (pane
, so
, ctrl_pts
, 4, pts
[2], (pts
[1] + pts
[3]) / 2,
3081 fabs (pts
[3]-pts
[1]) >= total_size
);
3082 set_item_x_y (pane
, so
, ctrl_pts
, 5, pts
[0], pts
[3], TRUE
);
3083 set_item_x_y (pane
, so
, ctrl_pts
, 6, (pts
[0] + pts
[2]) / 2, pts
[3],
3084 fabs (pts
[2]-pts
[0]) >= total_size
);
3085 set_item_x_y (pane
, so
, ctrl_pts
, 7, pts
[2], pts
[3], TRUE
);
3090 cb_bounds_changed (SheetObject
*so
, GocItem
*sov
)
3092 double coords
[4], *cur
;
3093 SheetControlGUI
*scg
= GNM_SIMPLE_CANVAS (sov
->canvas
)->scg
;
3094 if (GNM_PANE (sov
->canvas
)->drag
.button
!= 0)
3095 return; /* do not reset bounds during drag */
3097 scg_object_anchor_to_coords (scg
, sheet_object_get_anchor (so
), coords
);
3098 if (NULL
!= scg
->selected_objects
&&
3099 NULL
!= (cur
= g_hash_table_lookup (scg
->selected_objects
, so
))) {
3101 for (i
= 4; i
-- > 0 ;) cur
[i
] = coords
[i
];
3102 gnm_pane_object_update_bbox (GNM_PANE (sov
->canvas
), so
);
3105 sheet_object_view_set_bounds (GNM_SO_VIEW (sov
),
3106 coords
, so
->flags
& SHEET_OBJECT_IS_VISIBLE
);
3110 * gnm_pane_object_register:
3111 * @so: A sheet object
3112 * @view: A canvas item acting as a view for @so
3113 * @selectable: Add handlers for selecting and editing the object
3115 * Setup some standard callbacks for manipulating a view of a sheet object.
3116 * Returns: (transfer none): @view set to a #SheetObjectView.
3119 gnm_pane_object_register (SheetObject
*so
, GocItem
*view
, gboolean selectable
)
3121 g_signal_connect_object (so
, "bounds-changed",
3122 G_CALLBACK (cb_bounds_changed
), view
, 0);
3123 return GNM_SO_VIEW (view
);
3127 * gnm_pane_object_widget_register:
3128 * @so: A sheet object
3129 * @widget: The widget for the sheet object view
3130 * @view: A canvas item acting as a view for @so
3132 * Setup some standard callbacks for manipulating widgets as views of sheet
3136 gnm_pane_widget_register (SheetObject
*so
, GtkWidget
*w
, GocItem
*view
)
3138 if (GTK_IS_CONTAINER (w
)) {
3139 GList
*ptr
, *children
= gtk_container_get_children (GTK_CONTAINER (w
));
3140 for (ptr
= children
; ptr
!= NULL
; ptr
= ptr
->next
)
3141 gnm_pane_widget_register (so
, ptr
->data
, view
);
3142 g_list_free (children
);
3147 gnm_pane_set_direction (GnmPane
*pane
, GocDirection direction
)
3149 goc_canvas_set_direction (GOC_CANVAS (pane
), direction
);
3150 if (pane
->col
.canvas
!= NULL
)
3151 goc_canvas_set_direction (pane
->col
.canvas
, direction
);