2 * Gnumeric's extended canvas used to display a pane
5 * Miguel de Icaza (miguel@kernel.org)
6 * Jody Goldberg (jody@gnome.org)
8 #include <gnumeric-config.h>
11 #include <gnm-pane-impl.h>
14 #include <sheet-control-gui-priv.h>
18 #include <selection.h>
19 #include <parse-util.h>
22 #include <sheet-view.h>
23 #include <application.h>
24 #include <workbook-view.h>
25 #include <wbc-gtk-impl.h>
27 #include <workbook-cmd-format.h>
30 #include <clipboard.h>
31 #include <sheet-filter-combo.h>
32 #include <widgets/gnm-cell-combo-view.h>
34 #include <item-cursor.h>
35 #include <item-edit.h>
36 #include <item-grid.h>
37 #include <gnumeric-conf.h>
39 #include <gsf/gsf-impl-utils.h>
41 #include <gdk/gdkkeysyms.h>
45 #define SCROLL_LOCK_MASK GDK_MOD5_MASK
47 typedef GocCanvasClass GnmPaneClass
;
48 static GocCanvasClass
*parent_klass
;
50 static void cb_pane_popup_menu (GnmPane
*pane
);
51 static void gnm_pane_clear_obj_size_tip (GnmPane
*pane
);
52 static void gnm_pane_display_obj_size_tip (GnmPane
*pane
, GocItem
*ctrl_pt
);
55 * For now, application/x-gnumeric is disabled. It handles neither
56 * images nor graphs correctly.
58 static GtkTargetEntry
const drag_types_in
[] = {
59 {(char *) "GNUMERIC_SAME_PROC", GTK_TARGET_SAME_APP
, 0},
60 /* {(char *) "application/x-gnumeric", 0, 0}, */
63 static GtkTargetEntry
const drag_types_out
[] = {
64 {(char *) "GNUMERIC_SAME_PROC", GTK_TARGET_SAME_APP
, 0},
65 {(char *) "application/x-gnumeric", 0, 0},
69 gnm_pane_guru_key (WBCGtk
const *wbcg
, GdkEvent
*event
)
71 GtkWidget
*entry
, *guru
= wbc_gtk_get_guru (wbcg
);
76 entry
= wbcg_get_entry_underlying (wbcg
);
77 gtk_widget_event (entry
? entry
: guru
, event
);
82 gnm_pane_object_key_press (GnmPane
*pane
, GdkEventKey
*ev
)
84 SheetControlGUI
*scg
= pane
->simple
.scg
;
85 SheetControl
*sc
= GNM_SHEET_CONTROL (scg
);
86 gboolean
const shift
= 0 != (ev
->state
& GDK_SHIFT_MASK
);
87 gboolean
const control
= 0 != (ev
->state
& GDK_CONTROL_MASK
);
88 gboolean
const alt
= 0 != (ev
->state
& GDK_MOD1_MASK
);
89 gboolean
const symmetric
= control
&& alt
;
90 double const delta
= 1.0 / GOC_CANVAS (pane
)->pixels_per_unit
;
95 gnm_app_clipboard_unant ();
98 case GDK_KEY_BackSpace
: /* Ick! */
99 case GDK_KEY_KP_Delete
:
101 if (scg
->selected_objects
!= NULL
) {
102 cmd_objects_delete (sc
->wbc
,
103 go_hash_keys (scg
->selected_objects
), NULL
);
110 case GDK_KEY_ISO_Left_Tab
:
112 if ((scg_sheet (scg
))->sheet_objects
!= NULL
) {
113 scg_object_select_next (scg
, (ev
->state
& GDK_SHIFT_MASK
) != 0);
118 case GDK_KEY_KP_Left
: case GDK_KEY_Left
:
119 scg_objects_nudge (scg
, pane
, (alt
? 4 : (control
? 3 : 8)), -delta
, 0, symmetric
, shift
);
120 gnm_pane_display_obj_size_tip (pane
, NULL
);
122 case GDK_KEY_KP_Right
: case GDK_KEY_Right
:
123 scg_objects_nudge (scg
, pane
, (alt
? 4 : (control
? 3 : 8)), delta
, 0, symmetric
, shift
);
124 gnm_pane_display_obj_size_tip (pane
, NULL
);
126 case GDK_KEY_KP_Up
: case GDK_KEY_Up
:
127 scg_objects_nudge (scg
, pane
, (alt
? 6 : (control
? 1 : 8)), 0, -delta
, symmetric
, shift
);
128 gnm_pane_display_obj_size_tip (pane
, NULL
);
130 case GDK_KEY_KP_Down
: case GDK_KEY_Down
:
131 scg_objects_nudge (scg
, pane
, (alt
? 6 : (control
? 1 : 8)), 0, delta
, symmetric
, shift
);
132 gnm_pane_display_obj_size_tip (pane
, NULL
);
142 gnm_pane_key_mode_sheet (GnmPane
*pane
, GdkEventKey
*kevent
,
143 gboolean allow_rangesel
)
145 GdkEvent
*event
= (GdkEvent
*)kevent
;
146 SheetControlGUI
*scg
= pane
->simple
.scg
;
147 SheetControl
*sc
= (SheetControl
*) scg
;
148 SheetView
*sv
= sc
->view
;
149 Sheet
*sheet
= sv
->sheet
;
150 WBCGtk
*wbcg
= scg
->wbcg
;
151 WorkbookControl
* wbc
= scg_wbc(scg
);
152 Workbook
* wb
= wb_control_get_workbook(wbc
);
153 gboolean delayed_movement
= FALSE
;
154 gboolean jump_to_bounds
;
155 gboolean is_enter
= FALSE
;
158 void (*movefn
) (SheetControlGUI
*, int n
, gboolean jump
, gboolean horiz
);
159 gboolean transition_keys
= gnm_conf_get_core_gui_editing_transitionkeys ();
160 gboolean
const end_mode
= wbcg
->last_key_was_end
;
161 GdkModifierType event_state
;
163 (void)gdk_event_get_state (event
, &event_state
);
164 state
= gnm_filter_modifiers (event_state
);
165 jump_to_bounds
= (event_state
& GDK_CONTROL_MASK
) != 0;
167 /* Update end-mode for magic end key stuff. */
168 if (kevent
->keyval
!= GDK_KEY_End
&& kevent
->keyval
!= GDK_KEY_KP_End
)
169 wbcg_set_end_mode (wbcg
, FALSE
);
172 movefn
= (event_state
& GDK_SHIFT_MASK
)
173 ? scg_rangesel_extend
176 movefn
= (event_state
& GDK_SHIFT_MASK
)
180 switch (kevent
->keyval
) {
182 scg_select_all (scg
);
184 case GDK_KEY_KP_Left
:
186 if (event_state
& GDK_MOD1_MASK
)
187 return TRUE
; /* Alt is used for accelerators */
189 if (event_state
& SCROLL_LOCK_MASK
)
190 scg_set_left_col (scg
, pane
->first
.col
- 1);
191 else if (transition_keys
&& jump_to_bounds
) {
192 delayed_movement
= TRUE
;
193 scg_queue_movement (scg
, movefn
,
194 -(pane
->last_visible
.col
- pane
->first
.col
),
197 (*movefn
) (scg
, sheet
->text_is_rtl
? 1 : -1,
198 jump_to_bounds
|| end_mode
, TRUE
);
201 case GDK_KEY_KP_Right
:
203 if (event_state
& GDK_MOD1_MASK
)
204 return TRUE
; /* Alt is used for accelerators */
206 if (event_state
& SCROLL_LOCK_MASK
)
207 scg_set_left_col (scg
, pane
->first
.col
+ 1);
208 else if (transition_keys
&& jump_to_bounds
) {
209 delayed_movement
= TRUE
;
210 scg_queue_movement (scg
, movefn
,
211 pane
->last_visible
.col
- pane
->first
.col
,
214 (*movefn
) (scg
, sheet
->text_is_rtl
? -1 : 1,
215 jump_to_bounds
|| end_mode
, TRUE
);
220 if (event_state
& SCROLL_LOCK_MASK
)
221 scg_set_top_row (scg
, pane
->first
.row
- 1);
222 else if (transition_keys
&& jump_to_bounds
) {
223 delayed_movement
= TRUE
;
224 scg_queue_movement (scg
, movefn
,
225 -(pane
->last_visible
.row
- pane
->first
.row
),
228 (*movefn
) (scg
, -1, jump_to_bounds
|| end_mode
, FALSE
);
231 case GDK_KEY_KP_Down
:
233 if (gnm_filter_modifiers (event_state
) == GDK_MOD1_MASK
) {
234 /* 1) Any in cell combos ? */
235 SheetObject
*so
= sv_wbv (sv
)->in_cell_combo
;
237 /* 2) How about any autofilters ? */
240 GSList
*objs
= sheet_objects_get (sheet
,
241 range_init_cellpos (&r
, &sv
->edit_pos
),
242 GNM_FILTER_COMBO_TYPE
);
244 so
= objs
->data
, g_slist_free (objs
);
248 SheetObjectView
*sov
= sheet_object_get_view (so
,
249 (SheetObjectViewContainer
*)pane
);
250 gnm_cell_combo_view_popdown
252 gdk_event_get_time (event
));
257 if (event_state
& SCROLL_LOCK_MASK
)
258 scg_set_top_row (scg
, pane
->first
.row
+ 1);
259 else if (transition_keys
&& jump_to_bounds
) {
260 delayed_movement
= TRUE
;
261 scg_queue_movement (scg
, movefn
,
262 pane
->last_visible
.row
- pane
->first
.row
,
265 (*movefn
) (scg
, 1, jump_to_bounds
|| end_mode
, FALSE
);
268 case GDK_KEY_KP_Page_Up
:
269 case GDK_KEY_Page_Up
:
270 if (event_state
& GDK_CONTROL_MASK
) {
271 if (event_state
& GDK_SHIFT_MASK
) {
272 int old_pos
= sheet
->index_in_wb
;
274 WorkbookSheetState
* old_state
= workbook_sheet_state_new (wb
);
275 workbook_sheet_move (sheet
, -1);
276 cmd_reorganize_sheets (wbc
, old_state
, sheet
);
279 gnm_notebook_prev_page (wbcg
->bnotebook
);
281 } else if ((event_state
& GDK_MOD1_MASK
) == 0) {
282 delayed_movement
= TRUE
;
283 scg_queue_movement (scg
, movefn
,
284 -(pane
->last_visible
.row
- pane
->first
.row
),
287 delayed_movement
= TRUE
;
288 scg_queue_movement (scg
, movefn
,
289 -(pane
->last_visible
.col
- pane
->first
.col
),
294 case GDK_KEY_KP_Page_Down
:
295 case GDK_KEY_Page_Down
:
296 if ((event_state
& GDK_CONTROL_MASK
) != 0){
297 if ((event_state
& GDK_SHIFT_MASK
) != 0){
298 int num_sheets
= workbook_sheet_count (wb
);
299 gint old_pos
= sheet
->index_in_wb
;
300 if (old_pos
< num_sheets
- 1) {
301 WorkbookSheetState
*old_state
= workbook_sheet_state_new (wb
);
302 workbook_sheet_move (sheet
, 1);
303 cmd_reorganize_sheets (wbc
, old_state
, sheet
);
306 gnm_notebook_next_page (wbcg
->bnotebook
);
308 } else if ((event_state
& GDK_MOD1_MASK
) == 0) {
309 delayed_movement
= TRUE
;
310 scg_queue_movement (scg
, movefn
,
311 pane
->last_visible
.row
- pane
->first
.row
,
314 delayed_movement
= TRUE
;
315 scg_queue_movement (scg
, movefn
,
316 pane
->last_visible
.col
- pane
->first
.col
,
321 case GDK_KEY_KP_Home
:
323 if (event_state
& SCROLL_LOCK_MASK
) {
324 scg_set_left_col (scg
, sv
->edit_pos
.col
);
325 scg_set_top_row (scg
, sv
->edit_pos
.row
);
326 } else if (end_mode
) {
327 /* Same as ctrl-end. */
328 GnmRange r
= sheet_get_extent (sheet
, FALSE
, TRUE
);
329 (*movefn
) (scg
, r
.end
.col
- sv
->edit_pos
.col
, FALSE
, TRUE
);
330 (*movefn
)(scg
, r
.end
.row
- sv
->edit_pos
.row
, FALSE
, FALSE
);
332 /* do the ctrl-home jump to A1 in 2 steps */
333 (*movefn
)(scg
, -gnm_sheet_get_max_cols (sheet
), FALSE
, TRUE
);
334 if ((event_state
& GDK_CONTROL_MASK
) || transition_keys
)
335 (*movefn
)(scg
, -gnm_sheet_get_max_rows (sheet
), FALSE
, FALSE
);
341 if (event_state
& SCROLL_LOCK_MASK
) {
342 int new_col
= sv
->edit_pos
.col
- (pane
->last_full
.col
- pane
->first
.col
);
343 int new_row
= sv
->edit_pos
.row
- (pane
->last_full
.row
- pane
->first
.row
);
344 scg_set_left_col (scg
, new_col
);
345 scg_set_top_row (scg
, new_row
);
346 } else if ((event_state
& GDK_CONTROL_MASK
)) {
347 GnmRange r
= sheet_get_extent (sheet
, FALSE
, TRUE
);
349 /* do the ctrl-end jump to the extent in 2 steps */
350 (*movefn
)(scg
, r
.end
.col
- sv
->edit_pos
.col
, FALSE
, TRUE
);
351 (*movefn
)(scg
, r
.end
.row
- sv
->edit_pos
.row
, FALSE
, FALSE
);
352 } else /* toggle end mode */
353 wbcg_set_end_mode (wbcg
, !end_mode
);
356 case GDK_KEY_KP_Insert
:
358 if (gnm_pane_guru_key (wbcg
, event
))
360 if (state
== GDK_CONTROL_MASK
)
361 gnm_sheet_view_selection_copy (sv
, GNM_WBC (wbcg
));
362 else if (state
== GDK_SHIFT_MASK
)
363 cmd_paste_to_selection (GNM_WBC (wbcg
), sv
, PASTE_DEFAULT
);
366 case GDK_KEY_BackSpace
:
367 if (wbcg_is_editing (wbcg
))
369 else if (!wbcg_is_editing (wbcg
) && (event_state
& GDK_CONTROL_MASK
) != 0) {
370 /* Re-center the view on the active cell */
371 scg_make_cell_visible (scg
, sv
->edit_pos
.col
,
372 sv
->edit_pos
.row
, FALSE
, TRUE
);
377 case GDK_KEY_KP_Delete
:
379 if (wbcg_is_editing (wbcg
)) {
380 /* stop auto-completion. then do a quick and cheesy update */
381 wbcg_auto_complete_destroy (wbcg
);
382 SCG_FOREACH_PANE (scg
, pane
, {
384 goc_item_invalidate (GOC_ITEM (pane
->editor
));
388 if (gnm_pane_guru_key (wbcg
, event
))
390 if (state
== GDK_SHIFT_MASK
) {
392 gnm_sheet_view_selection_cut (sv
, GNM_WBC (wbcg
));
394 cmd_selection_clear (GNM_WBC (wbcg
), CLEAR_VALUES
);
398 * NOTE : Keep these in sync with the condition
401 case GDK_KEY_KP_Enter
:
403 if (wbcg_is_editing (wbcg
) &&
404 (state
== GDK_CONTROL_MASK
||
405 state
== (GDK_CONTROL_MASK
|GDK_SHIFT_MASK
) ||
406 gnm_filter_modifiers (event_state
) == GDK_MOD1_MASK
))
407 /* Forward the keystroke to the input line */
408 return gtk_widget_event (
409 wbcg_get_entry_underlying (wbcg
), (GdkEvent
*) event
);
414 case GDK_KEY_ISO_Left_Tab
:
416 if (gnm_pane_guru_key (wbcg
, event
))
419 /* Be careful to restore the editing sheet if we are editing */
420 if (wbcg_is_editing (wbcg
))
421 sheet
= wbcg
->editing_sheet
;
423 /* registering the cmd clears it, restore it afterwards */
424 first_tab_col
= sv
->first_tab_col
;
426 if (wbcg_edit_finish (wbcg
, WBC_EDIT_ACCEPT
, NULL
)) {
427 GODirection dir
= gnm_conf_get_core_gui_editing_enter_moves_dir ();
429 sv
->first_tab_col
= first_tab_col
;
431 if ((event_state
& GDK_MOD1_MASK
) &&
432 (event_state
& GDK_CONTROL_MASK
) &&
434 if (event_state
& GDK_SHIFT_MASK
)
435 workbook_cmd_dec_indent (sc
->wbc
);
437 workbook_cmd_inc_indent (sc
->wbc
);
438 } else if (!is_enter
|| dir
!= GO_DIRECTION_NONE
) {
439 gboolean forward
= TRUE
;
440 gboolean horizontal
= TRUE
;
442 horizontal
= go_direction_is_horizontal (dir
);
443 forward
= go_direction_is_forward (dir
);
444 } else if ((event_state
& GDK_CONTROL_MASK
) &&
445 ((sc_sheet (sc
))->sheet_objects
!= NULL
)) {
446 scg_object_select_next
447 (scg
, (event_state
& GDK_SHIFT_MASK
) != 0);
451 if (event_state
& GDK_SHIFT_MASK
)
454 sv_selection_walk_step (sv
, forward
, horizontal
);
456 /* invalidate, in case Enter direction changes */
458 sv
->first_tab_col
= -1;
464 wbcg_edit_finish (wbcg
, WBC_EDIT_REJECT
, NULL
);
465 gnm_app_clipboard_unant ();
469 if (wbcg_is_editing (wbcg
))
470 return gtk_widget_event (
471 wbcg_get_entry_underlying (wbcg
), (GdkEvent
*) event
);
475 if (gnm_pane_guru_key (wbcg
, event
))
478 if (wbcg_is_editing (wbcg
)) {
479 GtkWidget
*entry
= (GtkWidget
*) wbcg_get_entry (wbcg
);
480 GtkWindow
*top
= wbcg_toplevel (wbcg
);
481 if (entry
!= gtk_window_get_focus (top
)) {
482 gtk_window_set_focus (top
, entry
);
486 if (!wbcg_edit_start (wbcg
, FALSE
, FALSE
))
487 return FALSE
; /* attempt to edit failed */
491 if (!wbcg_is_editing (wbcg
)) {
492 if ((event_state
& (GDK_MOD1_MASK
|GDK_CONTROL_MASK
)) != 0)
495 /* If the character is not printable do not start editing */
496 if (kevent
->length
== 0)
499 if (!wbcg_edit_start (wbcg
, TRUE
, TRUE
))
500 return FALSE
; /* attempt to edit failed */
502 scg_rangesel_stop (scg
, FALSE
);
505 /* Forward the keystroke to the input line */
506 return gtk_widget_event (wbcg_get_entry_underlying (wbcg
),
510 if (!delayed_movement
) {
511 if (wbcg_is_editing (wbcg
))
512 sheet_update_only_grid (sheet
);
514 sheet_update (sheet
);
521 gnm_pane_colrow_key_press (SheetControlGUI
*scg
, GdkEventKey
*event
,
522 gboolean allow_rangesel
)
524 SheetControl
*sc
= (SheetControl
*) scg
;
525 SheetView
*sv
= sc
->view
;
528 if (allow_rangesel
) {
529 if (scg
->rangesel
.active
)
530 target
= scg
->rangesel
.displayed
;
532 target
.start
= target
.end
= sv
->edit_pos_real
;
534 GnmRange
const *r
= selection_first_range (sv
, NULL
, NULL
);
540 if (event
->state
& GDK_SHIFT_MASK
) {
541 if (event
->state
& GDK_CONTROL_MASK
) /* full sheet */
542 /* TODO : How to handle ctrl-A too ? */
543 range_init_full_sheet (&target
, sv
->sheet
);
544 else { /* full row */
545 target
.start
.col
= 0;
546 target
.end
.col
= gnm_sheet_get_last_col (sv
->sheet
);
548 } else if (event
->state
& GDK_CONTROL_MASK
) { /* full col */
549 target
.start
.row
= 0;
550 target
.end
.row
= gnm_sheet_get_last_row (sv
->sheet
);
554 /* Accept during rangesel */
556 scg_rangesel_bound (scg
,
557 target
.start
.col
, target
.start
.row
,
558 target
.end
.col
, target
.end
.row
);
559 /* actually want the ctrl/shift space keys handled by the input module
560 * filters during an edit */
561 else if (!wbcg_is_editing (scg
->wbcg
))
562 sv_selection_set (sv
, &sv
->edit_pos
,
563 target
.start
.col
, target
.start
.row
,
564 target
.end
.col
, target
.end
.row
);
572 gnm_pane_key_press (GtkWidget
*widget
, GdkEventKey
*event
)
574 GnmPane
*pane
= GNM_PANE (widget
);
575 SheetControlGUI
*scg
= pane
->simple
.scg
;
576 gboolean allow_rangesel
;
578 switch (event
->keyval
) {
579 case GDK_KEY_Shift_L
: case GDK_KEY_Shift_R
:
580 case GDK_KEY_Alt_L
: case GDK_KEY_Alt_R
:
581 case GDK_KEY_Control_L
: case GDK_KEY_Control_R
:
582 return (*GTK_WIDGET_CLASS (parent_klass
)->key_press_event
) (widget
, event
);
585 /* Object manipulation */
586 if (scg
->selected_objects
!= NULL
||
587 scg
->wbcg
->new_object
!= NULL
) {
588 if (wbc_gtk_get_guru (scg
->wbcg
) == NULL
&&
589 gnm_pane_object_key_press (pane
, event
))
593 /* handle grabs after object keys to allow Esc to cancel, and arrows to
594 * fine tune position even while dragging */
595 if (scg
->grab_stack
> 0)
598 allow_rangesel
= wbcg_rangesel_possible (scg
->wbcg
);
600 /* handle ctrl/shift space before input-method filter steals it */
601 if (event
->keyval
== GDK_KEY_space
&&
602 gnm_pane_colrow_key_press (scg
, event
, allow_rangesel
))
605 pane
->insert_decimal
=
606 event
->keyval
== GDK_KEY_KP_Decimal
||
607 event
->keyval
== GDK_KEY_KP_Separator
;
609 if (gtk_im_context_filter_keypress (pane
->im_context
, event
))
612 gtk_im_context_reset (pane
->im_context
);
614 if (gnm_pane_key_mode_sheet (pane
, event
, allow_rangesel
))
617 return (*GTK_WIDGET_CLASS (parent_klass
)->key_press_event
) (widget
, event
);
621 gnm_pane_key_release (GtkWidget
*widget
, GdkEventKey
*event
)
623 GnmPane
*pane
= GNM_PANE (widget
);
624 SheetControl
*sc
= (SheetControl
*) pane
->simple
.scg
;
626 if (pane
->simple
.scg
->grab_stack
> 0)
629 if (gtk_im_context_filter_keypress (pane
->im_context
, event
))
632 * The status_region normally displays the current edit_pos
633 * When we extend the selection it changes to displaying the size of
634 * the selected region while we are selecting. When the shift key
635 * is released, or the mouse button is release we need to reset
636 * to displaying the edit pos.
638 if (pane
->simple
.scg
->selected_objects
== NULL
&&
639 (event
->keyval
== GDK_KEY_Shift_L
|| event
->keyval
== GDK_KEY_Shift_R
))
640 wb_view_selection_desc (wb_control_view (sc
->wbc
), TRUE
, NULL
);
642 return (*GTK_WIDGET_CLASS (parent_klass
)->key_release_event
) (widget
, event
);
646 gnm_pane_focus_in (GtkWidget
*widget
, GdkEventFocus
*event
)
648 GnmPane
*pane
= GNM_PANE (widget
);
649 gtk_im_context_focus_in (pane
->im_context
);
650 return (*GTK_WIDGET_CLASS (parent_klass
)->focus_in_event
) (widget
, event
);
654 gnm_pane_focus_out (GtkWidget
*widget
, GdkEventFocus
*event
)
656 gnm_pane_clear_obj_size_tip (GNM_PANE (widget
));
657 gtk_im_context_focus_out (GNM_PANE (widget
)->im_context
);
658 return (*GTK_WIDGET_CLASS (parent_klass
)->focus_out_event
) (widget
, event
);
662 gnm_pane_realize (GtkWidget
*w
)
664 if (GTK_WIDGET_CLASS (parent_klass
)->realize
)
665 (*GTK_WIDGET_CLASS (parent_klass
)->realize
) (w
);
667 gtk_im_context_set_client_window
668 (GNM_PANE (w
)->im_context
,
669 gtk_widget_get_window (gtk_widget_get_toplevel (w
)));
673 gnm_pane_unrealize (GtkWidget
*widget
)
677 pane
= GNM_PANE (widget
);
678 g_return_if_fail (pane
!= NULL
);
680 if (pane
->im_context
) {
681 gtk_im_context_set_client_window (pane
->im_context
, NULL
);
684 (*GTK_WIDGET_CLASS (parent_klass
)->unrealize
)(widget
);
688 gnm_pane_size_allocate (GtkWidget
*w
, GtkAllocation
*allocation
)
690 GnmPane
*pane
= GNM_PANE (w
);
691 (*GTK_WIDGET_CLASS (parent_klass
)->size_allocate
) (w
, allocation
);
692 gnm_pane_compute_visible_region (pane
, TRUE
);
696 gnm_pane_get_editable (GnmPane
const *pane
)
698 GnmExprEntry
*gee
= wbcg_get_entry_logical (pane
->simple
.scg
->wbcg
);
699 GtkEntry
*entry
= gnm_expr_entry_get_entry (gee
);
700 return GTK_EDITABLE (entry
);
704 cb_gnm_pane_commit (GtkIMContext
*context
, char const *str
, GnmPane
*pane
)
706 gint tmp_pos
, length
;
707 WBCGtk
*wbcg
= pane
->simple
.scg
->wbcg
;
708 GtkEditable
*editable
= gnm_pane_get_editable (pane
);
710 if (!wbcg_is_editing (wbcg
) && !wbcg_edit_start (wbcg
, TRUE
, TRUE
))
713 if (pane
->insert_decimal
) {
714 GString
const *s
= go_locale_get_decimal ();
718 length
= strlen (str
);
720 if (gtk_editable_get_selection_bounds (editable
, NULL
, NULL
))
721 gtk_editable_delete_selection (editable
);
723 tmp_pos
= gtk_editable_get_position (editable
);
724 if (gtk_entry_get_overwrite_mode (GTK_ENTRY (editable
)))
725 gtk_editable_delete_text (editable
,tmp_pos
,tmp_pos
+1);
728 tmp_pos
= gtk_editable_get_position (editable
);
729 gtk_editable_insert_text (editable
, str
, length
, &tmp_pos
);
730 gtk_editable_set_position (editable
, tmp_pos
);
734 cb_gnm_pane_preedit_start (GtkIMContext
*context
, GnmPane
*pane
)
736 WBCGtk
*wbcg
= pane
->simple
.scg
->wbcg
;
737 pane
->im_preedit_started
= TRUE
;
738 if (!wbcg_is_editing (wbcg
))
739 wbcg_edit_start (wbcg
, TRUE
, TRUE
);
743 cb_gnm_pane_preedit_changed (GtkIMContext
*context
, GnmPane
*pane
)
745 gchar
*preedit_string
;
748 WBCGtk
*wbcg
= pane
->simple
.scg
->wbcg
;
749 GtkEditable
*editable
= gnm_pane_get_editable (pane
);
750 if (!pane
->im_preedit_started
)
753 tmp_pos
= gtk_editable_get_position (editable
);
754 if (pane
->preedit_attrs
)
755 pango_attr_list_unref (pane
->preedit_attrs
);
756 gtk_im_context_get_preedit_string (pane
->im_context
, &preedit_string
, &pane
->preedit_attrs
, &cursor_pos
);
758 if (!wbcg_is_editing (wbcg
) && !wbcg_edit_start (wbcg
, FALSE
, TRUE
)) {
759 gtk_im_context_reset (pane
->im_context
);
760 pane
->preedit_length
= 0;
761 if (pane
->preedit_attrs
)
762 pango_attr_list_unref (pane
->preedit_attrs
);
763 pane
->preedit_attrs
= NULL
;
764 g_free (preedit_string
);
768 if (pane
->preedit_length
)
769 gtk_editable_delete_text (editable
,tmp_pos
,tmp_pos
+pane
->preedit_length
);
770 pane
->preedit_length
= strlen (preedit_string
);
772 if (pane
->preedit_length
)
773 gtk_editable_insert_text (editable
, preedit_string
, pane
->preedit_length
, &tmp_pos
);
774 g_free (preedit_string
);
778 cb_gnm_pane_preedit_end (GtkIMContext
*context
, GnmPane
*pane
)
780 pane
->im_preedit_started
= FALSE
;
784 cb_gnm_pane_retrieve_surrounding (GtkIMContext
*context
, GnmPane
*pane
)
786 GtkEditable
*editable
= gnm_pane_get_editable (pane
);
787 gchar
*surrounding
= gtk_editable_get_chars (editable
, 0, -1);
788 gint cur_pos
= gtk_editable_get_position (editable
);
790 gtk_im_context_set_surrounding (context
,
791 surrounding
, strlen (surrounding
),
792 g_utf8_offset_to_pointer (surrounding
, cur_pos
) - surrounding
);
794 g_free (surrounding
);
799 cb_gnm_pane_delete_surrounding (GtkIMContext
*context
,
804 GtkEditable
*editable
= gnm_pane_get_editable (pane
);
805 gint cur_pos
= gtk_editable_get_position (editable
);
806 gtk_editable_delete_text (editable
,
808 cur_pos
+ offset
+ n_chars
);
813 /* create views for the sheet objects now that we exist */
815 cb_pane_init_objs (GnmPane
*pane
)
817 Sheet
*sheet
= scg_sheet (pane
->simple
.scg
);
821 /* List is stored in reverse stacking order. Top of stack is
822 * first. On creation new foocanvas item get added to
823 * the front, so we need to create the views in reverse order */
824 list
= g_slist_reverse (g_slist_copy (sheet
->sheet_objects
));
825 for (ptr
= list
; ptr
!= NULL
; ptr
= ptr
->next
)
826 sheet_object_new_view (ptr
->data
,
827 (SheetObjectViewContainer
*)pane
);
833 cb_ctrl_pts_free (GocItem
**ctrl_pts
)
837 if (ctrl_pts
[i
] != NULL
)
838 g_object_unref (ctrl_pts
[i
]);
843 gnm_pane_dispose (GObject
*obj
)
845 GnmPane
*pane
= GNM_PANE (obj
);
847 if (pane
->col
.canvas
!= NULL
) {
848 gtk_widget_destroy (GTK_WIDGET (pane
->col
.canvas
));
849 g_object_unref (pane
->col
.canvas
);
850 pane
->col
.canvas
= NULL
;
853 if (pane
->row
.canvas
!= NULL
) {
854 gtk_widget_destroy (GTK_WIDGET (pane
->row
.canvas
));
855 g_object_unref (pane
->row
.canvas
);
856 pane
->row
.canvas
= NULL
;
859 if (pane
->im_context
) {
860 GtkIMContext
*imc
= pane
->im_context
;
862 pane
->im_context
= NULL
;
863 g_signal_handlers_disconnect_by_func
864 (imc
, cb_gnm_pane_commit
, pane
);
865 g_signal_handlers_disconnect_by_func
866 (imc
, cb_gnm_pane_preedit_start
, pane
);
867 g_signal_handlers_disconnect_by_func
868 (imc
, cb_gnm_pane_preedit_changed
, pane
);
869 g_signal_handlers_disconnect_by_func
870 (imc
, cb_gnm_pane_preedit_end
, pane
);
871 g_signal_handlers_disconnect_by_func
872 (imc
, cb_gnm_pane_retrieve_surrounding
, pane
);
873 g_signal_handlers_disconnect_by_func
874 (imc
, cb_gnm_pane_delete_surrounding
, pane
);
875 gtk_im_context_set_client_window (imc
, NULL
);
876 g_object_unref (imc
);
879 g_slist_free (pane
->cursor
.animated
);
880 pane
->cursor
.animated
= NULL
;
881 g_slist_free_full (pane
->cursor
.expr_range
, g_object_unref
);
882 pane
->cursor
.expr_range
= NULL
;
884 g_clear_object (&pane
->mouse_cursor
);
885 gnm_pane_clear_obj_size_tip (pane
);
887 if (pane
->drag
.ctrl_pts
) {
888 g_hash_table_destroy (pane
->drag
.ctrl_pts
);
889 pane
->drag
.ctrl_pts
= NULL
;
892 /* Be anal just in case we somehow manage to remove a pane
896 pane
->cursor
.std
= pane
->cursor
.rangesel
= pane
->cursor
.special
= NULL
;
897 pane
->size_guide
.guide
= NULL
;
898 pane
->size_guide
.start
= NULL
;
899 pane
->size_guide
.points
= NULL
;
901 G_OBJECT_CLASS (parent_klass
)->dispose (obj
);
905 gnm_pane_init (GnmPane
*pane
)
907 GocCanvas
*canvas
= GOC_CANVAS (pane
);
908 GocGroup
*root_group
= goc_canvas_get_root (canvas
);
910 pane
->grid_items
= goc_group_new (root_group
);
911 pane
->object_views
= goc_group_new (root_group
);
912 pane
->action_items
= goc_group_new (root_group
);
914 pane
->first
.col
= pane
->last_full
.col
= pane
->last_visible
.col
= 0;
915 pane
->first
.row
= pane
->last_full
.row
= pane
->last_visible
.row
= 0;
916 pane
->first_offset
.x
= 0;
917 pane
->first_offset
.y
= 0;
920 pane
->mouse_cursor
= NULL
;
921 pane
->cursor
.rangesel
= NULL
;
922 pane
->cursor
.special
= NULL
;
923 pane
->cursor
.expr_range
= NULL
;
924 pane
->cursor
.animated
= NULL
;
925 pane
->size_tip
= NULL
;
927 pane
->slide_handler
= NULL
;
928 pane
->slide_data
= NULL
;
929 pane
->sliding_timer
= 0;
930 pane
->sliding_x
= pane
->sliding_dx
= -1;
931 pane
->sliding_y
= pane
->sliding_dy
= -1;
932 pane
->sliding_adjacent_h
= pane
->sliding_adjacent_v
= FALSE
;
934 pane
->drag
.button
= 0;
935 pane
->drag
.ctrl_pts
= g_hash_table_new_full (g_direct_hash
, g_direct_equal
,
936 NULL
, (GDestroyNotify
) cb_ctrl_pts_free
);
938 pane
->im_context
= gtk_im_multicontext_new ();
939 pane
->preedit_length
= 0;
940 pane
->preedit_attrs
= NULL
;
941 pane
->im_preedit_started
= FALSE
;
943 gtk_widget_set_can_focus (GTK_WIDGET (canvas
), TRUE
);
944 gtk_widget_set_can_default (GTK_WIDGET (canvas
), TRUE
);
946 g_signal_connect (G_OBJECT (pane
->im_context
), "commit",
947 G_CALLBACK (cb_gnm_pane_commit
), pane
);
948 g_signal_connect (G_OBJECT (pane
->im_context
), "preedit_start",
949 G_CALLBACK (cb_gnm_pane_preedit_start
), pane
);
950 g_signal_connect (G_OBJECT (pane
->im_context
), "preedit_changed",
951 G_CALLBACK (cb_gnm_pane_preedit_changed
), pane
);
952 g_signal_connect (G_OBJECT (pane
->im_context
), "preedit_end",
953 G_CALLBACK (cb_gnm_pane_preedit_end
), pane
);
954 g_signal_connect (G_OBJECT (pane
->im_context
), "retrieve_surrounding",
955 G_CALLBACK (cb_gnm_pane_retrieve_surrounding
),
957 g_signal_connect (G_OBJECT (pane
->im_context
), "delete_surrounding",
958 G_CALLBACK (cb_gnm_pane_delete_surrounding
),
963 gnm_pane_class_init (GnmPaneClass
*klass
)
965 GObjectClass
*gobject_class
= G_OBJECT_CLASS (klass
);
966 GtkWidgetClass
*widget_class
= (GtkWidgetClass
*) klass
;
968 parent_klass
= g_type_class_peek_parent (klass
);
970 gobject_class
->dispose
= gnm_pane_dispose
;
972 widget_class
->realize
= gnm_pane_realize
;
973 widget_class
->unrealize
= gnm_pane_unrealize
;
974 widget_class
->size_allocate
= gnm_pane_size_allocate
;
975 widget_class
->key_press_event
= gnm_pane_key_press
;
976 widget_class
->key_release_event
= gnm_pane_key_release
;
977 widget_class
->focus_in_event
= gnm_pane_focus_in
;
978 widget_class
->focus_out_event
= gnm_pane_focus_out
;
980 gtk_widget_class_install_style_property
982 g_param_spec_int ("function-indicator-size",
983 P_("Function Indicator Size"),
984 P_("Size of function indicator"),
990 gtk_widget_class_install_style_property
992 g_param_spec_int ("comment-indicator-size",
993 P_("comment Indicator Size"),
994 P_("Size of comment indicator"),
1000 gtk_widget_class_install_style_property
1002 g_param_spec_int ("resize-guide-width",
1003 P_("Resize Guide Width"),
1004 P_("With of the guides used for resizing columns and rows"),
1010 gtk_widget_class_install_style_property
1012 g_param_spec_int ("pane-resize-guide-width",
1013 P_("Pane Resize Guide Width"),
1014 P_("With of the guides used for resizing panes"),
1020 gtk_widget_class_install_style_property
1022 g_param_spec_int ("control-circle-size",
1023 P_("Control Circle Size"),
1024 P_("Size of control circle for sizing sheet objects"),
1030 gtk_widget_class_install_style_property
1032 g_param_spec_int ("control-circle-outline",
1033 P_("Control Circle Outline"),
1034 P_("Width of outline of control circle for sizing sheet objects"),
1041 GSF_CLASS (GnmPane
, gnm_pane
,
1042 gnm_pane_class_init
, gnm_pane_init
,
1043 GNM_SIMPLE_CANVAS_TYPE
)
1049 gnm_pane_header_init (GnmPane
*pane
, SheetControlGUI
*scg
,
1050 gboolean is_col_header
)
1052 Sheet
*sheet
= scg_sheet (scg
);
1053 GocCanvas
*canvas
= gnm_simple_canvas_new (scg
);
1054 GocGroup
*group
= goc_canvas_get_root (canvas
);
1055 GocItem
*item
= goc_item_new (group
,
1056 gnm_item_bar_get_type (),
1058 "IsColHeader", is_col_header
,
1061 /* give a non-constraining default in case something scrolls before we
1063 if (is_col_header
) {
1064 if (sheet
&& sheet
->text_is_rtl
)
1065 goc_canvas_set_direction (canvas
, GOC_DIRECTION_RTL
);
1066 pane
->col
.canvas
= g_object_ref_sink (canvas
);
1067 pane
->col
.item
= GNM_ITEM_BAR (item
);
1069 pane
->row
.canvas
= g_object_ref_sink (canvas
);
1070 pane
->row
.item
= GNM_ITEM_BAR (item
);
1073 pane
->size_guide
.points
= NULL
;
1074 pane
->size_guide
.start
= NULL
;
1075 pane
->size_guide
.guide
= NULL
;
1079 fabs (1. - sheet
->last_zoom_factor_used
) > 1e-6)
1080 goc_canvas_set_pixels_per_unit (canvas
, sheet
->last_zoom_factor_used
);
1084 cb_pane_drag_data_received (GtkWidget
*widget
, GdkDragContext
*context
,
1085 gint x
, gint y
, GtkSelectionData
*selection_data
,
1086 guint info
, guint time
, GnmPane
*pane
)
1090 if (gnm_debug_flag ("dnd")) {
1091 gchar
*target_name
= gdk_atom_name (gtk_selection_data_get_target (selection_data
));
1092 g_printerr ("drag-data-received - %s\n", target_name
);
1093 g_free (target_name
);
1096 goc_canvas_w2c (GOC_CANVAS (pane
), x
, y
, &wx
, &wy
);
1097 scg_drag_data_received (pane
->simple
.scg
,
1098 gtk_drag_get_source_widget (context
),
1099 wx
, wy
, selection_data
);
1103 cb_pane_drag_data_get (GtkWidget
*widget
, GdkDragContext
*context
,
1104 GtkSelectionData
*selection_data
,
1105 guint info
, guint time
,
1106 SheetControlGUI
*scg
)
1108 if (gnm_debug_flag ("dnd")) {
1109 gchar
*target_name
= gdk_atom_name (gtk_selection_data_get_target (selection_data
));
1110 g_printerr ("drag-data-get - %s \n", target_name
);
1111 g_free (target_name
);
1114 scg_drag_data_get (scg
, selection_data
);
1117 /* Move the rubber bands if we are the source */
1119 cb_pane_drag_motion (GtkWidget
*widget
, GdkDragContext
*context
,
1120 int x
, int y
, guint32 time
, GnmPane
*pane
)
1122 GtkWidget
*source_widget
= gtk_drag_get_source_widget (context
);
1123 SheetControlGUI
*scg
= GNM_PANE (widget
)->simple
.scg
;
1125 if ((GNM_IS_PANE (source_widget
) &&
1126 GNM_PANE (source_widget
)->simple
.scg
== scg
)) {
1128 GocCanvas
*canvas
= GOC_CANVAS (widget
);
1129 GdkModifierType mask
;
1130 GdkWindow
*window
= gtk_widget_get_parent_window (source_widget
);
1133 g_object_set_data (G_OBJECT (context
),
1134 "wbcg", scg_wbcg (scg
));
1135 goc_canvas_w2c (canvas
, x
, y
, &wx
, &wy
);
1136 wx
*= goc_canvas_get_pixels_per_unit (canvas
);
1137 wy
*= goc_canvas_get_pixels_per_unit (canvas
);
1139 gdk_window_get_device_position (window
,
1140 gdk_device_manager_get_client_pointer (gdk_display_get_device_manager (gdk_window_get_display (window
))),
1142 gnm_pane_objects_drag (GNM_PANE (source_widget
), NULL
,
1143 wx
, wy
, 8, FALSE
, (mask
& GDK_SHIFT_MASK
) != 0);
1144 gdk_drag_status (context
,
1145 (mask
& GDK_CONTROL_MASK
) != 0 ? GDK_ACTION_COPY
: GDK_ACTION_MOVE
,
1152 cb_pane_drag_end (GtkWidget
*widget
, GdkDragContext
*context
,
1153 GnmPane
*source_pane
)
1155 /* ungrab any grabbed item */
1156 GocItem
*item
= goc_canvas_get_grabbed_item (GOC_CANVAS (source_pane
));
1158 gnm_simple_canvas_ungrab (item
);
1159 /* sync the ctrl-pts with the object in case the drag was canceled. */
1160 gnm_pane_objects_drag (source_pane
, NULL
,
1161 source_pane
->drag
.origin_x
,
1162 source_pane
->drag
.origin_y
,
1164 source_pane
->drag
.had_motion
= FALSE
;
1165 source_pane
->drag
.button
= 0;
1169 * Move the rubber bands back to original position when curser leaves
1170 * the scg, but not when it moves to another pane. We use object data,
1171 * and rely on gtk sending drag_move to the new widget before sending
1172 * drag_leave to the old one.
1175 cb_pane_drag_leave (GtkWidget
*widget
, GdkDragContext
*context
,
1176 guint32 time
, GnmPane
*pane
)
1178 GtkWidget
*source_widget
= gtk_drag_get_source_widget (context
);
1179 GnmPane
*source_pane
;
1182 if (!source_widget
|| !GNM_IS_PANE (source_widget
)) return;
1184 source_pane
= GNM_PANE (source_widget
);
1186 wbcg
= scg_wbcg (source_pane
->simple
.scg
);
1187 if (wbcg
== g_object_get_data (G_OBJECT (context
), "wbcg"))
1190 gnm_pane_objects_drag (source_pane
, NULL
,
1191 source_pane
->drag
.origin_x
,
1192 source_pane
->drag
.origin_y
,
1194 source_pane
->drag
.had_motion
= FALSE
;
1198 gnm_pane_drag_dest_init (GnmPane
*pane
, SheetControlGUI
*scg
)
1200 GtkWidget
*widget
= GTK_WIDGET (pane
);
1202 gtk_drag_dest_set (widget
, GTK_DEST_DEFAULT_ALL
,
1203 drag_types_in
, G_N_ELEMENTS (drag_types_in
),
1204 GDK_ACTION_COPY
| GDK_ACTION_MOVE
);
1205 gtk_drag_dest_add_uri_targets (widget
);
1206 gtk_drag_dest_add_image_targets (widget
);
1207 gtk_drag_dest_add_text_targets (widget
);
1209 g_object_connect (G_OBJECT (widget
),
1210 "signal::drag-data-received", G_CALLBACK (cb_pane_drag_data_received
), pane
,
1211 "signal::drag-data-get", G_CALLBACK (cb_pane_drag_data_get
), scg
,
1212 "signal::drag-motion", G_CALLBACK (cb_pane_drag_motion
), pane
,
1213 "signal::drag-leave", G_CALLBACK (cb_pane_drag_leave
), pane
,
1214 "signal::drag-end", G_CALLBACK (cb_pane_drag_end
), pane
,
1219 gnm_pane_new (SheetControlGUI
*scg
,
1220 gboolean col_headers
, gboolean row_headers
, int index
)
1226 g_return_val_if_fail (GNM_IS_SCG (scg
), NULL
);
1228 pane
= g_object_new (GNM_PANE_TYPE
, NULL
);
1229 pane
->index
= index
;
1230 pane
->simple
.scg
= scg
;
1232 goc_canvas_set_document (GOC_CANVAS (pane
), wb_control_get_doc (scg_wbc (scg
)));
1233 if (NULL
!= (sheet
= scg_sheet (scg
)) &&
1234 fabs (1. - sheet
->last_zoom_factor_used
) > 1e-6)
1235 goc_canvas_set_pixels_per_unit (GOC_CANVAS (pane
),
1236 sheet
->last_zoom_factor_used
);
1238 gnm_pane_drag_dest_init (pane
, scg
);
1240 item
= goc_item_new (pane
->grid_items
,
1241 gnm_item_grid_get_type (),
1242 "SheetControlGUI", scg
,
1244 pane
->grid
= GNM_ITEM_GRID (item
);
1246 item
= goc_item_new (pane
->grid_items
,
1247 gnm_item_cursor_get_type (),
1248 "SheetControlGUI", scg
,
1250 pane
->cursor
.std
= GNM_ITEM_CURSOR (item
);
1252 gnm_pane_header_init (pane
, scg
, TRUE
);
1254 pane
->col
.canvas
= NULL
;
1256 gnm_pane_header_init (pane
, scg
, FALSE
);
1258 pane
->row
.canvas
= NULL
;
1260 g_signal_connect_swapped (pane
, "popup-menu",
1261 G_CALLBACK (cb_pane_popup_menu
), pane
);
1262 g_signal_connect_swapped (G_OBJECT (pane
), "realize",
1263 G_CALLBACK (cb_pane_init_objs
), pane
);
1269 * gnm_pane_find_col:
1271 * @x: In canvas coords
1272 * @col_origin: optionally return the canvas coord of the col
1274 * Returns the column containing canvas coord @x
1277 gnm_pane_find_col (GnmPane
const *pane
, gint64 x
, gint64
*col_origin
)
1279 Sheet
const *sheet
= scg_sheet (pane
->simple
.scg
);
1280 int col
= pane
->first
.col
;
1281 gint64 pixel
= pane
->first_offset
.x
;
1285 ColRowInfo
const *ci
= sheet_col_get_info (sheet
, --col
);
1287 pixel
-= ci
->size_pixels
;
1290 *col_origin
= pixel
;
1301 ColRowInfo
const *ci
= sheet_col_get_info (sheet
, col
);
1303 int const tmp
= ci
->size_pixels
;
1304 if (x
<= pixel
+ tmp
) {
1306 *col_origin
= pixel
;
1311 } while (++col
< gnm_sheet_get_last_col (sheet
));
1314 *col_origin
= pixel
;
1315 return gnm_sheet_get_last_col (sheet
);
1319 * gnm_pane_find_row:
1321 * @y: In canvas coords
1322 * @row_origin: optionally return the canvas coord of the row
1324 * Returns the column containing canvas coord @y
1327 gnm_pane_find_row (GnmPane
const *pane
, gint64 y
, gint64
*row_origin
)
1329 Sheet
const *sheet
= scg_sheet (pane
->simple
.scg
);
1330 int row
= pane
->first
.row
;
1331 gint64 pixel
= pane
->first_offset
.y
;
1335 ColRowInfo
const *ri
= sheet_row_get_info (sheet
, --row
);
1337 pixel
-= ri
->size_pixels
;
1340 *row_origin
= pixel
;
1351 ColRowInfo
const *ri
= sheet_row_get_info (sheet
, row
);
1353 int const tmp
= ri
->size_pixels
;
1354 if (pixel
<= y
&& y
<= pixel
+ tmp
) {
1356 *row_origin
= pixel
;
1361 } while (++row
< gnm_sheet_get_last_row (sheet
));
1363 *row_origin
= pixel
;
1364 return gnm_sheet_get_last_row (sheet
);
1368 * gnm_pane_compute_visible_region : Keeps the top left col/row the same and
1369 * recalculates the visible boundaries.
1372 * if TRUE recompute the pixel offsets of the top left row/col
1373 * else assumes that the pixel offsets of the top left have not changed.
1376 gnm_pane_compute_visible_region (GnmPane
*pane
,
1377 gboolean
const full_recompute
)
1379 SheetControlGUI
const * const scg
= pane
->simple
.scg
;
1380 Sheet
const *sheet
= scg_sheet (scg
);
1381 GocCanvas
*canvas
= GOC_CANVAS (pane
);
1383 int col
, row
, width
, height
;
1386 gtk_widget_get_allocation (GTK_WIDGET (canvas
), &ca
);
1388 /* When col/row sizes change we need to do a full recompute */
1389 if (full_recompute
) {
1390 gint64 col_offset
= pane
->first_offset
.x
= scg_colrow_distance_get (scg
,
1391 TRUE
, 0, pane
->first
.col
);
1392 if (NULL
!= pane
->col
.canvas
)
1393 goc_canvas_scroll_to (pane
->col
.canvas
, col_offset
/ canvas
->pixels_per_unit
, 0);
1395 pane
->first_offset
.y
= scg_colrow_distance_get (scg
,
1396 FALSE
, 0, pane
->first
.row
);
1397 if (NULL
!= pane
->row
.canvas
)
1398 goc_canvas_scroll_to (pane
->row
.canvas
,
1399 0, pane
->first_offset
.y
/ canvas
->pixels_per_unit
);
1401 goc_canvas_scroll_to (GOC_CANVAS (pane
),
1402 col_offset
/ canvas
->pixels_per_unit
, pane
->first_offset
.y
/ canvas
->pixels_per_unit
);
1405 /* Find out the last visible col and the last full visible column */
1407 col
= pane
->first
.col
;
1411 ColRowInfo
const * const ci
= sheet_col_get_info (sheet
, col
);
1413 int const bound
= pixels
+ ci
->size_pixels
;
1415 if (bound
== width
) {
1416 pane
->last_visible
.col
= col
;
1417 pane
->last_full
.col
= col
;
1420 if (bound
> width
) {
1421 pane
->last_visible
.col
= col
;
1422 if (col
== pane
->first
.col
)
1423 pane
->last_full
.col
= pane
->first
.col
;
1425 pane
->last_full
.col
= col
- 1;
1431 } while (pixels
< width
&& col
< gnm_sheet_get_max_cols (sheet
));
1433 if (col
>= gnm_sheet_get_max_cols (sheet
)) {
1434 pane
->last_visible
.col
= gnm_sheet_get_last_col (sheet
);
1435 pane
->last_full
.col
= gnm_sheet_get_last_col (sheet
);
1438 /* Find out the last visible row and the last fully visible row */
1440 row
= pane
->first
.row
;
1443 ColRowInfo
const * const ri
= sheet_row_get_info (sheet
, row
);
1445 int const bound
= pixels
+ ri
->size_pixels
;
1447 if (bound
== height
) {
1448 pane
->last_visible
.row
= row
;
1449 pane
->last_full
.row
= row
;
1452 if (bound
> height
) {
1453 pane
->last_visible
.row
= row
;
1454 if (row
== pane
->first
.row
)
1455 pane
->last_full
.row
= pane
->first
.row
;
1457 pane
->last_full
.row
= row
- 1;
1463 } while (pixels
< height
&& row
< gnm_sheet_get_max_rows (sheet
));
1465 if (row
>= gnm_sheet_get_max_rows (sheet
)) {
1466 pane
->last_visible
.row
= gnm_sheet_get_last_row (sheet
);
1467 pane
->last_full
.row
= gnm_sheet_get_last_row (sheet
);
1470 /* Update the scrollbar sizes for the primary pane */
1471 if (pane
->index
== 0)
1472 sc_scrollbar_config (GNM_SHEET_CONTROL (scg
));
1474 /* Force the cursor to update its bounds relative to the new visible region */
1475 gnm_pane_reposition_cursors (pane
);
1479 gnm_pane_redraw_range (GnmPane
*pane
, GnmRange
const *r
)
1481 SheetControlGUI
*scg
;
1482 gint64 x1
, y1
, x2
, y2
;
1485 double scale
= goc_canvas_get_pixels_per_unit (GOC_CANVAS (pane
));
1487 g_return_if_fail (GNM_IS_PANE (pane
));
1489 scg
= pane
->simple
.scg
;
1490 sheet
= scg_sheet (scg
);
1492 if ((r
->end
.col
< pane
->first
.col
) ||
1493 (r
->end
.row
< pane
->first
.row
) ||
1494 (r
->start
.col
> pane
->last_visible
.col
) ||
1495 (r
->start
.row
> pane
->last_visible
.row
))
1498 /* Only draw those regions that are visible */
1499 tmp
.start
.col
= MAX (pane
->first
.col
, r
->start
.col
);
1500 tmp
.start
.row
= MAX (pane
->first
.row
, r
->start
.row
);
1501 tmp
.end
.col
= MIN (pane
->last_visible
.col
, r
->end
.col
);
1502 tmp
.end
.row
= MIN (pane
->last_visible
.row
, r
->end
.row
);
1504 /* redraw a border of 2 pixels around the region to handle thick borders
1505 * NOTE the 2nd coordinates are excluded so add 1 extra (+2border +1include)
1507 x1
= scg_colrow_distance_get (scg
, TRUE
, pane
->first
.col
, tmp
.start
.col
) +
1508 pane
->first_offset
.x
;
1509 y1
= scg_colrow_distance_get (scg
, FALSE
, pane
->first
.row
, tmp
.start
.row
) +
1510 pane
->first_offset
.y
;
1511 x2
= (tmp
.end
.col
< gnm_sheet_get_last_col (sheet
))
1512 ? 4 + 1 + x1
+ scg_colrow_distance_get (scg
, TRUE
,
1513 tmp
.start
.col
, tmp
.end
.col
+1)
1515 y2
= (tmp
.end
.row
< gnm_sheet_get_last_row (sheet
))
1516 ? 4 + 1 + y1
+ scg_colrow_distance_get (scg
, FALSE
,
1517 tmp
.start
.row
, tmp
.end
.row
+1)
1520 goc_canvas_invalidate (&pane
->simple
.canvas
, (x1
-2) / scale
, (y1
-2) / scale
, x2
/ scale
, y2
/ scale
);
1523 /*****************************************************************************/
1526 gnm_pane_slide_stop (GnmPane
*pane
)
1528 if (pane
->sliding_timer
== 0)
1531 g_source_remove (pane
->sliding_timer
);
1532 pane
->slide_handler
= NULL
;
1533 pane
->slide_data
= NULL
;
1534 pane
->sliding_timer
= 0;
1538 col_scroll_step (int dx
, Sheet
*sheet
)
1540 /* FIXME: get from gdk. */
1541 int dpi_x_this_screen
= 90;
1542 int start_x
= dpi_x_this_screen
/ 3;
1543 double double_dx
= dpi_x_this_screen
/ 3.0;
1544 double step
= pow (2.0, (dx
- start_x
) / double_dx
);
1546 return (int) (CLAMP (step
, 1.0, gnm_sheet_get_max_cols (sheet
) / 15.0));
1550 row_scroll_step (int dy
, Sheet
*sheet
)
1552 /* FIXME: get from gdk. */
1553 int dpi_y_this_screen
= 90;
1554 int start_y
= dpi_y_this_screen
/ 4;
1555 double double_dy
= dpi_y_this_screen
/ 8.0;
1556 double step
= pow (2.0, (dy
- start_y
) / double_dy
);
1558 return (int) (CLAMP (step
, 1.0, gnm_sheet_get_max_rows (sheet
) / 15.0));
1562 cb_pane_sliding (GnmPane
*pane
)
1564 int const pane_index
= pane
->index
;
1565 GnmPane
*pane0
= scg_pane (pane
->simple
.scg
, 0);
1566 GnmPane
*pane1
= scg_pane (pane
->simple
.scg
, 1);
1567 GnmPane
*pane3
= scg_pane (pane
->simple
.scg
, 3);
1568 gboolean slide_x
= FALSE
, slide_y
= FALSE
;
1569 int col
= -1, row
= -1;
1570 Sheet
*sheet
= scg_sheet (pane
->simple
.scg
);
1571 GnmPaneSlideInfo info
;
1574 gtk_widget_get_allocation (GTK_WIDGET (pane
), &pa
);
1576 if (pane
->sliding_dx
> 0) {
1577 GnmPane
*target_pane
= pane
;
1580 if (pane_index
== 1 || pane_index
== 2) {
1581 if (!pane
->sliding_adjacent_h
) {
1582 int width
= pa
.width
;
1583 int x
= pane
->first_offset
.x
+ width
+ pane
->sliding_dx
;
1585 /* in case pane is narrow */
1586 col
= gnm_pane_find_col (pane
, x
, NULL
);
1587 if (col
> pane0
->last_full
.col
) {
1588 pane
->sliding_adjacent_h
= TRUE
;
1589 pane
->sliding_dx
= 1; /* good enough */
1593 target_pane
= pane0
;
1595 pane
->sliding_adjacent_h
= FALSE
;
1598 col
= target_pane
->last_full
.col
+
1599 col_scroll_step (pane
->sliding_dx
, sheet
);
1600 if (col
>= gnm_sheet_get_last_col (sheet
)) {
1601 col
= gnm_sheet_get_last_col (sheet
);
1605 } else if (pane
->sliding_dx
< 0) {
1607 col
= pane0
->first
.col
- col_scroll_step (-pane
->sliding_dx
, sheet
);
1609 if (pane1
!= NULL
) {
1610 if (pane_index
== 0 || pane_index
== 3) {
1614 gtk_widget_get_allocation (GTK_WIDGET (pane1
),
1618 if (pane
->sliding_dx
> (-width
) &&
1619 col
<= pane1
->last_visible
.col
) {
1620 int x
= pane1
->first_offset
.x
+ width
+ pane
->sliding_dx
;
1621 col
= gnm_pane_find_col (pane
, x
, NULL
);
1626 if (col
<= pane1
->first
.col
) {
1627 col
= pane1
->first
.col
;
1630 } else if (col
<= 0) {
1636 if (pane
->sliding_dy
> 0) {
1637 GnmPane
*target_pane
= pane
;
1640 if (pane_index
== 3 || pane_index
== 2) {
1641 if (!pane
->sliding_adjacent_v
) {
1642 int height
= pa
.height
;
1643 int y
= pane
->first_offset
.y
+ height
+ pane
->sliding_dy
;
1645 /* in case pane is short */
1646 row
= gnm_pane_find_row (pane
, y
, NULL
);
1647 if (row
> pane0
->last_full
.row
) {
1648 pane
->sliding_adjacent_v
= TRUE
;
1649 pane
->sliding_dy
= 1; /* good enough */
1653 target_pane
= pane0
;
1655 pane
->sliding_adjacent_v
= FALSE
;
1658 row
= target_pane
->last_full
.row
+
1659 row_scroll_step (pane
->sliding_dy
, sheet
);
1660 if (row
>= gnm_sheet_get_last_row (sheet
)) {
1661 row
= gnm_sheet_get_last_row (sheet
);
1665 } else if (pane
->sliding_dy
< 0) {
1667 row
= pane0
->first
.row
- row_scroll_step (-pane
->sliding_dy
, sheet
);
1669 if (pane3
!= NULL
) {
1670 if (pane_index
== 0 || pane_index
== 1) {
1674 gtk_widget_get_allocation (GTK_WIDGET (pane3
),
1677 height
= p3a
.height
;
1678 if (pane
->sliding_dy
> (-height
) &&
1679 row
<= pane3
->last_visible
.row
) {
1680 int y
= pane3
->first_offset
.y
+ height
+ pane
->sliding_dy
;
1681 row
= gnm_pane_find_row (pane3
, y
, NULL
);
1686 if (row
<= pane3
->first
.row
) {
1687 row
= pane3
->first
.row
;
1690 } else if (row
<= 0) {
1696 if (col
< 0 && row
< 0) {
1697 gnm_pane_slide_stop (pane
);
1702 col
= gnm_pane_find_col (pane
, pane
->sliding_x
, NULL
);
1704 row
= gnm_pane_find_row (pane
, pane
->sliding_y
, NULL
);
1708 info
.user_data
= pane
->slide_data
;
1709 if (pane
->slide_handler
== NULL
||
1710 (*pane
->slide_handler
) (pane
, &info
))
1711 scg_make_cell_visible (pane
->simple
.scg
, col
, row
, FALSE
, TRUE
);
1713 if (!slide_x
&& !slide_y
)
1714 gnm_pane_slide_stop (pane
);
1715 else if (pane
->sliding_timer
== 0)
1716 pane
->sliding_timer
= g_timeout_add (300, (GSourceFunc
)cb_pane_sliding
, pane
);
1722 * gnm_pane_handle_motion:
1723 * @pane: The GnmPane managing the scroll
1724 * @canvas: The Canvas the event comes from
1726 * @handler: (scope async): The handler when sliding
1727 * @user_data: closure data
1729 * Handle a motion event from a @canvas and scroll the @pane
1730 * depending on how far outside the bounds of @pane the @event is.
1731 * Usually @canvas == @pane however as long as the canvases share a basis
1732 * space they can be different.
1735 gnm_pane_handle_motion (GnmPane
*pane
,
1736 GocCanvas
*canvas
, gint64 x
, gint64 y
,
1737 GnmPaneSlideFlags slide_flags
,
1738 GnmPaneSlideHandler slide_handler
,
1741 GnmPane
*pane0
, *pane1
, *pane3
;
1742 int pindex
, width
, height
;
1743 gint64 dx
= 0, dy
= 0, left
, top
;
1744 GtkAllocation pa
, p0a
, p1a
, p3a
;
1746 g_return_val_if_fail (GNM_IS_PANE (pane
), FALSE
);
1747 g_return_val_if_fail (GOC_IS_CANVAS (canvas
), FALSE
);
1748 g_return_val_if_fail (slide_handler
!= NULL
, FALSE
);
1750 pindex
= pane
->index
;
1751 left
= pane
->first_offset
.x
;
1752 top
= pane
->first_offset
.y
;
1753 gtk_widget_get_allocation (GTK_WIDGET (pane
), &pa
);
1757 pane0
= scg_pane (pane
->simple
.scg
, 0);
1758 gtk_widget_get_allocation (GTK_WIDGET (pane0
), &p0a
);
1760 pane1
= scg_pane (pane
->simple
.scg
, 1);
1761 if (pane1
) gtk_widget_get_allocation (GTK_WIDGET (pane1
), &p1a
);
1763 pane3
= scg_pane (pane
->simple
.scg
, 3);
1764 if (pane3
) gtk_widget_get_allocation (GTK_WIDGET (pane3
), &p3a
);
1766 if (slide_flags
& GNM_PANE_SLIDE_X
) {
1769 else if (x
>= left
+ width
)
1770 dx
= x
- width
- left
;
1773 if (slide_flags
& GNM_PANE_SLIDE_Y
) {
1776 else if (y
>= top
+ height
)
1777 dy
= y
- height
- top
;
1780 if (pane
->sliding_adjacent_h
) {
1781 if (pindex
== 0 || pindex
== 3) {
1783 x
= pane1
->first_offset
.x
;
1789 pane
->sliding_adjacent_h
= FALSE
;
1792 x
= pane0
->first_offset
.x
+ dx
;
1796 } else if (dx
== 0) {
1797 /* initiate a reverse scroll of panes 0,3 */
1798 if ((pane1
->last_visible
.col
+1) != pane0
->first
.col
)
1799 dx
= x
- (left
+ width
);
1805 if (pane
->sliding_adjacent_v
) {
1806 if (pindex
== 0 || pindex
== 1) {
1808 y
= pane3
->first_offset
.y
;
1814 pane
->sliding_adjacent_v
= FALSE
;
1817 y
= pane0
->first_offset
.y
+ dy
;
1821 } else if (dy
== 0) {
1822 /* initiate a reverse scroll of panes 0,1 */
1823 if ((pane3
->last_visible
.row
+1) != pane0
->first
.row
)
1824 dy
= y
- (top
+ height
);
1830 /* Movement is inside the visible region */
1831 if (dx
== 0 && dy
== 0) {
1832 if (!(slide_flags
& GNM_PANE_SLIDE_EXTERIOR_ONLY
)) {
1833 GnmPaneSlideInfo info
;
1834 info
.row
= gnm_pane_find_row (pane
, y
, NULL
);
1835 info
.col
= gnm_pane_find_col (pane
, x
, NULL
);
1836 info
.user_data
= user_data
;
1837 (*slide_handler
) (pane
, &info
);
1839 gnm_pane_slide_stop (pane
);
1843 pane
->sliding_x
= x
;
1844 pane
->sliding_dx
= dx
;
1845 pane
->sliding_y
= y
;
1846 pane
->sliding_dy
= dy
;
1847 pane
->slide_handler
= slide_handler
;
1848 pane
->slide_data
= user_data
;
1850 if (pane
->sliding_timer
== 0)
1851 cb_pane_sliding (pane
);
1855 /* TODO : All the slide_* members of GnmPane really ought to be in
1856 * SheetControlGUI, most of these routines also belong there. However, since
1857 * the primary point of access is via GnmPane and SCG is very large
1858 * already I'm leaving them here for now. Move them when we return to
1859 * investigate how to do reverse scrolling for pseudo-adjacent panes.
1862 gnm_pane_slide_init (GnmPane
*pane
)
1864 GnmPane
*pane0
, *pane1
, *pane3
;
1866 g_return_if_fail (GNM_IS_PANE (pane
));
1868 pane0
= scg_pane (pane
->simple
.scg
, 0);
1869 pane1
= scg_pane (pane
->simple
.scg
, 1);
1870 pane3
= scg_pane (pane
->simple
.scg
, 3);
1872 pane
->sliding_adjacent_h
= (pane1
!= NULL
)
1873 ? (pane1
->last_full
.col
== (pane0
->first
.col
- 1))
1875 pane
->sliding_adjacent_v
= (pane3
!= NULL
)
1876 ? (pane3
->last_full
.row
== (pane0
->first
.row
- 1))
1881 cb_obj_autoscroll (GnmPane
*pane
, GnmPaneSlideInfo
const *info
)
1883 SheetControlGUI
*scg
= pane
->simple
.scg
;
1884 GdkModifierType mask
;
1885 GdkWindow
*window
= gtk_widget_get_parent_window (GTK_WIDGET (pane
));
1887 /* Cheesy hack calculate distance we move the screen, this loses the
1889 double dx
= pane
->first_offset
.x
;
1890 double dy
= pane
->first_offset
.y
;
1891 scg_make_cell_visible (scg
, info
->col
, info
->row
, FALSE
, TRUE
);
1892 dx
= pane
->first_offset
.x
- dx
;
1893 dy
= pane
->first_offset
.y
- dy
;
1895 pane
->drag
.had_motion
= TRUE
;
1896 gdk_window_get_device_position (window
,
1897 gdk_device_manager_get_client_pointer (gdk_display_get_device_manager (gdk_window_get_display (window
))),
1899 scg_objects_drag (pane
->simple
.scg
, pane
,
1900 NULL
, &dx
, &dy
, 8, FALSE
, (mask
& GDK_SHIFT_MASK
) != 0, TRUE
);
1902 pane
->drag
.last_x
+= dx
;
1903 pane
->drag
.last_y
+= dy
;
1908 gnm_pane_object_autoscroll (GnmPane
*pane
, GdkDragContext
*context
,
1909 gint x
, gint y
, guint time
)
1911 int const pane_index
= pane
->index
;
1912 SheetControlGUI
*scg
= pane
->simple
.scg
;
1913 GnmPane
*pane0
= scg_pane (scg
, 0);
1914 GnmPane
*pane1
= scg_pane (scg
, 1);
1915 GnmPane
*pane3
= scg_pane (scg
, 3);
1916 GtkWidget
*w
= GTK_WIDGET (pane
);
1920 gtk_widget_get_allocation (w
, &wa
);
1923 if (pane_index
< 2 && pane3
!= NULL
) {
1924 w
= GTK_WIDGET (pane3
);
1925 gtk_widget_get_allocation (w
, &wa
);
1928 g_return_if_fail (dy
<= 0);
1929 } else if (y
>= (wa
.y
+ wa
.height
)) {
1930 if (pane_index
>= 2) {
1931 w
= GTK_WIDGET (pane0
);
1932 gtk_widget_get_allocation (w
, &wa
);
1934 dy
= y
- (wa
.y
+ wa
.height
);
1935 g_return_if_fail (dy
>= 0);
1939 if ((pane_index
== 0 || pane_index
== 3) && pane1
!= NULL
) {
1940 w
= GTK_WIDGET (pane1
);
1941 gtk_widget_get_allocation (w
, &wa
);
1944 g_return_if_fail (dx
<= 0);
1945 } else if (x
>= (wa
.x
+ wa
.width
)) {
1946 if (pane_index
>= 2) {
1947 w
= GTK_WIDGET (pane0
);
1948 gtk_widget_get_allocation (w
, &wa
);
1950 dx
= x
- (wa
.x
+ wa
.width
);
1951 g_return_if_fail (dx
>= 0);
1955 g_object_set_data (G_OBJECT (context
),
1956 "wbcg", scg_wbcg (scg
));
1957 pane
->sliding_dx
= dx
;
1958 pane
->sliding_dy
= dy
;
1959 pane
->slide_handler
= &cb_obj_autoscroll
;
1960 pane
->slide_data
= NULL
;
1961 pane
->sliding_x
= x
;
1962 pane
->sliding_y
= y
;
1963 if (pane
->sliding_timer
== 0)
1964 cb_pane_sliding (pane
);
1968 * gnm_pane_object_group:
1971 * Returns: (transfer none): the #GocGroup including all #SheetObjectView
1972 * instances in @pane.
1975 gnm_pane_object_group (GnmPane
*pane
)
1977 return pane
->object_views
;
1981 gnm_pane_clear_obj_size_tip (GnmPane
*pane
)
1983 if (pane
->size_tip
) {
1984 gtk_widget_destroy (gtk_widget_get_toplevel (pane
->size_tip
));
1985 pane
->size_tip
= NULL
;
1990 gnm_pane_display_obj_size_tip (GnmPane
*pane
, GocItem
*ctrl_pt
)
1992 SheetControlGUI
*scg
= pane
->simple
.scg
;
1993 double const *coords
;
1996 SheetObjectAnchor anchor
;
1998 if (pane
->size_tip
== NULL
) {
1999 GtkWidget
*cw
= GTK_WIDGET (pane
);
2003 if (ctrl_pt
== NULL
) {
2005 * Keyboard navigation when we are not displaying
2006 * a tooltip already.
2011 pane
->size_tip
= gnm_create_tooltip (cw
);
2012 top
= gtk_widget_get_toplevel (pane
->size_tip
);
2014 gnm_canvas_get_screen_position (ctrl_pt
->canvas
,
2015 ctrl_pt
->x1
, ctrl_pt
->y1
,
2017 gtk_window_move (GTK_WINDOW (top
), x
+ 10, y
+ 10);
2018 gtk_widget_show_all (top
);
2021 g_return_if_fail (pane
->cur_object
!= NULL
);
2022 g_return_if_fail (pane
->size_tip
!= NULL
);
2024 coords
= g_hash_table_lookup (scg
->selected_objects
, pane
->cur_object
);
2025 anchor
= *sheet_object_get_anchor (pane
->cur_object
);
2026 scg_object_coords_to_anchor (scg
, coords
, &anchor
);
2027 sheet_object_anchor_to_pts (&anchor
, scg_sheet (scg
), pts
);
2028 msg
= g_strdup_printf (_("%.1f x %.1f pts\n%d x %d pixels"),
2029 MAX (fabs (pts
[2] - pts
[0]), 0),
2030 MAX (fabs (pts
[3] - pts
[1]), 0),
2031 MAX ((int)floor (fabs (coords
[2] - coords
[0]) + 0.5), 0),
2032 MAX ((int)floor (fabs (coords
[3] - coords
[1]) + 0.5), 0));
2033 gtk_label_set_text (GTK_LABEL (pane
->size_tip
), msg
);
2038 gnm_pane_bound_set (GnmPane
*pane
,
2039 int start_col
, int start_row
,
2040 int end_col
, int end_row
)
2044 g_return_if_fail (pane
!= NULL
);
2046 range_init (&r
, start_col
, start_row
, end_col
, end_row
);
2047 goc_item_set (GOC_ITEM (pane
->grid
),
2052 /****************************************************************************/
2055 gnm_pane_size_guide_start (GnmPane
*pane
,
2056 gboolean vert
, int colrow
, gboolean is_colrow_resize
)
2058 SheetControlGUI
const *scg
;
2059 double x0
, y0
, x1
, y1
, pos
;
2063 GtkStyleContext
*context
;
2064 const char *guide_class
= is_colrow_resize
? "resize-guide" : "pane-resize-guide";
2065 const char *colrow_class
= vert
? "col" : "row";
2066 const char *width_prop_name
= is_colrow_resize
? "resize-guide-width" : "pane-resize-guide-width";
2069 g_return_if_fail (pane
!= NULL
);
2070 g_return_if_fail (pane
->size_guide
.guide
== NULL
);
2071 g_return_if_fail (pane
->size_guide
.start
== NULL
);
2072 g_return_if_fail (pane
->size_guide
.points
== NULL
);
2074 zoom
= GOC_CANVAS (pane
)->pixels_per_unit
;
2075 scg
= pane
->simple
.scg
;
2077 pos
= scg_colrow_distance_get (scg
, vert
, 0, colrow
) / zoom
;
2080 y0
= scg_colrow_distance_get (scg
, FALSE
,
2081 0, pane
->first
.row
) / zoom
;
2083 y1
= scg_colrow_distance_get (scg
, FALSE
,
2084 0, pane
->last_visible
.row
+1) / zoom
;
2086 x0
= scg_colrow_distance_get (scg
, TRUE
,
2087 0, pane
->first
.col
) / zoom
;
2089 x1
= scg_colrow_distance_get (scg
, TRUE
,
2090 0, pane
->last_visible
.col
+1) / zoom
;
2094 gtk_widget_style_get (GTK_WIDGET (pane
), width_prop_name
, &width
, NULL
);
2096 /* Guideline positioning is done in gnm_pane_size_guide_motion */
2097 pane
->size_guide
.guide
= goc_item_new (pane
->action_items
,
2102 style
= go_styled_object_get_style (GO_STYLED_OBJECT (pane
->size_guide
.guide
));
2103 style
->line
.width
= width
;
2104 context
= goc_item_get_style_context (pane
->size_guide
.guide
);
2105 gtk_style_context_add_class (context
, guide_class
);
2106 gtk_style_context_add_class (context
, colrow_class
);
2107 if (is_colrow_resize
)
2108 gtk_style_context_add_class (context
, "end");
2109 gnm_style_context_get_color (context
, GTK_STATE_FLAG_SELECTED
, &rgba
);
2110 go_color_from_gdk_rgba (&rgba
, &style
->line
.color
);
2112 if (is_colrow_resize
) {
2113 pane
->size_guide
.start
= goc_item_new (pane
->action_items
,
2118 style
= go_styled_object_get_style (GO_STYLED_OBJECT (pane
->size_guide
.start
));
2119 context
= goc_item_get_style_context (pane
->size_guide
.start
);
2120 gtk_style_context_add_class (context
, guide_class
);
2121 gtk_style_context_add_class (context
, colrow_class
);
2122 gtk_style_context_add_class (context
, "start");
2123 gnm_style_context_get_color (context
, GTK_STATE_FLAG_SELECTED
, &rgba
);
2124 go_color_from_gdk_rgba (&rgba
, &style
->line
.color
);
2125 style
->line
.width
= width
;
2130 gnm_pane_size_guide_stop (GnmPane
*pane
)
2132 g_return_if_fail (pane
!= NULL
);
2134 g_clear_object (&pane
->size_guide
.start
);
2135 g_clear_object (&pane
->size_guide
.guide
);
2139 * gnm_pane_size_guide_motion:
2141 * @vert: TRUE for a vertical guide, FALSE for horizontal
2142 * @guide_pos: in unscaled sheet pixel coords
2144 * Moves the guide line to @guide_pos.
2145 * NOTE : gnm_pane_size_guide_start must be called before any calls to
2146 * gnm_pane_size_guide_motion
2149 gnm_pane_size_guide_motion (GnmPane
*pane
, gboolean vert
, gint64 guide_pos
)
2151 GocItem
*resize_guide
= GOC_ITEM (pane
->size_guide
.guide
);
2152 double const scale
= 1. / resize_guide
->canvas
->pixels_per_unit
;
2155 x
= scale
* (guide_pos
- .5);
2157 goc_item_set (resize_guide
, "x0", x
, "x1", x
, NULL
);
2159 goc_item_set (resize_guide
, "y0", x
, "y1", x
, NULL
);
2162 /****************************************************************************/
2165 cb_update_ctrl_pts (SheetObject
*so
, GocItem
**ctrl_pts
, GnmPane
*pane
)
2167 double *coords
= g_hash_table_lookup (
2168 pane
->simple
.scg
->selected_objects
, so
);
2169 scg_object_anchor_to_coords (pane
->simple
.scg
, sheet_object_get_anchor (so
), coords
);
2170 gnm_pane_object_update_bbox (pane
, so
);
2173 /* Called when the zoom changes */
2175 gnm_pane_reposition_cursors (GnmPane
*pane
)
2179 gnm_item_cursor_reposition (pane
->cursor
.std
);
2180 if (NULL
!= pane
->cursor
.rangesel
)
2181 gnm_item_cursor_reposition (pane
->cursor
.rangesel
);
2182 if (NULL
!= pane
->cursor
.special
)
2183 gnm_item_cursor_reposition (pane
->cursor
.special
);
2184 for (l
= pane
->cursor
.expr_range
; l
; l
= l
->next
)
2185 gnm_item_cursor_reposition (GNM_ITEM_CURSOR (l
->data
));
2186 for (l
= pane
->cursor
.animated
; l
; l
= l
->next
)
2187 gnm_item_cursor_reposition (GNM_ITEM_CURSOR (l
->data
));
2189 /* ctrl pts do not scale with the zoom, compensate */
2190 if (pane
->drag
.ctrl_pts
!= NULL
)
2191 g_hash_table_foreach (pane
->drag
.ctrl_pts
,
2192 (GHFunc
) cb_update_ctrl_pts
, pane
);
2196 gnm_pane_cursor_bound_set (GnmPane
*pane
, GnmRange
const *r
)
2198 return gnm_item_cursor_bound_set (pane
->cursor
.std
, r
);
2201 /****************************************************************************/
2204 gnm_pane_rangesel_bound_set (GnmPane
*pane
, GnmRange
const *r
)
2206 return gnm_item_cursor_bound_set (pane
->cursor
.rangesel
, r
);
2209 gnm_pane_rangesel_start (GnmPane
*pane
, GnmRange
const *r
)
2212 SheetControlGUI
*scg
= pane
->simple
.scg
;
2214 g_return_if_fail (pane
->cursor
.rangesel
== NULL
);
2216 /* Hide the primary cursor while the range selection cursor is visible
2217 * and we are selecting on a different sheet than the expr being edited */
2218 if (scg_sheet (scg
) != wb_control_cur_sheet (scg_wbc (scg
)))
2219 gnm_item_cursor_set_visibility (pane
->cursor
.std
, FALSE
);
2220 item
= goc_item_new (pane
->grid_items
,
2221 gnm_item_cursor_get_type (),
2222 "SheetControlGUI", scg
,
2223 "style", GNM_ITEM_CURSOR_ANTED
,
2225 pane
->cursor
.rangesel
= GNM_ITEM_CURSOR (item
);
2226 gnm_item_cursor_bound_set (pane
->cursor
.rangesel
, r
);
2230 gnm_pane_rangesel_stop (GnmPane
*pane
)
2232 g_return_if_fail (pane
->cursor
.rangesel
!= NULL
);
2234 g_clear_object (&pane
->cursor
.rangesel
);
2236 /* Make the primary cursor visible again */
2237 gnm_item_cursor_set_visibility (pane
->cursor
.std
, TRUE
);
2239 gnm_pane_slide_stop (pane
);
2242 /****************************************************************************/
2245 gnm_pane_special_cursor_bound_set (GnmPane
*pane
, GnmRange
const *r
)
2247 return gnm_item_cursor_bound_set (pane
->cursor
.special
, r
);
2251 gnm_pane_special_cursor_start (GnmPane
*pane
, int style
, int button
)
2254 GocCanvas
*canvas
= GOC_CANVAS (pane
);
2256 g_return_if_fail (pane
->cursor
.special
== NULL
);
2257 item
= goc_item_new (
2258 GOC_GROUP (canvas
->root
),
2259 gnm_item_cursor_get_type (),
2260 "SheetControlGUI", pane
->simple
.scg
,
2264 pane
->cursor
.special
= GNM_ITEM_CURSOR (item
);
2268 gnm_pane_special_cursor_stop (GnmPane
*pane
)
2270 g_return_if_fail (pane
->cursor
.special
!= NULL
);
2272 g_clear_object (&pane
->cursor
.special
);
2276 gnm_pane_mouse_cursor_set (GnmPane
*pane
, GdkCursor
*c
)
2279 if (pane
->mouse_cursor
)
2280 g_object_unref (pane
->mouse_cursor
);
2281 pane
->mouse_cursor
= c
;
2284 /****************************************************************************/
2288 gnm_pane_expr_cursor_bound_set (GnmPane
*pane
, GnmRange
const *r
,
2291 GnmItemCursor
*cursor
;
2293 cursor
= (GnmItemCursor
*) goc_item_new
2294 (GOC_GROUP (GOC_CANVAS (pane
)->root
),
2295 gnm_item_cursor_get_type (),
2296 "SheetControlGUI", pane
->simple
.scg
,
2297 "style", GNM_ITEM_CURSOR_EXPR_RANGE
,
2301 gnm_item_cursor_bound_set (cursor
, r
);
2302 pane
->cursor
.expr_range
= g_slist_prepend
2303 (pane
->cursor
.expr_range
, cursor
);
2307 gnm_pane_expr_cursor_stop (GnmPane
*pane
)
2309 g_slist_free_full (pane
->cursor
.expr_range
, g_object_unref
);
2310 pane
->cursor
.expr_range
= NULL
;
2313 /****************************************************************************/
2316 gnm_pane_edit_start (GnmPane
*pane
)
2318 GocCanvas
*canvas
= GOC_CANVAS (pane
);
2320 g_return_if_fail (pane
->editor
== NULL
);
2322 /* edit item handles visibility checks */
2323 pane
->editor
= (GnmItemEdit
*) goc_item_new (
2324 GOC_GROUP (canvas
->root
),
2325 gnm_item_edit_get_type (),
2326 "SheetControlGUI", pane
->simple
.scg
,
2331 gnm_pane_edit_stop (GnmPane
*pane
)
2333 g_clear_object (&pane
->editor
);
2337 gnm_pane_objects_drag (GnmPane
*pane
, SheetObject
*so
,
2338 gdouble new_x
, gdouble new_y
, int drag_type
,
2339 gboolean symmetric
,gboolean snap_to_grid
)
2342 dx
= new_x
- pane
->drag
.last_x
;
2343 dy
= new_y
- pane
->drag
.last_y
;
2344 pane
->drag
.had_motion
= TRUE
;
2345 scg_objects_drag (pane
->simple
.scg
, pane
,
2346 so
, &dx
, &dy
, drag_type
, symmetric
, snap_to_grid
, TRUE
);
2348 pane
->drag
.last_x
+= dx
;
2349 pane
->drag
.last_y
+= dy
;
2352 /* new_x and new_y are in world coords */
2354 gnm_pane_object_move (GnmPane
*pane
, GObject
*ctrl_pt
,
2355 gdouble new_x
, gdouble new_y
,
2357 gboolean snap_to_grid
)
2359 int const idx
= GPOINTER_TO_INT (g_object_get_data (ctrl_pt
, "index"));
2360 pane
->cur_object
= g_object_get_data (G_OBJECT (ctrl_pt
), "so");
2362 gnm_pane_objects_drag (pane
, pane
->cur_object
, new_x
, new_y
, idx
,
2363 symmetric
, snap_to_grid
);
2365 gnm_pane_display_obj_size_tip (pane
, GOC_ITEM (ctrl_pt
));
2369 cb_slide_handler (GnmPane
*pane
, GnmPaneSlideInfo
const *info
)
2372 SheetControlGUI
const *scg
= pane
->simple
.scg
;
2373 double const scale
= 1. / GOC_CANVAS (pane
)->pixels_per_unit
;
2375 x
= scg_colrow_distance_get (scg
, TRUE
, pane
->first
.col
, info
->col
);
2376 x
+= pane
->first_offset
.x
;
2377 y
= scg_colrow_distance_get (scg
, FALSE
, pane
->first
.row
, info
->row
);
2378 y
+= pane
->first_offset
.y
;
2380 gnm_pane_object_move (pane
, info
->user_data
,
2381 x
* scale
, y
* scale
, FALSE
, FALSE
);
2387 cb_ptr_array_free (GPtrArray
*actions
)
2389 g_ptr_array_free (actions
, TRUE
);
2392 /* event and so can be NULL */
2394 gnm_pane_display_object_menu (GnmPane
*pane
, SheetObject
*so
, GdkEvent
*event
)
2396 SheetControlGUI
*scg
= pane
->simple
.scg
;
2397 GPtrArray
*actions
= g_ptr_array_new ();
2401 if (NULL
!= so
&& (!scg
->selected_objects
||
2402 NULL
== g_hash_table_lookup (scg
->selected_objects
, so
)))
2403 scg_object_select (scg
, so
);
2405 sheet_object_populate_menu (so
, actions
);
2407 if (actions
->len
== 0) {
2408 g_ptr_array_free (actions
, TRUE
);
2412 menu
= sheet_object_build_menu
2413 (sheet_object_get_view (so
, (SheetObjectViewContainer
*) pane
),
2415 g_object_set_data_full (G_OBJECT (menu
), "actions", actions
,
2416 (GDestroyNotify
)cb_ptr_array_free
);
2417 gtk_widget_show_all (menu
);
2418 gnumeric_popup_menu (GTK_MENU (menu
), event
);
2422 cb_collect_selected_objs (SheetObject
*so
, double *coords
, GSList
**accum
)
2424 *accum
= g_slist_prepend (*accum
, so
);
2428 cb_pane_popup_menu (GnmPane
*pane
)
2430 SheetControlGUI
*scg
= pane
->simple
.scg
;
2432 /* ignore new_object, it is not visible, and should not create a
2434 if (NULL
!= scg
->selected_objects
) {
2435 GSList
*accum
= NULL
;
2436 g_hash_table_foreach (scg
->selected_objects
,
2437 (GHFunc
) cb_collect_selected_objs
, &accum
);
2438 if (NULL
!= accum
&& NULL
== accum
->next
)
2439 gnm_pane_display_object_menu (pane
, accum
->data
, NULL
);
2440 g_slist_free (accum
);
2442 /* the popup-menu signal is a binding. the grid almost always
2443 * has focus we need to cheat to find out if the user
2444 * realllllly wants a col/row header menu */
2445 gboolean is_col
= FALSE
;
2446 gboolean is_row
= FALSE
;
2447 GdkWindow
*gdk_win
= gdk_device_get_window_at_position (
2448 gtk_get_current_event_device (),
2451 if (gdk_win
!= NULL
) {
2452 gpointer gtk_win_void
= NULL
;
2453 GtkWindow
*gtk_win
= NULL
;
2454 gdk_window_get_user_data (gdk_win
, >k_win_void
);
2455 gtk_win
= gtk_win_void
;
2456 if (gtk_win
!= NULL
) {
2457 if (gtk_win
== (GtkWindow
*)pane
->col
.canvas
)
2459 else if (gtk_win
== (GtkWindow
*)pane
->row
.canvas
)
2464 scg_context_menu (scg
, NULL
, is_col
, is_row
);
2469 control_point_set_cursor (SheetControlGUI
const *scg
, GocItem
*ctrl_pt
)
2471 SheetObject
*so
= g_object_get_data (G_OBJECT (ctrl_pt
), "so");
2472 int idx
= GPOINTER_TO_INT (g_object_get_data (G_OBJECT (ctrl_pt
), "index"));
2473 double const *coords
= g_hash_table_lookup (scg
->selected_objects
, so
);
2474 gboolean invert_h
= coords
[0] > coords
[2];
2475 gboolean invert_v
= coords
[1] > coords
[3];
2476 GdkCursorType cursor
;
2478 if (goc_canvas_get_direction (ctrl_pt
->canvas
) == GOC_DIRECTION_RTL
)
2479 invert_h
= !invert_h
;
2482 case 1: invert_v
= !invert_v
;
2484 case 6: cursor
= invert_v
? GDK_TOP_SIDE
: GDK_BOTTOM_SIDE
;
2487 case 3: invert_h
= !invert_h
;
2489 case 4: cursor
= invert_h
? GDK_LEFT_SIDE
: GDK_RIGHT_SIDE
;
2492 case 2: invert_h
= !invert_h
;
2494 case 0: cursor
= invert_v
2495 ? (invert_h
? GDK_BOTTOM_RIGHT_CORNER
: GDK_BOTTOM_LEFT_CORNER
)
2496 : (invert_h
? GDK_TOP_RIGHT_CORNER
: GDK_TOP_LEFT_CORNER
);
2499 case 7: invert_h
= !invert_h
;
2501 case 5: cursor
= invert_v
2502 ? (invert_h
? GDK_TOP_RIGHT_CORNER
: GDK_TOP_LEFT_CORNER
)
2503 : (invert_h
? GDK_BOTTOM_RIGHT_CORNER
: GDK_BOTTOM_LEFT_CORNER
);
2510 gnm_widget_set_cursor_type (GTK_WIDGET (ctrl_pt
->canvas
), cursor
);
2514 target_list_add_list (GtkTargetList
*targets
, GtkTargetList
*added_targets
)
2517 GtkTargetEntry
*gte
;
2519 g_return_if_fail (targets
!= NULL
);
2521 if (added_targets
== NULL
)
2524 gte
= gtk_target_table_new_from_list (added_targets
, &n
);
2525 gtk_target_list_add_table (targets
, gte
, n
);
2526 gtk_target_table_free (gte
, n
);
2530 * Drag one or more sheet objects using GTK drag and drop, to the same
2531 * sheet, another workbook, another gnumeric or a different application.
2534 gnm_pane_drag_begin (GnmPane
*pane
, SheetObject
*so
, GdkEvent
*event
)
2536 GtkTargetList
*targets
, *im_targets
;
2537 GocCanvas
*canvas
= GOC_CANVAS (pane
);
2538 SheetControlGUI
*scg
= pane
->simple
.scg
;
2540 SheetObject
*imageable
= NULL
, *exportable
= NULL
;
2542 SheetObject
*candidate
;
2544 targets
= gtk_target_list_new (drag_types_out
,
2545 G_N_ELEMENTS (drag_types_out
));
2546 objects
= go_hash_keys (scg
->selected_objects
);
2547 for (ptr
= objects
; ptr
!= NULL
; ptr
= ptr
->next
) {
2548 candidate
= GNM_SO (ptr
->data
);
2550 if (exportable
== NULL
&&
2551 GNM_IS_SO_EXPORTABLE (candidate
))
2552 exportable
= candidate
;
2553 if (imageable
== NULL
&&
2554 GNM_IS_SO_IMAGEABLE (candidate
))
2555 imageable
= candidate
;
2559 im_targets
= sheet_object_exportable_get_target_list (exportable
);
2560 if (im_targets
!= NULL
) {
2561 target_list_add_list (targets
, im_targets
);
2562 gtk_target_list_unref (im_targets
);
2566 im_targets
= sheet_object_get_target_list (imageable
);
2567 if (im_targets
!= NULL
) {
2568 target_list_add_list (targets
, im_targets
);
2569 gtk_target_list_unref (im_targets
);
2574 if (gnm_debug_flag ("dnd")) {
2576 GtkTargetEntry
*gte
= gtk_target_table_new_from_list (targets
, &n
);
2577 g_printerr ("%u offered formats:\n", n
);
2578 for (i
= 0; i
< n
; i
++)
2579 g_printerr ("%s\n", gte
[n
].target
);
2580 gtk_target_table_free (gte
, n
);
2583 gtk_drag_begin (GTK_WIDGET (canvas
), targets
,
2584 GDK_ACTION_COPY
| GDK_ACTION_MOVE
,
2585 pane
->drag
.button
, event
);
2586 gtk_target_list_unref (targets
);
2587 g_slist_free (objects
);
2591 gnm_pane_object_start_resize (GnmPane
*pane
, int button
, guint64 x
, gint64 y
,
2592 SheetObject
*so
, int drag_type
, gboolean is_creation
)
2596 g_return_if_fail (GNM_IS_SO (so
));
2597 g_return_if_fail (0 <= drag_type
);
2598 g_return_if_fail (drag_type
< 9);
2600 ctrl_pts
= g_hash_table_lookup (pane
->drag
.ctrl_pts
, so
);
2602 g_return_if_fail (NULL
!= ctrl_pts
);
2604 if (is_creation
&& !sheet_object_can_resize (so
)) {
2605 scg_objects_drag_commit (pane
->simple
.scg
, 9, TRUE
,
2609 gnm_simple_canvas_grab (ctrl_pts
[drag_type
]);
2610 pane
->drag
.created_objects
= is_creation
;
2611 pane
->drag
.button
= button
;
2612 pane
->drag
.last_x
= pane
->drag
.origin_x
= x
;
2613 pane
->drag
.last_y
= pane
->drag
.origin_y
= y
;
2614 pane
->drag
.had_motion
= FALSE
;
2615 gnm_pane_slide_init (pane
);
2616 gnm_widget_set_cursor_type (GTK_WIDGET (pane
), GDK_HAND2
);
2620 GnmControlCircleItem
2622 typedef GocCircle GnmControlCircle
;
2623 typedef GocCircleClass GnmControlCircleClass
;
2625 #define CONTROL_TYPE_CIRCLE (control_circle_get_type ())
2626 #define CONTROL_CIRCLE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CONTROL_TYPE_CIRCLE, GnmControlCircle))
2627 #define CONTROL_IS_CIRCLE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CONTROL_TYPE_CIRCLE))
2629 static GType
control_circle_get_type (void);
2632 control_point_button_pressed (GocItem
*item
, int button
, double x
, double y
)
2634 GnmPane
*pane
= GNM_PANE (item
->canvas
);
2635 GdkEventButton
*event
= (GdkEventButton
*) goc_canvas_get_cur_event (item
->canvas
);
2639 if (0 != pane
->drag
.button
)
2642 x
*= goc_canvas_get_pixels_per_unit (item
->canvas
);
2643 y
*= goc_canvas_get_pixels_per_unit (item
->canvas
);
2644 so
= g_object_get_data (G_OBJECT (item
), "so");
2645 idx
= GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item
), "index"));
2646 switch (event
->button
) {
2648 case 2: gnm_pane_object_start_resize (pane
, button
, x
, y
, so
, idx
, FALSE
);
2650 case 3: gnm_pane_display_object_menu (pane
, so
, (GdkEvent
*) event
);
2652 default: /* Ignore mouse wheel events */
2659 control_point_button_released (GocItem
*item
, int button
, G_GNUC_UNUSED
double x
, G_GNUC_UNUSED
double y
)
2661 GnmPane
*pane
= GNM_PANE (item
->canvas
);
2662 SheetControlGUI
*scg
= pane
->simple
.scg
;
2666 if (pane
->drag
.button
!= button
)
2668 so
= g_object_get_data (G_OBJECT (item
), "so");
2669 idx
= GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item
), "index"));
2670 pane
->drag
.button
= 0;
2671 gnm_simple_canvas_ungrab (item
);
2672 gnm_pane_slide_stop (pane
);
2673 control_point_set_cursor (scg
, item
);
2675 ; /* ignore fake event generated by the dnd code */
2676 else if (pane
->drag
.had_motion
)
2677 scg_objects_drag_commit (scg
, idx
,
2678 pane
->drag
.created_objects
,
2680 else if (pane
->drag
.created_objects
&& idx
== 7) {
2682 sheet_object_default_size (so
, &w
, &h
);
2683 scg_objects_drag (scg
, NULL
, NULL
, &w
, &h
, 7, FALSE
, FALSE
, FALSE
);
2684 scg_objects_drag_commit (scg
, 7, TRUE
,
2687 gnm_pane_clear_obj_size_tip (pane
);
2692 control_point_motion (GocItem
*item
, double x
, double y
)
2694 GnmPane
*pane
= GNM_PANE (item
->canvas
);
2695 GdkEventMotion
*event
= (GdkEventMotion
*) goc_canvas_get_cur_event (item
->canvas
);
2699 if (0 == pane
->drag
.button
)
2702 x
*= goc_canvas_get_pixels_per_unit (item
->canvas
);
2703 y
*= goc_canvas_get_pixels_per_unit (item
->canvas
);
2704 /* TODO : motion is still too granular along the internal axis
2705 * when the other axis is external.
2706 * eg drag from middle of sheet down. The x axis is still internal
2707 * onlt the y is external, however, since we are autoscrolling
2708 * we are limited to moving with col/row coords, not x,y.
2709 * Possible solution would be to change the EXTERIOR_ONLY flag
2710 * to something like USE_PIXELS_INSTEAD_OF_COLROW and change
2711 * the semantics of the col,row args in the callback. However,
2712 * that is more work than I want to do right now.
2714 so
= g_object_get_data (G_OBJECT (item
), "so");
2715 idx
= GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item
), "index"));
2717 gnm_pane_drag_begin (pane
, so
, (GdkEvent
*) event
);
2718 else if (gnm_pane_handle_motion (pane
,
2720 GNM_PANE_SLIDE_X
| GNM_PANE_SLIDE_Y
|
2721 GNM_PANE_SLIDE_EXTERIOR_ONLY
,
2722 cb_slide_handler
, item
))
2723 gnm_pane_object_move (pane
, G_OBJECT (item
),
2725 (event
->state
& GDK_CONTROL_MASK
) != 0,
2726 (event
->state
& GDK_SHIFT_MASK
) != 0);
2731 control_point_button2_pressed (GocItem
*item
, int button
, G_GNUC_UNUSED
double x
, G_GNUC_UNUSED
double y
)
2733 GnmPane
*pane
= GNM_PANE (item
->canvas
);
2734 SheetControlGUI
*scg
= pane
->simple
.scg
;
2737 so
= g_object_get_data (G_OBJECT (item
), "so");
2738 if (pane
->drag
.button
== 1)
2739 sheet_object_get_editor (so
, GNM_SHEET_CONTROL (scg
));
2744 update_control_point_colors (GocItem
*item
, GtkStateFlags flags
)
2746 GtkStyleContext
*context
= goc_item_get_style_context (item
);
2747 GOStyle
*style
= go_styled_object_get_style (GO_STYLED_OBJECT (item
));
2748 GdkRGBA
*fore
, *back
;
2750 gtk_style_context_get (context
, flags
,
2752 "background-color", &back
,
2754 go_color_from_gdk_rgba (fore
, &style
->line
.color
);
2755 go_color_from_gdk_rgba (back
, &style
->fill
.pattern
.back
);
2756 gdk_rgba_free (fore
);
2757 gdk_rgba_free (back
);
2758 goc_item_invalidate (item
);
2762 control_point_enter_notify (GocItem
*item
, G_GNUC_UNUSED
double x
, G_GNUC_UNUSED
double y
)
2764 GnmPane
*pane
= GNM_PANE (item
->canvas
);
2765 SheetControlGUI
*scg
= pane
->simple
.scg
;
2768 control_point_set_cursor (scg
, item
);
2770 pane
->cur_object
= g_object_get_data (G_OBJECT (item
), "so");
2771 idx
= GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item
), "index"));
2773 update_control_point_colors (item
, GTK_STATE_FLAG_PRELIGHT
);
2774 gnm_pane_display_obj_size_tip (pane
, item
);
2780 control_point_leave_notify (GocItem
*item
, G_GNUC_UNUSED
double x
, G_GNUC_UNUSED
double y
)
2782 GnmPane
*pane
= GNM_PANE (item
->canvas
);
2783 SheetControlGUI
*scg
= pane
->simple
.scg
;
2786 control_point_set_cursor (scg
, item
);
2788 idx
= GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item
), "index"));
2790 update_control_point_colors (item
, GTK_STATE_FLAG_NORMAL
);
2791 gnm_pane_clear_obj_size_tip (pane
);
2793 pane
->cur_object
= NULL
;
2798 control_circle_class_init (GocItemClass
*item_klass
)
2800 item_klass
->button_pressed
= control_point_button_pressed
;
2801 item_klass
->button_released
= control_point_button_released
;
2802 item_klass
->motion
= control_point_motion
;
2803 item_klass
->button2_pressed
= control_point_button2_pressed
;
2804 item_klass
->enter_notify
= control_point_enter_notify
;
2805 item_klass
->leave_notify
= control_point_leave_notify
;
2808 static GSF_CLASS (GnmControlCircle
, control_circle
,
2809 control_circle_class_init
, NULL
,
2812 #define GNM_ITEM_ACETATE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), item_acetate_get_type (), ItemAcetate))
2813 #define GNM_IS_ITEM_ACETATE(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), item_acetate_get_type ()))
2817 static GType
item_acetate_get_type (void);
2819 typedef GocRectangle ItemAcetate
;
2820 typedef GocRectangleClass ItemAcetateClass
;
2823 item_acetate_distance (GocItem
*item
, double x
, double y
, GocItem
**actual_item
)
2825 if (x
< (item
->x0
- MARGIN
) ||
2826 x
> (item
->x1
+ MARGIN
) ||
2827 y
< (item
->y0
- MARGIN
) ||
2828 y
> (item
->y1
+ MARGIN
))
2830 *actual_item
= item
;
2835 item_acetate_class_init (GocItemClass
*item_class
)
2837 item_class
->distance
= item_acetate_distance
;
2838 item_class
->button_pressed
= control_point_button_pressed
;
2839 item_class
->button_released
= control_point_button_released
;
2840 item_class
->motion
= control_point_motion
;
2841 item_class
->button2_pressed
= control_point_button2_pressed
;
2842 item_class
->enter_notify
= control_point_enter_notify
;
2843 item_class
->leave_notify
= control_point_leave_notify
;
2846 static GSF_CLASS (ItemAcetate
, item_acetate
,
2847 item_acetate_class_init
, NULL
,
2851 * new_control_point:
2853 * @idx: control point index to be created
2854 * @x: x coordinate of control point
2855 * @y: y coordinate of control point
2857 * This is used to create a number of control points in a sheet
2858 * object, the meaning of them is used in other parts of the code
2859 * to belong to the following locations:
2861 * 0 -------- 1 -------- 2
2865 * 5 -------- 6 -------- 7
2867 * 8 == a clear overlay that extends slightly beyond the region
2868 * 9 == an optional stippled rectangle for moving/resizing expensive
2872 new_control_point (GnmPane
*pane
, SheetObject
*so
, int idx
, double x
, double y
)
2876 int radius
, outline
;
2877 double scale
= GOC_CANVAS (pane
)->pixels_per_unit
;
2879 gtk_widget_style_get (GTK_WIDGET (pane
),
2880 "control-circle-size", &radius
,
2881 "control-circle-outline", &outline
,
2884 style
= go_style_new ();
2885 style
->line
.width
= outline
;
2886 style
->line
.auto_color
= FALSE
;
2887 style
->line
.dash_type
= GO_LINE_SOLID
; /* anything but 0 */
2888 style
->line
.pattern
= GO_PATTERN_SOLID
;
2889 item
= goc_item_new (
2891 CONTROL_TYPE_CIRCLE
,
2894 "radius", radius
/ scale
,
2896 g_object_unref (style
);
2898 update_control_point_colors (item
, GTK_STATE_FLAG_NORMAL
);
2900 g_object_set_data (G_OBJECT (item
), "index", GINT_TO_POINTER (idx
));
2901 g_object_set_data (G_OBJECT (item
), "so", so
);
2908 * Changes the x and y position of the idx-th control point,
2909 * creating the control point if necessary.
2912 set_item_x_y (GnmPane
*pane
, SheetObject
*so
, GocItem
**ctrl_pts
,
2913 int idx
, double x
, double y
, gboolean visible
)
2915 double scale
= GOC_CANVAS (pane
)->pixels_per_unit
;
2916 if (ctrl_pts
[idx
] == NULL
)
2917 ctrl_pts
[idx
] = new_control_point (pane
, so
, idx
, x
/ scale
, y
/ scale
);
2919 goc_item_set (ctrl_pts
[idx
], "x", x
/ scale
, "y", y
/ scale
, NULL
);
2921 goc_item_show (ctrl_pts
[idx
]);
2923 goc_item_hide (ctrl_pts
[idx
]);
2926 #define normalize_high_low(d1,d2) if (d1<d2) { double tmp=d1; d1=d2; d2=tmp;}
2929 set_acetate_coords (GnmPane
*pane
, SheetObject
*so
, GocItem
**ctrl_pts
,
2930 double l
, double t
, double r
, double b
)
2932 double scale
= goc_canvas_get_pixels_per_unit (GOC_CANVAS (pane
));
2933 int radius
, outline
;
2935 if (!sheet_object_rubber_band_directly (so
)) {
2936 if (NULL
== ctrl_pts
[9]) {
2937 GOStyle
*style
= go_style_new ();
2938 GtkStyleContext
*context
;
2942 ctrl_pts
[9] = item
= goc_item_new (pane
->action_items
,
2945 context
= goc_item_get_style_context (item
);
2946 gtk_style_context_add_class (context
, "object-size");
2947 gtk_style_context_add_class (context
, "rubber-band");
2949 style
->fill
.auto_type
= FALSE
;
2950 style
->fill
.type
= GO_STYLE_FILL_PATTERN
;
2951 style
->fill
.auto_back
= FALSE
;
2952 style
->fill
.pattern
.back
= 0;
2953 style
->fill
.auto_fore
= FALSE
;
2954 style
->fill
.pattern
.fore
= 0;
2955 style
->line
.pattern
= GO_PATTERN_FOREGROUND_SOLID
;
2956 style
->line
.width
= 0.;
2957 style
->line
.auto_color
= FALSE
;
2958 style
->line
.color
= 0;
2959 gnm_style_context_get_color (context
, GTK_STATE_FLAG_NORMAL
, &rgba
);
2960 go_color_from_gdk_rgba (&rgba
, &style
->line
.fore
);
2961 go_styled_object_set_style (GO_STYLED_OBJECT (item
),
2963 g_object_unref (style
);
2964 goc_item_lower_to_bottom (item
);
2966 normalize_high_low (r
, l
);
2967 normalize_high_low (b
, t
);
2968 goc_item_set (ctrl_pts
[9],
2969 "x", l
/ scale
, "y", t
/ scale
,
2970 "width", (r
- l
) / scale
, "height", (b
- t
) / scale
,
2974 SheetObjectView
*sov
= sheet_object_get_view (so
, (SheetObjectViewContainer
*)pane
);
2976 sov
= sheet_object_new_view (so
, (SheetObjectViewContainer
*)pane
);
2978 coords
[0] = l
; coords
[2] = r
; coords
[1] = t
; coords
[3] = b
;
2980 sheet_object_view_set_bounds (sov
, coords
, TRUE
);
2981 normalize_high_low (r
, l
);
2982 normalize_high_low (b
, t
);
2985 gtk_widget_style_get (GTK_WIDGET (pane
),
2986 "control-circle-size", &radius
,
2987 "control-circle-outline", &outline
,
2990 l
-= (radius
+ outline
) / 2 - 1;
2991 r
+= (radius
+ outline
) / 2;
2992 t
-= (radius
+ outline
) / 2 - 1;
2993 b
+= (radius
+ outline
) / 2;
2995 if (NULL
== ctrl_pts
[8]) {
2996 GOStyle
*style
= go_style_new ();
2999 style
->fill
.auto_type
= FALSE
;
3000 style
->fill
.type
= GO_STYLE_FILL_PATTERN
;
3001 style
->fill
.auto_back
= FALSE
;
3002 go_pattern_set_solid (&style
->fill
.pattern
, 0);
3003 style
->line
.auto_dash
= FALSE
;
3004 style
->line
.dash_type
= GO_LINE_NONE
;
3005 /* work around the screwup in shapes that adds a large
3006 * border to anything that uses miter (is this required for
3007 * a rectangle in goc-canvas? */
3008 style
->line
.join
= CAIRO_LINE_JOIN_ROUND
;
3009 item
= goc_item_new (
3011 item_acetate_get_type (),
3014 g_object_unref (style
);
3015 g_object_set_data (G_OBJECT (item
), "index",
3016 GINT_TO_POINTER (8));
3017 g_object_set_data (G_OBJECT (item
), "so", so
);
3021 goc_item_set (ctrl_pts
[8],
3024 "width", (r
- l
) / scale
,
3025 "height", (b
- t
) / scale
,
3030 gnm_pane_object_unselect (GnmPane
*pane
, SheetObject
*so
)
3032 gnm_pane_clear_obj_size_tip (pane
);
3033 g_hash_table_remove (pane
->drag
.ctrl_pts
, so
);
3037 * gnm_pane_object_update_bbox:
3041 * Updates the position and potentially creates control points
3042 * for manipulating the size/position of @so.
3045 gnm_pane_object_update_bbox (GnmPane
*pane
, SheetObject
*so
)
3047 GocItem
**ctrl_pts
= g_hash_table_lookup (pane
->drag
.ctrl_pts
, so
);
3048 double const *pts
= g_hash_table_lookup (
3049 pane
->simple
.scg
->selected_objects
, so
);
3050 int radius
, outline
, total_size
;
3052 if (ctrl_pts
== NULL
) {
3053 ctrl_pts
= g_new0 (GocItem
*, 10);
3054 g_hash_table_insert (pane
->drag
.ctrl_pts
, so
, ctrl_pts
);
3057 g_return_if_fail (ctrl_pts
!= NULL
);
3059 gtk_widget_style_get (GTK_WIDGET (pane
),
3060 "control-circle-size", &radius
,
3061 "control-circle-outline", &outline
,
3063 /* space for 2 halves and a full */
3064 total_size
= radius
* 4 + outline
* 2;
3066 /* set the acetate 1st so that the other points will override it */
3067 set_acetate_coords (pane
, so
, ctrl_pts
, pts
[0], pts
[1], pts
[2], pts
[3]);
3068 if (sheet_object_can_resize (so
)) {
3069 set_item_x_y (pane
, so
, ctrl_pts
, 0, pts
[0], pts
[1], TRUE
);
3070 set_item_x_y (pane
, so
, ctrl_pts
, 1, (pts
[0] + pts
[2]) / 2, pts
[1],
3071 fabs (pts
[2]-pts
[0]) >= total_size
);
3072 set_item_x_y (pane
, so
, ctrl_pts
, 2, pts
[2], pts
[1], TRUE
);
3073 set_item_x_y (pane
, so
, ctrl_pts
, 3, pts
[0], (pts
[1] + pts
[3]) / 2,
3074 fabs (pts
[3]-pts
[1]) >= total_size
);
3075 set_item_x_y (pane
, so
, ctrl_pts
, 4, pts
[2], (pts
[1] + pts
[3]) / 2,
3076 fabs (pts
[3]-pts
[1]) >= total_size
);
3077 set_item_x_y (pane
, so
, ctrl_pts
, 5, pts
[0], pts
[3], TRUE
);
3078 set_item_x_y (pane
, so
, ctrl_pts
, 6, (pts
[0] + pts
[2]) / 2, pts
[3],
3079 fabs (pts
[2]-pts
[0]) >= total_size
);
3080 set_item_x_y (pane
, so
, ctrl_pts
, 7, pts
[2], pts
[3], TRUE
);
3085 cb_bounds_changed (SheetObject
*so
, GocItem
*sov
)
3087 double coords
[4], *cur
;
3088 SheetControlGUI
*scg
= GNM_SIMPLE_CANVAS (sov
->canvas
)->scg
;
3089 if (GNM_PANE (sov
->canvas
)->drag
.button
!= 0)
3090 return; /* do not reset bounds during drag */
3092 scg_object_anchor_to_coords (scg
, sheet_object_get_anchor (so
), coords
);
3093 if (NULL
!= scg
->selected_objects
&&
3094 NULL
!= (cur
= g_hash_table_lookup (scg
->selected_objects
, so
))) {
3096 for (i
= 4; i
-- > 0 ;) cur
[i
] = coords
[i
];
3097 gnm_pane_object_update_bbox (GNM_PANE (sov
->canvas
), so
);
3100 sheet_object_view_set_bounds (GNM_SO_VIEW (sov
),
3101 coords
, so
->flags
& SHEET_OBJECT_IS_VISIBLE
);
3105 * gnm_pane_object_register:
3106 * @so: A sheet object
3107 * @view: A canvas item acting as a view for @so
3108 * @selectable: Add handlers for selecting and editing the object
3110 * Setup some standard callbacks for manipulating a view of a sheet object.
3111 * Returns: (transfer none): @view set to a #SheetObjectView.
3114 gnm_pane_object_register (SheetObject
*so
, GocItem
*view
, gboolean selectable
)
3116 g_signal_connect_object (so
, "bounds-changed",
3117 G_CALLBACK (cb_bounds_changed
), view
, 0);
3118 return GNM_SO_VIEW (view
);
3122 * gnm_pane_object_widget_register:
3123 * @so: A sheet object
3124 * @widget: The widget for the sheet object view
3125 * @view: A canvas item acting as a view for @so
3127 * Setup some standard callbacks for manipulating widgets as views of sheet
3131 gnm_pane_widget_register (SheetObject
*so
, GtkWidget
*w
, GocItem
*view
)
3133 if (GTK_IS_CONTAINER (w
)) {
3134 GList
*ptr
, *children
= gtk_container_get_children (GTK_CONTAINER (w
));
3135 for (ptr
= children
; ptr
!= NULL
; ptr
= ptr
->next
)
3136 gnm_pane_widget_register (so
, ptr
->data
, view
);
3137 g_list_free (children
);
3142 gnm_pane_set_direction (GnmPane
*pane
, GocDirection direction
)
3144 goc_canvas_set_direction (GOC_CANVAS (pane
), direction
);
3145 if (pane
->col
.canvas
!= NULL
)
3146 goc_canvas_set_direction (pane
->col
.canvas
, direction
);