2 * sheet-control-gui.c: Implements a graphic control for a sheet.
4 * Copyright (C) 2000-2006 Jody Goldberg (jody@gnome.org)
5 * Copyright (C) 1997-1999 Miguel de Icaza (miguel@kernel.org)
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) version 3.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
22 #include <gnumeric-config.h>
23 #include <glib/gi18n-lib.h>
25 #include <sheet-control-gui-priv.h>
28 #include <sheet-private.h>
29 #include <sheet-view.h>
30 #include <sheet-merge.h>
32 #include <workbook-view.h>
33 #include <workbook-cmd-format.h>
34 #include <wbc-gtk-impl.h>
36 #include <selection.h>
38 #include <sheet-style.h>
39 #include <sheet-object-impl.h>
40 #include <sheet-object-cell-comment.h>
41 #include <sheet-object-image.h>
44 #include <parse-util.h>
45 #include <selection.h>
46 #include <application.h>
50 #include <gnm-commands-slicer.h>
51 #include <clipboard.h>
52 #include <dialogs/dialogs.h>
54 #include <sheet-merge.h>
57 #include <style-color.h>
58 #include <gnumeric-conf.h>
60 #include <gnm-pane-impl.h>
62 #include <item-cursor.h>
63 #include <widgets/gnm-expr-entry.h>
64 #include <gnm-sheet-slicer.h>
65 #include <input-msg.h>
67 #include <go-data-slicer-field.h>
68 #include <goffice/goffice.h>
70 #include <gdk/gdkkeysyms.h>
71 #include <gsf/gsf-impl-utils.h>
72 #include <gsf/gsf-input.h>
73 #include <gsf/gsf-output-memory.h>
77 static GObjectClass
*scg_parent_class
;
79 static void scg_unant (SheetControl
*sc
);
80 static void set_resize_pane_pos (SheetControlGUI
*scg
, GtkPaned
*p
);
81 static void cb_resize_pane_motion (GtkPaned
*p
, GParamSpec
*pspec
, SheetControlGUI
*scg
);
86 * @scg: #SheetControlGUI
87 * @pane: the pane index.
89 * Returns: (transfer none): the pane.
92 scg_pane (SheetControlGUI
*scg
, int p
)
94 /* it is ok to request a pane when we are not frozen */
95 g_return_val_if_fail (GNM_IS_SCG (scg
), NULL
);
96 g_return_val_if_fail (p
>= 0, NULL
);
97 g_return_val_if_fail (p
< 4, NULL
);
104 * @scg: #SheetControlGUI
106 * Returns: (transfer none): the sheet view.
109 scg_view (SheetControlGUI
const *scg
)
111 g_return_val_if_fail (GNM_IS_SCG (scg
), NULL
);
112 return scg
->sheet_control
.view
;
118 * @scg: #SheetControlGUI
120 * Returns: (transfer none): the sheet.
123 scg_sheet (SheetControlGUI
const *scg
)
125 return sc_sheet ((SheetControl
*)scg
);
131 * @scg: #SheetControlGUI
133 * Returns: (transfer none): the workbook control.
136 scg_wbc (SheetControlGUI
const *scg
)
138 g_return_val_if_fail (GNM_IS_SCG (scg
), NULL
);
139 return scg
->sheet_control
.wbc
;
145 * @scg: #SheetControlGUI
147 * Returns: (transfer none): the #WBCGtk.
151 scg_wbcg (SheetControlGUI
const *scg
)
153 g_return_val_if_fail (GNM_IS_SCG (scg
), NULL
);
158 scg_redraw_all (SheetControl
*sc
, gboolean headers
)
160 SheetControlGUI
*scg
= (SheetControlGUI
*)sc
;
162 g_return_if_fail (GNM_IS_SCG (scg
));
164 SCG_FOREACH_PANE (scg
, pane
, {
165 goc_canvas_invalidate (GOC_CANVAS (pane
),
166 G_MININT64
, 0, G_MAXINT64
, G_MAXINT64
);
168 if (NULL
!= pane
->col
.canvas
)
169 goc_canvas_invalidate (pane
->col
.canvas
,
170 0, 0, G_MAXINT64
, G_MAXINT64
);
171 if (NULL
!= pane
->row
.canvas
)
172 goc_canvas_invalidate (pane
->row
.canvas
,
173 0, 0, G_MAXINT64
, G_MAXINT64
);
179 scg_redraw_range (SheetControl
*sc
, GnmRange
const *r
)
181 SheetControlGUI
*scg
= (SheetControlGUI
*)sc
;
182 Sheet
const *sheet
= scg_sheet (scg
);
183 GnmRange visible
, area
;
186 * Getting the bounding box causes row respans to be done if
187 * needed. That can be expensive, so just redraw the whole
188 * sheet if the row count is too big.
190 if (r
->end
.row
- r
->start
.row
> 500) {
191 scg_redraw_all (sc
, FALSE
);
195 /* We potentially do a lot of recalcs as part of this, so make sure
196 stuff that caches sub-computations see the whole thing instead
197 of clearing between cells. */
198 gnm_app_recalc_start ();
200 SCG_FOREACH_PANE (scg
, pane
, {
201 visible
.start
= pane
->first
;
202 visible
.end
= pane
->last_visible
;
204 if (range_intersection (&area
, r
, &visible
)) {
205 sheet_range_bounding_box (sheet
, &area
);
206 gnm_pane_redraw_range (pane
, &area
);
210 gnm_app_recalc_finish ();
214 scg_redraw_headers (SheetControl
*sc
,
215 gboolean
const col
, gboolean
const row
,
216 GnmRange
const * r
/* optional == NULL */)
218 SheetControlGUI
*scg
= (SheetControlGUI
*)sc
;
224 * A rough guess of the trade off point between of redrawing all
225 * and calculating the redraw size
227 const int COL_HEURISTIC
= 20;
228 const int ROW_HEURISTIC
= 50;
230 for (i
= scg
->active_panes
; i
-- > 0 ; ) {
231 if (NULL
== (pane
= scg
->pane
[i
]))
234 if (col
&& pane
->col
.canvas
!= NULL
) {
235 int left
= 0, right
= G_MAXINT
- 1;
236 GocCanvas
* const col_canvas
= GOC_CANVAS (pane
->col
.canvas
);
237 scale
= goc_canvas_get_pixels_per_unit (col_canvas
);
240 int const size
= r
->end
.col
- r
->start
.col
;
241 if (-COL_HEURISTIC
< size
&& size
< COL_HEURISTIC
) {
242 left
= pane
->first_offset
.x
+
243 scg_colrow_distance_get (scg
, TRUE
,
244 pane
->first
.col
, r
->start
.col
);
246 scg_colrow_distance_get (scg
, TRUE
,
247 r
->start
.col
, r
->end
.col
+1);
250 goc_canvas_invalidate (col_canvas
,
251 left
/ scale
, 0, right
/ scale
, G_MAXINT64
);
254 if (row
&& pane
->row
.canvas
!= NULL
) {
255 gint64 top
= 0, bottom
= G_MAXINT64
- 1;
256 scale
= goc_canvas_get_pixels_per_unit (pane
->row
.canvas
);
258 int const size
= r
->end
.row
- r
->start
.row
;
259 if (-ROW_HEURISTIC
< size
&& size
< ROW_HEURISTIC
) {
260 top
= pane
->first_offset
.y
+
261 scg_colrow_distance_get (scg
, FALSE
,
262 pane
->first
.row
, r
->start
.row
);
264 scg_colrow_distance_get (scg
, FALSE
,
265 r
->start
.row
, r
->end
.row
+1);
268 goc_canvas_invalidate (GOC_CANVAS (pane
->row
.canvas
),
269 0, top
/ scale
, G_MAXINT64
, bottom
/ scale
);
275 cb_outline_button (GtkWidget
*btn
, SheetControlGUI
*scg
)
277 SheetControl
*sc
= (SheetControl
*) scg
;
278 WorkbookControl
*wbc
= sc
->wbc
;
279 GPtrArray
const *btns
;
281 gboolean is_cols
= g_object_get_data (G_OBJECT (btn
), "is_cols") != NULL
;
284 btns
= is_cols
? scg
->col_group
.buttons
: scg
->row_group
.buttons
;
285 for (i
= 0; i
< btns
->len
; i
++)
286 if (g_ptr_array_index (btns
, i
) == btn
)
289 g_return_if_fail (i
< btns
->len
);
291 cmd_global_outline_change (wbc
, is_cols
, i
+1);
295 scg_setup_group_buttons (SheetControlGUI
*scg
, unsigned max_outline
,
296 GnmItemBar
const *ib
, gboolean is_cols
, int w
, int h
,
297 GPtrArray
*btns
, GtkWidget
*box
)
299 PangoFontDescription
*font_desc
;
301 Sheet
const *sheet
= scg_sheet (scg
);
303 if (!sheet
->display_outlines
)
305 else if (max_outline
> 0)
308 while (btns
->len
> max_outline
) {
309 GtkWidget
*w
= g_ptr_array_remove_index_fast (btns
, btns
->len
- 1);
310 gtk_container_remove (GTK_CONTAINER (box
),
311 gtk_widget_get_parent (w
));
314 while (btns
->len
< max_outline
) {
315 GtkWidget
*out
= gtk_alignment_new (.5, .5, 1., 1.);
316 GtkWidget
*in
= gtk_alignment_new (.5, .5, 0., 0.);
317 GtkWidget
*btn
= gtk_button_new ();
318 char *tmp
= g_strdup_printf ("<small>%d</small>", btns
->len
+1);
319 GtkWidget
*label
= gtk_label_new (NULL
);
320 gtk_label_set_markup (GTK_LABEL (label
), tmp
);
323 gtk_widget_set_can_focus (btn
, FALSE
);
324 gtk_container_add (GTK_CONTAINER (in
), label
);
325 gtk_container_add (GTK_CONTAINER (btn
), in
);
326 gtk_container_add (GTK_CONTAINER (out
), btn
);
327 gtk_box_pack_start (GTK_BOX (box
), out
, TRUE
, TRUE
, 0);
328 g_ptr_array_add (btns
, btn
);
330 g_signal_connect (G_OBJECT (btn
),
332 G_CALLBACK (cb_outline_button
), scg
);
334 g_object_set_data (G_OBJECT (btn
),
335 "is_cols", GINT_TO_POINTER (1));
338 font_desc
= item_bar_normal_font (ib
);
340 /* size all of the button so things work after a zoom */
341 for (i
= 0 ; i
< btns
->len
; i
++) {
342 GtkWidget
*btn
= g_ptr_array_index (btns
, i
);
343 GtkWidget
*label
= gtk_bin_get_child (GTK_BIN (gtk_bin_get_child (GTK_BIN (btn
))));
344 gtk_widget_set_size_request (GTK_WIDGET (btn
), w
, h
);
345 gtk_widget_override_font (label
, font_desc
);
348 pango_font_description_free (font_desc
);
349 gtk_widget_show_all (box
);
353 scg_resize (SheetControlGUI
*scg
, G_GNUC_UNUSED gboolean force_scroll
)
355 Sheet
const *sheet
= scg_sheet (scg
);
356 GnmPane
*pane
= scg_pane (scg
, 0);
357 int h
, w
, btn_h
, btn_w
, tmp
;
362 /* Recalibrate the starting offsets */
363 pane
->first_offset
.x
= scg_colrow_distance_get (scg
,
364 TRUE
, 0, pane
->first
.col
);
365 pane
->first_offset
.y
= scg_colrow_distance_get (scg
,
366 FALSE
, 0, pane
->first
.row
);
368 /* resize Pane[0] headers */
369 h
= gnm_item_bar_calc_size (scg
->pane
[0]->col
.item
);
370 btn_h
= h
- gnm_item_bar_indent (scg
->pane
[0]->col
.item
);
371 w
= gnm_item_bar_calc_size (scg
->pane
[0]->row
.item
);
372 btn_w
= w
- gnm_item_bar_indent (scg
->pane
[0]->row
.item
);
373 gtk_widget_set_size_request (scg
->select_all_btn
, btn_w
, btn_h
);
374 gtk_widget_set_size_request (GTK_WIDGET (scg
->pane
[0]->col
.canvas
), -1, h
);
375 gtk_widget_set_size_request (GTK_WIDGET (scg
->pane
[0]->row
.canvas
), w
, -1);
377 tmp
= gnm_item_bar_group_size (scg
->pane
[0]->col
.item
,
378 sheet
->cols
.max_outline_level
);
379 scg_setup_group_buttons (scg
, sheet
->cols
.max_outline_level
,
380 scg
->pane
[0]->col
.item
, TRUE
,
381 tmp
, tmp
, scg
->col_group
.buttons
, scg
->col_group
.button_box
);
382 scg_setup_group_buttons (scg
, sheet
->rows
.max_outline_level
,
383 scg
->pane
[0]->row
.item
, FALSE
,
384 -1, btn_h
, scg
->row_group
.buttons
, scg
->row_group
.button_box
);
386 if (scg
->active_panes
!= 1 && gnm_sheet_view_is_frozen (scg_view (scg
))) {
387 GnmCellPos
const *tl
= &scg_view (scg
)->frozen_top_left
;
388 GnmCellPos
const *br
= &scg_view (scg
)->unfrozen_top_left
;
389 int const l
= scg_colrow_distance_get (scg
, TRUE
,
391 int const r
= scg_colrow_distance_get (scg
, TRUE
,
392 tl
->col
, br
->col
) + l
;
393 int const t
= scg_colrow_distance_get (scg
, FALSE
,
395 int const b
= scg_colrow_distance_get (scg
, FALSE
,
396 tl
->row
, br
->row
) + t
;
398 int fw
= MIN (scg
->screen_width
, r
- l
);
399 int fh
= MIN (scg
->screen_height
, b
- t
);
401 /* pane 0 has already been done */
402 for (i
= scg
->active_panes
; i
-- > 1 ; ) {
403 GnmPane
*pane
= scg
->pane
[i
];
405 pane
->first_offset
.x
= scg_colrow_distance_get (
406 scg
, TRUE
, 0, pane
->first
.col
);
407 pane
->first_offset
.y
= scg_colrow_distance_get (
408 scg
, FALSE
, 0, pane
->first
.row
);
413 if (gnm_debug_flag ("frozen-panes"))
414 g_printerr ("Pane 1: %d\n", r
- l
);
416 gtk_widget_set_size_request (GTK_WIDGET (scg
->pane
[1]), fw
, -1);
417 /* The item_bar_calcs should be equal */
418 /* FIXME : The canvas gets confused when the initial scroll
419 * region is set too early in its life cycle.
420 * It likes it to be at the origin, we can live with that for now.
421 * However, we really should track the bug eventually.
423 h
= gnm_item_bar_calc_size (scg
->pane
[1]->col
.item
);
424 gtk_widget_set_size_request (GTK_WIDGET (scg
->pane
[1]->col
.canvas
), fw
, h
);
428 if (gnm_debug_flag ("frozen-panes"))
429 g_printerr ("Pane 2: %d\n", b
- t
);
431 gtk_widget_set_size_request (GTK_WIDGET (scg
->pane
[3]), -1, fh
);
432 /* The item_bar_calcs should be equal */
433 w
= gnm_item_bar_calc_size (scg
->pane
[3]->row
.item
);
434 gtk_widget_set_size_request (GTK_WIDGET (scg
->pane
[3]->row
.canvas
), w
, fh
);
438 if (gnm_debug_flag ("frozen-panes"))
439 g_printerr ("Pane 3: %d %d\n", r
- l
, b
- t
);
441 gtk_widget_set_size_request (GTK_WIDGET (scg
->pane
[2]), fw
, fh
);
445 SCG_FOREACH_PANE (scg
, pane
, {
446 gnm_pane_reposition_cursors (pane
);
451 scg_resize_virt (SheetControl
*sc
, gboolean force_scroll
)
453 scg_resize ((SheetControlGUI
*)sc
, force_scroll
);
457 gnm_adjustment_configure (GtkAdjustment
*adjustment
,
461 gdouble step_increment
,
462 gdouble page_increment
,
465 g_object_freeze_notify (G_OBJECT (adjustment
));
467 // These do nothing if value isn't changed
468 gtk_adjustment_set_lower (adjustment
, lower
);
469 gtk_adjustment_set_upper (adjustment
, upper
);
470 gtk_adjustment_set_step_increment (adjustment
, step_increment
);
471 gtk_adjustment_set_page_increment (adjustment
, page_increment
);
472 gtk_adjustment_set_page_size (adjustment
, page_size
);
474 g_object_thaw_notify (G_OBJECT (adjustment
));
476 // These fire signals if nothing changes, so check by hand
477 if (!(gtk_adjustment_get_value (adjustment
) == value
))
478 gtk_adjustment_set_value (adjustment
, value
);
483 * scg_scrollbar_config:
486 * Manages the scrollbar dimensions and paging parameters.
487 * Currently sizes things based on the cols/rows visible in pane-0. This has
488 * several subtleties.
490 * 1) Using cols/rows instead of pixels means that the scrollbar changes size
491 * as it passes through regions with different sized cols/rows.
493 * 2) It does NOT take into account hidden rows/cols So a region that contains
494 * a large hidden segment will appear larger.
496 * 3) It only uses pane-0 because that is the only one that can scroll in both
497 * dimensions. The others are of fixed size.
500 scg_scrollbar_config_real (SheetControl
const *sc
)
502 SheetControlGUI
*scg
= GNM_SCG (sc
);
503 GtkAdjustment
*va
= scg
->va
;
504 GtkAdjustment
*ha
= scg
->ha
;
505 GnmPane
*pane
= scg_pane (scg
, 0);
506 SheetView
const *sv
= sc
->view
;
507 Sheet
const *sheet
= sv
->sheet
;
510 int const last_col
= pane
->last_full
.col
;
511 int const last_row
= pane
->last_full
.row
;
512 int max_col
= last_col
;
513 int max_row
= last_row
;
515 if (max_row
< sheet
->rows
.max_used
)
516 max_row
= sheet
->rows
.max_used
;
517 if (max_row
< sheet
->max_object_extent
.row
)
518 max_row
= sheet
->max_object_extent
.row
;
519 gnm_adjustment_configure
522 gnm_sheet_view_is_frozen (sv
) ? sv
->unfrozen_top_left
.row
: 0,
525 MAX (gtk_adjustment_get_page_size (va
) - 3.0, 1.0),
526 last_row
- pane
->first
.row
+ 1);
528 if (max_col
< sheet
->cols
.max_used
)
529 max_col
= sheet
->cols
.max_used
;
530 if (max_col
< sheet
->max_object_extent
.col
)
531 max_col
= sheet
->max_object_extent
.col
;
532 gnm_adjustment_configure
535 gnm_sheet_view_is_frozen (sv
) ? sv
->unfrozen_top_left
.col
: 0,
538 MAX (gtk_adjustment_get_page_size (ha
) - 3.0, 1.0),
539 last_col
- pane
->first
.col
+ 1);
542 scg
->scroll_bar_timer
= 0;
548 scg_scrollbar_config (SheetControl
*sc
)
550 SheetControlGUI
*scg
= GNM_SCG (sc
);
552 if (!scg
->scroll_bar_timer
)
553 scg
->scroll_bar_timer
=
555 (GSourceFunc
) scg_scrollbar_config_real
,
560 scg_colrow_size_set (SheetControlGUI
*scg
,
561 gboolean is_cols
, int index
, int new_size_pixels
)
563 WorkbookControl
*wbc
= scg_wbc (scg
);
564 SheetView
*sv
= scg_view (scg
);
566 /* If all cols/rows in the selection are completely selected
567 * then resize all of them, otherwise just resize the selected col/row.
569 if (!sv_is_full_colrow_selected (sv
, is_cols
, index
))
570 cmd_resize_colrow (wbc
, sv
->sheet
, is_cols
,
571 colrow_get_index_list (index
, index
, NULL
), new_size_pixels
);
573 workbook_cmd_resize_selected_colrow (wbc
, sv
->sheet
, is_cols
,
578 scg_select_all (SheetControlGUI
*scg
)
580 Sheet
*sheet
= scg_sheet (scg
);
581 gboolean
const rangesel
= wbcg_rangesel_possible (scg
->wbcg
);
584 scg_rangesel_bound (scg
,
585 0, 0, gnm_sheet_get_last_col (sheet
), gnm_sheet_get_last_row (sheet
));
586 gnm_expr_entry_signal_update (
587 wbcg_get_entry_logical (scg
->wbcg
), TRUE
);
588 } else if (wbc_gtk_get_guru (scg
->wbcg
) == NULL
) {
589 SheetView
*sv
= scg_view (scg
);
592 wbcg_edit_finish (scg
->wbcg
, WBC_EDIT_REJECT
, NULL
);
593 sv_selection_reset (sv
);
594 sv_selection_add_full (sv
, sv
->edit_pos
.col
, sv
->edit_pos
.row
,
595 0, 0, gnm_sheet_get_last_col (sheet
),
596 gnm_sheet_get_last_row (sheet
),
597 GNM_SELECTION_MODE_ADD
);
599 sheet_update (sheet
);
603 scg_colrow_select (SheetControlGUI
*scg
, gboolean is_cols
,
604 int index
, int modifiers
)
606 SheetView
*sv
= scg_view (scg
);
607 gboolean
const rangesel
= wbcg_rangesel_possible (scg
->wbcg
);
610 !wbcg_edit_finish (scg
->wbcg
, WBC_EDIT_ACCEPT
, NULL
))
613 if (modifiers
& GDK_SHIFT_MASK
) {
616 scg_rangesel_extend_to (scg
, index
, -1);
618 scg_rangesel_extend_to (scg
, -1, index
);
621 sv_selection_extend_to (sv
, index
, -1);
623 sv_selection_extend_to (sv
, -1, index
);
626 if (!rangesel
&& !(modifiers
& GDK_CONTROL_MASK
))
627 sv_selection_reset (sv
);
631 scg_rangesel_bound (scg
,
632 index
, 0, index
, gnm_sheet_get_last_row (sv
->sheet
));
634 scg_rangesel_bound (scg
,
635 0, index
, gnm_sheet_get_last_col (sv
->sheet
), index
);
636 } else if (is_cols
) {
638 scg_pane (scg
, scg
->pane
[3] ? 3 : 0);
639 sv_selection_add_full (sv
,
640 index
, pane
->first
.row
,
642 index
, gnm_sheet_get_last_row (sv
->sheet
),
643 GNM_SELECTION_MODE_ADD
);
646 scg_pane (scg
, scg
->pane
[1] ? 1 : 0);
647 sv_selection_add_full (sv
,
648 pane
->first
.col
, index
,
650 gnm_sheet_get_last_col (sv
->sheet
), index
,
651 GNM_SELECTION_MODE_ADD
);
655 /* The edit pos, and the selection may have changed */
657 sheet_update (sv
->sheet
);
661 /***************************************************************************/
664 cb_select_all_btn_draw (GtkWidget
*widget
, cairo_t
*cr
, SheetControlGUI
*scg
)
666 int offset
= scg_sheet (scg
)->text_is_rtl
? -1 : 0;
668 GtkStyleContext
*ctxt
= gtk_widget_get_style_context (widget
);
670 gtk_widget_get_allocation (widget
, &a
);
672 gtk_style_context_save (ctxt
);
673 gtk_style_context_set_state (ctxt
, GTK_STATE_FLAG_NORMAL
);
674 gtk_render_background (ctxt
, cr
, offset
+ 1, 1,
675 a
.width
- 1, a
.height
- 1);
676 gtk_render_frame (ctxt
, cr
, offset
, 0, a
.width
+ 1, a
.height
+ 1);
677 gtk_style_context_restore (ctxt
);
681 cb_select_all_btn_event (G_GNUC_UNUSED GtkWidget
*widget
, GdkEvent
*event
, SheetControlGUI
*scg
)
683 if (event
->type
== GDK_BUTTON_PRESS
) {
684 scg_select_all (scg
);
692 cb_vscrollbar_value_changed (GtkRange
*range
, SheetControlGUI
*scg
)
694 GtkAdjustment
*adj
= gtk_range_get_adjustment (range
);
695 scg_set_top_row (scg
, gtk_adjustment_get_value (adj
));
699 cb_hscrollbar_value_changed (GtkRange
*range
, SheetControlGUI
*scg
)
701 GtkAdjustment
*adj
= gtk_range_get_adjustment (range
);
702 scg_set_left_col (scg
, gtk_adjustment_get_value (adj
));
706 cb_hscrollbar_adjust_bounds (GtkRange
*range
, gdouble new_value
, Sheet
*sheet
)
708 GtkAdjustment
*adj
= gtk_range_get_adjustment (range
);
709 double upper
= gtk_adjustment_get_upper (adj
);
710 double page_size
= gtk_adjustment_get_page_size (adj
);
711 gdouble limit
= upper
- page_size
;
712 if (upper
< gnm_sheet_get_max_cols (sheet
) && new_value
>= limit
) {
713 upper
= new_value
+ page_size
+ 1;
714 if (upper
> gnm_sheet_get_max_cols (sheet
))
715 upper
= gnm_sheet_get_max_cols (sheet
);
716 gtk_adjustment_set_upper (adj
, upper
);
720 cb_vscrollbar_adjust_bounds (GtkRange
*range
, gdouble new_value
, Sheet
*sheet
)
722 GtkAdjustment
*adj
= gtk_range_get_adjustment (range
);
723 double upper
= gtk_adjustment_get_upper (adj
);
724 double page_size
= gtk_adjustment_get_page_size (adj
);
725 gdouble limit
= upper
- page_size
;
726 if (upper
< gnm_sheet_get_max_rows (sheet
) && new_value
>= limit
) {
727 upper
= new_value
+ page_size
+ 1;
728 if (upper
> gnm_sheet_get_max_rows (sheet
))
729 upper
= gnm_sheet_get_max_rows (sheet
);
730 gtk_adjustment_set_upper (adj
, upper
);
735 cb_table_destroy (SheetControlGUI
*scg
)
737 SheetControl
*sc
= (SheetControl
*) scg
;
740 g_clear_object (&scg
->grid
);
742 scg_mode_edit (scg
); /* finish any object edits */
743 scg_unant (sc
); /* Make sure that everything is unanted */
746 GtkWindow
*toplevel
= wbcg_toplevel (scg
->wbcg
);
748 /* Only pane-0 ever gets focus */
749 if (NULL
!= toplevel
&&
750 gtk_window_get_focus (toplevel
) == GTK_WIDGET (scg_pane (scg
, 0)))
751 gtk_window_set_focus (toplevel
, NULL
);
754 for (i
= scg
->active_panes
; i
-- > 0 ; )
755 if (NULL
!= scg
->pane
[i
]) {
756 gtk_widget_destroy (GTK_WIDGET (scg
->pane
[i
]));
760 g_object_unref (scg
);
764 scg_init (SheetControlGUI
*scg
)
766 scg
->comment
.selected
= NULL
;
767 scg
->comment
.item
= NULL
;
768 scg
->comment
.timer
= 0;
770 scg
->delayedMovement
.timer
= 0;
771 scg
->delayedMovement
.handler
= NULL
;
774 scg
->selected_objects
= NULL
;
779 // These shouldn't matter and will be overwritten
780 scg
->screen_width
= 1920;
781 scg
->screen_height
= 1200;
784 /*************************************************************************/
787 * gnm_pane_update_inital_top_left:
788 * A convenience routine to store the new topleft back in the view.
791 gnm_pane_update_inital_top_left (GnmPane
const *pane
)
793 if (pane
->index
== 0) {
794 SheetView
*sv
= scg_view (pane
->simple
.scg
);
795 sv
->initial_top_left
= pane
->first
;
800 bar_set_left_col (GnmPane
*pane
, int new_first_col
)
806 col_offset
= pane
->first_offset
.x
+=
807 scg_colrow_distance_get (pane
->simple
.scg
, TRUE
, pane
->first
.col
, new_first_col
);
808 pane
->first
.col
= new_first_col
;
810 /* Scroll the column headers */
811 if (NULL
!= (colc
= pane
->col
.canvas
))
812 goc_canvas_scroll_to (colc
, col_offset
/ colc
->pixels_per_unit
, 0);
818 gnm_pane_set_left_col (GnmPane
*pane
, int new_first_col
)
821 g_return_if_fail (pane
!= NULL
);
822 sheet
= scg_sheet (pane
->simple
.scg
);
823 g_return_if_fail (0 <= new_first_col
&& new_first_col
< gnm_sheet_get_max_cols (sheet
));
825 if (pane
->first
.col
!= new_first_col
) {
826 GocCanvas
* const canvas
= GOC_CANVAS (pane
);
827 gint64
const col_offset
= bar_set_left_col (pane
, new_first_col
);
829 gnm_pane_compute_visible_region (pane
, FALSE
);
830 goc_canvas_scroll_to (canvas
, col_offset
/ canvas
->pixels_per_unit
, pane
->first_offset
.y
/ canvas
->pixels_per_unit
);
831 gnm_pane_update_inital_top_left (pane
);
836 scg_set_left_col (SheetControlGUI
*scg
, int col
)
839 GnmRange
const *bound
;
841 g_return_if_fail (GNM_IS_SCG (scg
));
843 sheet
= scg_sheet (scg
);
844 bound
= &sheet
->priv
->unhidden_region
;
845 if (col
< bound
->start
.col
)
846 col
= bound
->start
.col
;
847 else if (col
>= gnm_sheet_get_max_cols (sheet
))
848 col
= gnm_sheet_get_last_col (sheet
);
849 else if (col
> bound
->end
.col
)
850 col
= bound
->end
.col
;
853 int right
= scg_view (scg
)->unfrozen_top_left
.col
;
858 gnm_pane_set_left_col (scg_pane (scg
, 3), col
);
859 gnm_pane_set_left_col (scg_pane (scg
, 0), col
);
863 bar_set_top_row (GnmPane
*pane
, int new_first_row
)
868 row_offset
= pane
->first_offset
.y
+=
869 scg_colrow_distance_get (pane
->simple
.scg
, FALSE
, pane
->first
.row
, new_first_row
);
870 pane
->first
.row
= new_first_row
;
872 /* Scroll the row headers */
873 if (NULL
!= (rowc
= pane
->row
.canvas
))
874 goc_canvas_scroll_to (rowc
, 0, row_offset
/ rowc
->pixels_per_unit
);
880 gnm_pane_set_top_row (GnmPane
*pane
, int new_first_row
)
883 g_return_if_fail (pane
!= NULL
);
884 sheet
= scg_sheet (pane
->simple
.scg
);
885 g_return_if_fail (0 <= new_first_row
&& new_first_row
< gnm_sheet_get_max_rows (sheet
));
887 if (pane
->first
.row
!= new_first_row
) {
888 GocCanvas
* const canvas
= GOC_CANVAS(pane
);
889 gint64
const row_offset
= bar_set_top_row (pane
, new_first_row
);
890 gint64 col_offset
= pane
->first_offset
.x
;
892 gnm_pane_compute_visible_region (pane
, FALSE
);
893 goc_canvas_scroll_to (canvas
, col_offset
/ canvas
->pixels_per_unit
, row_offset
/ canvas
->pixels_per_unit
);
894 gnm_pane_update_inital_top_left (pane
);
899 scg_set_top_row (SheetControlGUI
*scg
, int row
)
902 GnmRange
const *bound
;
904 g_return_if_fail (GNM_IS_SCG (scg
));
906 sheet
= scg_sheet (scg
);
907 bound
= &sheet
->priv
->unhidden_region
;
908 if (row
< bound
->start
.row
)
909 row
= bound
->start
.row
;
910 else if (row
>= gnm_sheet_get_max_rows (sheet
))
911 row
= gnm_sheet_get_last_row (sheet
);
912 else if (row
> bound
->end
.row
)
913 row
= bound
->end
.row
;
916 int bottom
= scg_view (scg
)->unfrozen_top_left
.row
;
921 gnm_pane_set_top_row (scg_pane (scg
, 1), row
);
922 gnm_pane_set_top_row (scg_pane (scg
, 0), row
);
926 gnm_pane_set_top_left (GnmPane
*pane
,
927 int col
, int row
, gboolean force_scroll
)
929 gboolean changed
= FALSE
;
930 gint64 col_offset
, row_offset
;
933 g_return_if_fail (0 <= col
&&
934 col
< gnm_sheet_get_max_cols (scg_sheet (pane
->simple
.scg
)));
935 g_return_if_fail (0 <= row
&&
936 row
< gnm_sheet_get_max_rows (scg_sheet (pane
->simple
.scg
)));
938 if (pane
->first
.col
!= col
|| force_scroll
) {
940 /* Clear the offsets in case col/row size changed */
941 pane
->first_offset
.x
= 0;
945 col_offset
= bar_set_left_col (pane
, col
);
948 col_offset
= pane
->first_offset
.x
;
951 if (pane
->first
.row
!= row
|| force_scroll
) {
953 /* Clear the offsets in case col/row size changed */
954 pane
->first_offset
.y
= 0;
957 row_offset
= bar_set_top_row (pane
, row
);
960 row_offset
= pane
->first_offset
.y
;
965 gnm_pane_compute_visible_region (pane
, force_scroll
);
966 canvas
= GOC_CANVAS (pane
);
967 goc_canvas_scroll_to (canvas
, col_offset
/ canvas
->pixels_per_unit
, row_offset
/ canvas
->pixels_per_unit
);
968 gnm_pane_update_inital_top_left (pane
);
972 scg_set_top_left (SheetControl
*sc
, int col
, int row
)
974 SheetControlGUI
*scg
= (SheetControlGUI
*)sc
;
976 g_return_if_fail (GNM_IS_SCG (scg
));
980 /* We could be faster if necessary */
981 scg_set_left_col (scg
, col
);
982 scg_set_top_row (scg
, row
);
986 gnm_pane_make_cell_visible (GnmPane
*pane
, int col
, int row
,
987 gboolean
const force_scroll
)
991 int new_first_col
, new_first_row
;
995 g_return_if_fail (GNM_IS_PANE (pane
));
997 /* Avoid calling this before the canvas is realized: We do not know the
998 * visible area, and would unconditionally scroll the cell to the top
999 * left of the viewport.
1001 if (!gtk_widget_get_realized (GTK_WIDGET (pane
)))
1004 sheet
= scg_sheet (pane
->simple
.scg
);
1005 g_return_if_fail (col
>= 0);
1006 g_return_if_fail (row
>= 0);
1007 g_return_if_fail (col
< gnm_sheet_get_max_cols (sheet
));
1008 g_return_if_fail (row
< gnm_sheet_get_max_rows (sheet
));
1010 canvas
= GOC_CANVAS (pane
);
1011 range
.start
.col
= range
.end
.col
= col
;
1012 range
.start
.row
= range
.end
.row
= row
;
1013 gnm_sheet_merge_find_bounding_box (sheet
, &range
);
1015 gtk_widget_get_allocation (GTK_WIDGET (canvas
), &ca
);
1017 /* Find the new pane->first.col */
1018 if (range
.start
.col
< pane
->first
.col
) {
1019 new_first_col
= range
.start
.col
;
1020 } else if (range
.end
.col
> pane
->last_full
.col
) {
1021 int width
= ca
.width
;
1022 ColRowInfo
const * const ci
= sheet_col_get_info (sheet
, range
.end
.col
);
1023 if (ci
->size_pixels
< width
) {
1024 int first_col
= (pane
->last_visible
.col
== pane
->first
.col
)
1025 ? pane
->first
.col
: range
.end
.col
;
1027 for (; first_col
> 0; --first_col
) {
1028 ColRowInfo
const * const ci
= sheet_col_get_info (sheet
, first_col
);
1030 width
-= ci
->size_pixels
;
1035 new_first_col
= first_col
+1;
1036 if (new_first_col
> range
.start
.col
)
1037 new_first_col
= range
.start
.col
;
1039 new_first_col
= col
;
1041 new_first_col
= pane
->first
.col
;
1043 /* Find the new pane->first.row */
1044 if (range
.start
.row
< pane
->first
.row
) {
1045 new_first_row
= range
.start
.row
;
1046 } else if (range
.end
.row
> pane
->last_full
.row
) {
1047 int height
= ca
.height
;
1048 ColRowInfo
const * const ri
= sheet_row_get_info (sheet
, range
.end
.row
);
1049 if (ri
->size_pixels
< height
) {
1050 int first_row
= (pane
->last_visible
.row
== pane
->first
.row
)
1051 ? pane
->first
.row
: range
.end
.row
;
1053 for (; first_row
> 0; --first_row
) {
1054 ColRowInfo
const * const ri
= sheet_row_get_info (sheet
, first_row
);
1056 height
-= ri
->size_pixels
;
1061 new_first_row
= first_row
+1;
1062 if (new_first_row
> range
.start
.row
)
1063 new_first_row
= range
.start
.row
;
1065 new_first_row
= row
;
1067 new_first_row
= pane
->first
.row
;
1069 gnm_pane_set_top_left (pane
, new_first_col
, new_first_row
,
1074 * scg_make_cell_visible:
1075 * @scg: The gui control
1078 * @force_scroll: Completely recalibrate the offsets to the new position
1079 * @couple_panes: Scroll scroll dynamic panes back to bounds if target
1080 * is in frozen segment.
1082 * Ensure that cell (col, row) is visible.
1083 * Sheet is scrolled if cell is outside viewport.
1086 scg_make_cell_visible (SheetControlGUI
*scg
, int col
, int row
,
1087 gboolean force_scroll
, gboolean couple_panes
)
1089 SheetView
const *sv
= scg_view (scg
);
1090 GnmCellPos
const *tl
, *br
;
1092 g_return_if_fail (GNM_IS_SCG (scg
));
1094 if (!scg
->active_panes
)
1097 tl
= &sv
->frozen_top_left
;
1098 br
= &sv
->unfrozen_top_left
;
1099 if (col
< br
->col
) {
1100 if (row
>= br
->row
) { /* pane 1 */
1103 gnm_pane_make_cell_visible (scg
->pane
[1],
1104 col
, row
, force_scroll
);
1105 gnm_pane_set_top_left (scg
->pane
[0],
1106 couple_panes
? br
->col
: scg
->pane
[0]->first
.col
,
1107 scg
->pane
[1]->first
.row
,
1109 if (couple_panes
&& scg
->pane
[3])
1110 gnm_pane_set_left_col (scg
->pane
[3], br
->col
);
1111 } else if (couple_panes
) { /* pane 2 */
1112 /* FIXME : We may need to change the way this routine
1113 * is used to fix this. Because we only know what the
1114 * target cell is we cannot absolutely differentiate
1115 * between col & row scrolling. For now use the
1116 * heuristic that if the col was visible this is a
1119 if (scg
->pane
[2]->first
.col
<= col
&&
1120 scg
->pane
[2]->last_visible
.col
>= col
) {
1121 scg_set_top_row (scg
, row
);
1123 scg_set_left_col (scg
, col
);
1125 } else if (row
< br
->row
) { /* pane 3 */
1128 gnm_pane_make_cell_visible (scg
->pane
[3],
1129 col
, row
, force_scroll
);
1130 gnm_pane_set_top_left (scg
->pane
[0],
1131 scg
->pane
[3]->first
.col
,
1134 : scg
->pane
[0]->first
.row
,
1136 if (couple_panes
&& scg
->pane
[1])
1137 gnm_pane_set_top_row (scg
->pane
[1],
1139 } else { /* pane 0 */
1140 gnm_pane_make_cell_visible (scg
->pane
[0],
1141 col
, row
, force_scroll
);
1143 gnm_pane_set_top_left (scg
->pane
[1],
1144 tl
->col
, scg
->pane
[0]->first
.row
, force_scroll
);
1146 gnm_pane_set_top_left (scg
->pane
[3],
1147 scg
->pane
[0]->first
.col
, tl
->row
, force_scroll
);
1150 gnm_pane_set_top_left (scg
->pane
[2],
1151 tl
->col
, tl
->row
, force_scroll
);
1155 scg_make_cell_visible_virt (SheetControl
*sc
, int col
, int row
,
1156 gboolean couple_panes
)
1158 scg_make_cell_visible ((SheetControlGUI
*)sc
, col
, row
,
1159 FALSE
, couple_panes
);
1162 /*************************************************************************/
1165 scg_set_panes (SheetControl
*sc
)
1167 SheetControlGUI
*scg
= (SheetControlGUI
*) sc
;
1168 SheetView
*sv
= sc
->view
;
1169 gboolean
const being_frozen
= gnm_sheet_view_is_frozen (sv
);
1170 GocDirection direction
= (sv_sheet (sv
)->text_is_rtl
)? GOC_DIRECTION_RTL
: GOC_DIRECTION_LTR
;
1172 g_return_if_fail (GNM_IS_SHEET_VIEW (sv
));
1177 GnmCellPos
const *tl
= &sv
->frozen_top_left
;
1178 GnmCellPos
const *br
= &sv
->unfrozen_top_left
;
1179 gboolean
const freeze_h
= br
->col
> tl
->col
;
1180 gboolean
const freeze_v
= br
->row
> tl
->row
;
1182 gnm_pane_bound_set (scg
->pane
[0],
1184 gnm_sheet_get_last_col (sv
->sheet
), gnm_sheet_get_last_row (sv
->sheet
));
1187 scg
->active_panes
= 2;
1188 if (!scg
->pane
[1]) {
1189 scg
->pane
[1] = gnm_pane_new (scg
, TRUE
, FALSE
, 1);
1190 gnm_pane_set_direction (scg
->pane
[1], direction
);
1191 gtk_grid_attach (scg
->grid
,
1192 GTK_WIDGET (scg
->pane
[1]),
1194 gtk_grid_attach (scg
->grid
,
1195 GTK_WIDGET (scg
->pane
[1]->col
.canvas
),
1198 gnm_pane_bound_set (scg
->pane
[1],
1199 tl
->col
, br
->row
, br
->col
- 1, gnm_sheet_get_last_row (sv
->sheet
));
1201 if (freeze_h
&& freeze_v
) {
1202 scg
->active_panes
= 4;
1203 if (!scg
->pane
[2]) {
1204 scg
->pane
[2] = gnm_pane_new (scg
, FALSE
, FALSE
, 2);
1205 gnm_pane_set_direction (scg
->pane
[2], direction
);
1206 gtk_grid_attach (scg
->grid
,
1207 GTK_WIDGET (scg
->pane
[2]),
1210 gnm_pane_bound_set (scg
->pane
[2],
1211 tl
->col
, tl
->row
, br
->col
- 1, br
->row
- 1);
1214 scg
->active_panes
= 4;
1215 if (!scg
->pane
[3]) {
1216 scg
->pane
[3] = gnm_pane_new (scg
, FALSE
, TRUE
, 3);
1217 gnm_pane_set_direction (scg
->pane
[3], direction
);
1218 gtk_grid_attach (scg
->grid
,
1219 GTK_WIDGET (scg
->pane
[3]),
1221 gtk_grid_attach (scg
->grid
,
1222 GTK_WIDGET (scg
->pane
[3]->row
.canvas
),
1225 gnm_pane_bound_set (scg
->pane
[3],
1226 br
->col
, tl
->row
, gnm_sheet_get_last_col (sv
->sheet
), br
->row
- 1);
1230 for (i
= 1 ; i
<= 3 ; i
++)
1232 gtk_widget_destroy (GTK_WIDGET (scg
->pane
[i
]));
1233 scg
->pane
[i
] = NULL
;
1236 scg
->active_panes
= 1;
1237 gnm_pane_bound_set (scg
->pane
[0],
1238 0, 0, gnm_sheet_get_last_col (sv
->sheet
), gnm_sheet_get_last_row (sv
->sheet
));
1241 gtk_widget_show_all (GTK_WIDGET (scg
->grid
));
1243 /* in case headers are hidden */
1244 scg_adjust_preferences (scg
);
1245 scg_resize (scg
, TRUE
);
1248 GnmCellPos
const *tl
= &sc
->view
->frozen_top_left
;
1251 gnm_pane_set_left_col (scg
->pane
[1], tl
->col
);
1253 gnm_pane_set_top_left (scg
->pane
[2], tl
->col
, tl
->row
, TRUE
);
1255 gnm_pane_set_top_row (scg
->pane
[3], tl
->row
);
1257 set_resize_pane_pos (scg
, scg
->vpane
);
1258 set_resize_pane_pos (scg
, scg
->hpane
);
1262 cb_wbc_destroyed (SheetControlGUI
*scg
)
1265 scg
->sheet_control
.wbc
= NULL
;
1269 cb_scg_redraw (SheetControlGUI
*scg
)
1271 scg_adjust_preferences (scg
);
1272 scg_redraw_all (&scg
->sheet_control
, TRUE
);
1276 cb_scg_redraw_resize (SheetControlGUI
*scg
)
1278 cb_scg_redraw (scg
);
1279 scg_resize (scg
, FALSE
);
1283 cb_scg_sheet_resized (SheetControlGUI
*scg
)
1285 cb_scg_redraw_resize (scg
);
1286 sc_set_panes (&scg
->sheet_control
);
1290 cb_scg_direction_changed (SheetControlGUI
*scg
)
1292 /* set direction in the canvas */
1293 int i
= scg
->active_panes
;
1295 GnmPane
*pane
= scg
->pane
[i
];
1297 gnm_pane_set_direction (scg
->pane
[i
],
1298 scg_sheet (scg
)->text_is_rtl
? GOC_DIRECTION_RTL
: GOC_DIRECTION_LTR
);
1300 scg_resize (scg
, TRUE
);
1303 static GnmPane
const *
1304 resize_pane_pos (SheetControlGUI
*scg
, GtkPaned
*p
,
1305 int *colrow_result
, gint64
*guide_pos
)
1307 ColRowInfo
const *cri
;
1308 GnmPane
const *pane
= scg_pane (scg
, 0);
1309 gboolean
const vert
= (p
== scg
->hpane
);
1311 gint64 pos
= gtk_paned_get_position (p
);
1313 gtk_widget_style_get (GTK_WIDGET (p
), "handle-size", &handle
, NULL
);
1316 if (gtk_widget_get_visible (GTK_WIDGET (pane
->row
.canvas
))) {
1318 gtk_widget_get_allocation (GTK_WIDGET (pane
->row
.canvas
), &ca
);
1323 gtk_widget_get_allocation (GTK_WIDGET (scg
->pane
[1]),
1327 pane
= scg_pane (scg
, 1);
1332 pos
+= pane
->first_offset
.x
;
1333 colrow
= gnm_pane_find_col (pane
, pos
, guide_pos
);
1335 if (gtk_widget_get_visible (GTK_WIDGET (pane
->col
.canvas
))) {
1337 gtk_widget_get_allocation (GTK_WIDGET (pane
->col
.canvas
), &ca
);
1342 gtk_widget_get_allocation (GTK_WIDGET (scg
->pane
[3]),
1344 if (pos
< pa
.height
)
1345 pane
= scg_pane (scg
, 3);
1350 pos
+= pane
->first_offset
.y
;
1351 colrow
= gnm_pane_find_row (pane
, pos
, guide_pos
);
1353 cri
= sheet_colrow_get_info (scg_sheet (scg
), colrow
, vert
);
1354 if (pos
>= (*guide_pos
+ cri
->size_pixels
/ 2)) {
1355 *guide_pos
+= cri
->size_pixels
;
1358 if (NULL
!= colrow_result
)
1359 *colrow_result
= colrow
;
1365 scg_gtk_paned_set_position (SheetControlGUI
*scg
, GtkPaned
*p
, int pane_pos
)
1367 /* A negative position is special to GtkPaned. */
1368 pane_pos
= MAX (pane_pos
, 0);
1370 if (p
== scg
->vpane
)
1371 scg
->vpos
= pane_pos
;
1373 scg
->hpos
= pane_pos
;
1375 gtk_paned_set_position (p
, pane_pos
);
1379 set_resize_pane_pos (SheetControlGUI
*scg
, GtkPaned
*p
)
1381 int handle_size
, pane_pos
, size
;
1382 GnmPane
*pane0
= scg
->pane
[0];
1387 if (p
== scg
->vpane
) {
1388 if (gtk_widget_get_visible (GTK_WIDGET (pane0
->col
.canvas
))) {
1389 GtkAllocation alloc
;
1390 gtk_widget_get_allocation (GTK_WIDGET (pane0
->col
.canvas
), &alloc
);
1391 pane_pos
= alloc
.height
;
1395 gtk_widget_get_size_request (
1396 GTK_WIDGET (scg
->pane
[3]), NULL
, &size
);
1400 if (gtk_widget_get_visible (GTK_WIDGET (pane0
->row
.canvas
))) {
1401 GtkAllocation alloc
;
1402 gtk_widget_get_allocation (GTK_WIDGET (pane0
->row
.canvas
), &alloc
);
1403 pane_pos
= alloc
.width
;
1407 gtk_widget_get_size_request (
1408 GTK_WIDGET (scg
->pane
[1]), &size
, NULL
);
1412 gtk_widget_style_get (GTK_WIDGET (p
), "handle-size", &handle_size
, NULL
);
1413 pane_pos
-= handle_size
/ 2;
1415 g_signal_handlers_block_by_func (G_OBJECT (p
),
1416 G_CALLBACK (cb_resize_pane_motion
), scg
);
1417 scg_gtk_paned_set_position (scg
, p
, pane_pos
);
1418 g_signal_handlers_unblock_by_func (G_OBJECT (p
),
1419 G_CALLBACK (cb_resize_pane_motion
), scg
);
1423 cb_check_resize (GtkPaned
*p
, GtkAllocation
*allocation
, SheetControlGUI
*scg
);
1426 resize_pane_finish (SheetControlGUI
*scg
, GtkPaned
*p
)
1428 SheetView
*sv
= scg_view (scg
);
1429 GnmCellPos frozen_tl
, unfrozen_tl
;
1430 GnmPane
const *pane
;
1434 #warning GTK3: replace this?
1439 pane
= resize_pane_pos (scg
, p
, &colrow
, &guide_pos
);
1441 if (gnm_sheet_view_is_frozen (sv
)) {
1442 frozen_tl
= sv
->frozen_top_left
;
1443 unfrozen_tl
= sv
->unfrozen_top_left
;
1445 frozen_tl
= pane
->first
;
1446 if (p
== scg
->hpane
) {
1447 unfrozen_tl
.col
= colrow
;
1448 if (!gnm_sheet_view_is_frozen (sv
))
1449 unfrozen_tl
.row
= frozen_tl
.row
= 0;
1451 unfrozen_tl
.row
= colrow
;
1452 if (!gnm_sheet_view_is_frozen (sv
))
1453 unfrozen_tl
.col
= frozen_tl
.col
= 0;
1455 gnm_sheet_view_freeze_panes (sv
, &frozen_tl
, &unfrozen_tl
);
1457 scg
->pane_drag_handler
= 0;
1458 scg_size_guide_stop (scg
);
1460 set_resize_pane_pos (scg
, p
);
1462 g_signal_handlers_unblock_by_func
1464 G_CALLBACK (cb_check_resize
), scg
);
1469 cb_resize_vpane_finish (SheetControlGUI
*scg
)
1471 return resize_pane_finish (scg
, scg
->vpane
);
1474 cb_resize_hpane_finish (SheetControlGUI
*scg
)
1476 return resize_pane_finish (scg
, scg
->hpane
);
1480 cb_resize_pane_motion (GtkPaned
*p
,
1481 G_GNUC_UNUSED GParamSpec
*pspec
,
1482 SheetControlGUI
*scg
)
1484 gboolean
const vert
= (p
== scg
->hpane
);
1488 resize_pane_pos (scg
, p
, &colrow
, &guide_pos
);
1489 #warning GTK3: what replaces p->in_drag?
1490 if (scg
->pane_drag_handler
== 0/* && p->in_drag*/) {
1491 g_signal_handlers_block_by_func
1493 G_CALLBACK (cb_check_resize
), scg
);
1494 scg_size_guide_start (scg
, vert
, colrow
, FALSE
);
1495 scg
->pane_drag_handler
= g_timeout_add (250,
1496 vert
? (GSourceFunc
) cb_resize_hpane_finish
1497 : (GSourceFunc
) cb_resize_vpane_finish
,
1500 if (scg
->pane_drag_handler
)
1501 scg_size_guide_motion (scg
, vert
, guide_pos
);
1506 cb_check_resize (GtkPaned
*p
, G_GNUC_UNUSED GtkAllocation
*allocation
,
1507 SheetControlGUI
*scg
)
1509 gboolean
const vert
= (p
== scg
->vpane
);
1510 gint max
, pos
= vert
? scg
->vpos
: scg
->hpos
;
1512 g_object_get (G_OBJECT (p
), "max-position", &max
, NULL
);
1516 if (gtk_paned_get_position (p
) != pos
) {
1517 g_signal_handlers_block_by_func
1519 G_CALLBACK (cb_resize_pane_motion
), scg
);
1520 gtk_paned_set_position (p
, pos
);
1521 g_signal_handlers_unblock_by_func
1523 G_CALLBACK (cb_resize_pane_motion
), scg
);
1527 struct resize_closure
{
1529 SheetControlGUI
*scg
;
1533 idle_resize (struct resize_closure
*r
)
1536 set_resize_pane_pos (r
->scg
, r
->p
);
1542 cb_canvas_resize (GtkWidget
*w
, G_GNUC_UNUSED GtkAllocation
*allocation
,
1543 SheetControlGUI
*scg
)
1545 struct resize_closure
*r
= g_new (struct resize_closure
, 1);
1547 r
->p
= (w
== GTK_WIDGET (scg
->pane
[0]->col
.canvas
))? scg
->hpane
: scg
->vpane
;
1548 /* The allocation is not correct at this point, weird */
1549 g_idle_add ((GSourceFunc
) idle_resize
, r
);
1553 post_create_cb (SheetControlGUI
*scg
)
1555 Sheet
*sheet
= sc_sheet (GNM_SHEET_CONTROL (scg
));
1556 if (sheet
->sheet_objects
)
1557 scg_object_select (scg
, (SheetObject
*) sheet
->sheet_objects
->data
);
1562 sheet_object_key_pressed (G_GNUC_UNUSED GtkWidget
*w
, GdkEventKey
*event
, SheetControlGUI
*scg
)
1564 Sheet
*sheet
= scg_sheet (scg
);
1565 WorkbookControl
* wbc
= scg_wbc (scg
);
1566 Workbook
* wb
= wb_control_get_workbook (wbc
);
1567 switch (event
->keyval
) {
1568 case GDK_KEY_KP_Page_Up
:
1569 case GDK_KEY_Page_Up
:
1570 if ((event
->state
& GDK_CONTROL_MASK
) != 0){
1571 if ((event
->state
& GDK_SHIFT_MASK
) != 0){
1572 WorkbookSheetState
* old_state
= workbook_sheet_state_new(wb
);
1573 int old_pos
= sheet
->index_in_wb
;
1576 workbook_sheet_move(sheet
, -1);
1577 cmd_reorganize_sheets (wbc
, old_state
, sheet
);
1580 gnm_notebook_prev_page (scg
->wbcg
->bnotebook
);
1585 case GDK_KEY_KP_Page_Down
:
1586 case GDK_KEY_Page_Down
:
1588 if ((event
->state
& GDK_CONTROL_MASK
) != 0){
1589 if ((event
->state
& GDK_SHIFT_MASK
) != 0){
1590 WorkbookSheetState
* old_state
= workbook_sheet_state_new(wb
);
1591 int num_sheets
= workbook_sheet_count(wb
);
1592 gint old_pos
= sheet
->index_in_wb
;
1594 if (old_pos
< num_sheets
- 1){
1595 workbook_sheet_move(sheet
, 1);
1596 cmd_reorganize_sheets (wbc
, old_state
, sheet
);
1599 gnm_notebook_next_page (scg
->wbcg
->bnotebook
);
1609 cb_screen_changed (GtkWidget
*widget
, G_GNUC_UNUSED GdkScreen
*prev
,
1610 SheetControlGUI
*scg
)
1612 GdkScreen
*screen
= gtk_widget_get_screen (widget
);
1615 scg
->screen_width
= gdk_screen_get_width (screen
);
1616 scg
->screen_height
= gdk_screen_get_height (screen
);
1621 sheet_control_gui_new (SheetView
*sv
, WBCGtk
*wbcg
)
1623 SheetControlGUI
*scg
;
1625 GocDirection direction
;
1626 GdkRGBA cfore
, cback
;
1628 g_return_val_if_fail (GNM_IS_SHEET_VIEW (sv
), NULL
);
1630 sheet
= sv_sheet (sv
);
1631 direction
= (sheet
->text_is_rtl
)? GOC_DIRECTION_RTL
: GOC_DIRECTION_LTR
;
1633 scg
= g_object_new (GNM_SCG_TYPE
, NULL
);
1635 scg
->sheet_control
.wbc
= GNM_WBC (wbcg
);
1637 g_object_weak_ref (G_OBJECT (wbcg
),
1638 (GWeakNotify
) cb_wbc_destroyed
,
1641 if (sheet
->sheet_type
== GNM_SHEET_DATA
) {
1642 scg
->active_panes
= 1;
1643 scg
->pane
[0] = NULL
;
1644 scg
->pane
[1] = NULL
;
1645 scg
->pane
[2] = NULL
;
1646 scg
->pane
[3] = NULL
;
1647 scg
->pane_drag_handler
= 0;
1649 scg
->col_group
.buttons
= g_ptr_array_new ();
1650 scg
->row_group
.buttons
= g_ptr_array_new ();
1651 scg
->col_group
.button_box
= gtk_box_new (GTK_ORIENTATION_VERTICAL
, 0);
1652 g_object_set (scg
->col_group
.button_box
,
1653 "halign", GTK_ALIGN_CENTER
,
1654 "homogeneous", TRUE
,
1656 scg
->row_group
.button_box
= gtk_box_new (GTK_ORIENTATION_HORIZONTAL
, 0);
1657 g_object_set (scg
->row_group
.button_box
,
1658 "valign", GTK_ALIGN_CENTER
,
1659 "homogeneous", TRUE
,
1661 scg
->select_all_btn
= gtk_drawing_area_new ();
1662 gtk_style_context_add_class (gtk_widget_get_style_context (scg
->select_all_btn
),
1663 GTK_STYLE_CLASS_BUTTON
);
1664 gtk_style_context_add_class (gtk_widget_get_style_context (scg
->select_all_btn
),
1666 gtk_widget_add_events (scg
->select_all_btn
, GDK_BUTTON_PRESS_MASK
);
1667 g_signal_connect (G_OBJECT (scg
->select_all_btn
), "draw",
1668 G_CALLBACK (cb_select_all_btn_draw
), scg
);
1669 g_signal_connect (G_OBJECT (scg
->select_all_btn
), "event",
1670 G_CALLBACK (cb_select_all_btn_event
), scg
);
1672 scg
->grid
= GTK_GRID (gtk_grid_new ());
1673 gtk_grid_attach (scg
->grid
, scg
->col_group
.button_box
,
1675 gtk_grid_attach (scg
->grid
, scg
->row_group
.button_box
,
1677 gtk_grid_attach (scg
->grid
, scg
->select_all_btn
, 1, 1, 1, 1);
1679 scg
->pane
[1] = scg
->pane
[2] = scg
->pane
[3] = NULL
;
1680 scg
->pane
[0] = gnm_pane_new (scg
, TRUE
, TRUE
, 0);
1681 gnm_pane_set_direction (scg
->pane
[0], direction
);
1682 gtk_grid_attach (scg
->grid
,
1683 GTK_WIDGET (scg
->pane
[0]->col
.canvas
),
1685 gtk_grid_attach (scg
->grid
,
1686 GTK_WIDGET (scg
->pane
[0]->row
.canvas
),
1688 g_object_set (scg
->pane
[0],
1692 gtk_grid_attach (scg
->grid
, GTK_WIDGET (scg
->pane
[0]),
1694 g_signal_connect_after (G_OBJECT (scg
->pane
[0]->col
.canvas
), "size-allocate",
1695 G_CALLBACK (cb_canvas_resize
), scg
);
1696 g_signal_connect_after (G_OBJECT (scg
->pane
[0]->row
.canvas
), "size-allocate",
1697 G_CALLBACK (cb_canvas_resize
), scg
);
1699 scg
->va
= (GtkAdjustment
*)gtk_adjustment_new (0., 0., 1, 1., 1., 1.);
1700 scg
->vs
= g_object_new (GTK_TYPE_SCROLLBAR
,
1701 "orientation", GTK_ORIENTATION_VERTICAL
,
1702 "adjustment", scg
->va
,
1704 g_signal_connect (G_OBJECT (scg
->vs
),
1706 G_CALLBACK (cb_vscrollbar_value_changed
), scg
);
1707 g_signal_connect (G_OBJECT (scg
->vs
),
1709 G_CALLBACK (cb_vscrollbar_adjust_bounds
), sheet
);
1711 scg
->ha
= (GtkAdjustment
*)gtk_adjustment_new (0., 0., 1, 1., 1., 1.);
1712 scg
->hs
= g_object_new (GTK_TYPE_SCROLLBAR
,
1713 "adjustment", scg
->ha
,
1715 g_signal_connect (G_OBJECT (scg
->hs
),
1717 G_CALLBACK (cb_hscrollbar_value_changed
), scg
);
1718 g_signal_connect (G_OBJECT (scg
->hs
),
1720 G_CALLBACK (cb_hscrollbar_adjust_bounds
), sheet
);
1722 g_object_ref (scg
->grid
);
1723 scg
->vpane
= g_object_new (GTK_TYPE_PANED
, "orientation", GTK_ORIENTATION_VERTICAL
, NULL
);
1724 gtk_paned_add1 (scg
->vpane
, gtk_label_new (NULL
)); /* use a spacer */
1725 gtk_paned_add2 (scg
->vpane
, scg
->vs
);
1726 scg_gtk_paned_set_position (scg
, scg
->vpane
, 0);
1727 gtk_widget_set_vexpand (GTK_WIDGET (scg
->vpane
), TRUE
);
1728 gtk_grid_attach (scg
->grid
,
1729 GTK_WIDGET (scg
->vpane
), 4, 0, 1, 4);
1730 scg
->hpane
= g_object_new (GTK_TYPE_PANED
, NULL
);
1731 gtk_paned_add1 (scg
->hpane
, gtk_label_new (NULL
)); /* use a spacer */
1732 gtk_paned_add2 (scg
->hpane
, scg
->hs
);
1733 scg_gtk_paned_set_position (scg
, scg
->hpane
, 0);
1734 gtk_widget_set_hexpand (GTK_WIDGET (scg
->hpane
), TRUE
);
1735 gtk_grid_attach (scg
->grid
,
1736 GTK_WIDGET (scg
->hpane
), 0, 4, 4, 1);
1737 /* do not connect until after setting position */
1738 g_signal_connect (G_OBJECT (scg
->vpane
), "notify::position",
1739 G_CALLBACK (cb_resize_pane_motion
), scg
);
1740 g_signal_connect (G_OBJECT (scg
->hpane
), "notify::position",
1741 G_CALLBACK (cb_resize_pane_motion
), scg
);
1742 g_signal_connect_after (G_OBJECT (scg
->vpane
), "size-allocate",
1743 G_CALLBACK (cb_check_resize
), scg
);
1744 g_signal_connect_after (G_OBJECT (scg
->hpane
), "size-allocate",
1745 G_CALLBACK (cb_check_resize
), scg
);
1747 g_signal_connect_data (G_OBJECT (scg
->grid
),
1749 G_CALLBACK (scg_scrollbar_config
), scg
, NULL
,
1750 G_CONNECT_AFTER
| G_CONNECT_SWAPPED
);
1751 g_signal_connect_object (G_OBJECT (scg
->grid
),
1753 G_CALLBACK (cb_table_destroy
), G_OBJECT (scg
),
1756 gnm_sheet_view_attach_control (sv
, GNM_SHEET_CONTROL (scg
));
1758 g_object_connect (G_OBJECT (sheet
),
1759 "swapped_signal::notify::text-is-rtl", cb_scg_direction_changed
, scg
,
1760 "swapped_signal::notify::display-formulas", cb_scg_redraw
, scg
,
1761 "swapped_signal::notify::display-zeros", cb_scg_redraw
, scg
,
1762 "swapped_signal::notify::display-grid", cb_scg_redraw
, scg
,
1763 "swapped_signal::notify::display-column-header", scg_adjust_preferences
, scg
,
1764 "swapped_signal::notify::display-row-header", scg_adjust_preferences
, scg
,
1765 "swapped_signal::notify::use-r1c1", cb_scg_redraw
, scg
,
1766 "swapped_signal::notify::display-outlines", cb_scg_redraw_resize
, scg
,
1767 "swapped_signal::notify::display-outlines-below", cb_scg_redraw_resize
, scg
,
1768 "swapped_signal::notify::display-outlines-right", cb_scg_redraw_resize
, scg
,
1769 "swapped_signal::notify::columns", cb_scg_sheet_resized
, scg
,
1770 "swapped_signal::notify::rows", cb_scg_sheet_resized
, scg
,
1773 scg
->active_panes
= 0;
1774 scg
->grid
= GTK_GRID (gtk_grid_new ());
1775 g_object_ref (scg
->grid
);
1776 sheet
->hide_col_header
= sheet
->hide_row_header
= FALSE
;
1777 if (sheet
->sheet_type
== GNM_SHEET_OBJECT
) {
1778 /* WHY store this in ->vs? */
1779 scg
->vs
= g_object_new (GOC_TYPE_CANVAS
,
1783 gtk_style_context_add_class (gtk_widget_get_style_context (scg
->vs
),
1785 gtk_grid_attach (scg
->grid
, scg
->vs
, 0, 0, 1, 1);
1786 gtk_widget_set_can_focus (scg
->vs
, TRUE
);
1787 gtk_widget_set_can_default (scg
->vs
, TRUE
);
1788 g_signal_connect (G_OBJECT (scg
->vs
), "key-press-event",
1789 G_CALLBACK (sheet_object_key_pressed
), scg
);
1791 gnm_sheet_view_attach_control (sv
, GNM_SHEET_CONTROL (scg
));
1793 g_object_set_data (G_OBJECT (scg
->vs
), "sheet-control", scg
);
1794 if (sheet
->sheet_objects
) {
1795 /* we need an idle function because not everything is initialized at this point */
1796 sheet_object_new_view ((SheetObject
*) sheet
->sheet_objects
->data
,
1797 (SheetObjectViewContainer
*) scg
->vs
);
1798 g_idle_add ((GSourceFunc
) post_create_cb
, scg
);
1803 scg
->label
= g_object_new
1804 (GNM_NOTEBOOK_BUTTON_TYPE
,
1805 "label", sheet
->name_unquoted
,
1806 //"valign", GTK_ALIGN_START,
1809 ? go_color_to_gdk_rgba (sheet
->tab_color
->go_color
,
1813 (sheet
->tab_text_color
1814 ? go_color_to_gdk_rgba (sheet
->tab_text_color
->go_color
,
1818 g_object_ref (scg
->label
);
1820 g_signal_connect (G_OBJECT (scg
->grid
),
1822 G_CALLBACK (cb_screen_changed
),
1829 scg_comment_timer_clear (SheetControlGUI
*scg
)
1831 if (scg
->comment
.timer
!= 0) {
1832 g_source_remove (scg
->comment
.timer
);
1833 scg
->comment
.timer
= 0;
1838 scg_im_destroy (SheetControlGUI
*scg
) {
1839 if (scg
->im
.timer
!= 0) {
1840 g_source_remove (scg
->im
.timer
);
1844 gtk_widget_destroy (scg
->im
.item
);
1845 scg
->im
.item
= NULL
;
1850 scg_finalize (GObject
*object
)
1852 SheetControlGUI
*scg
= GNM_SCG (object
);
1853 SheetControl
*sc
= (SheetControl
*) scg
;
1854 Sheet
*sheet
= scg_sheet (scg
);
1857 /* remove the object view before we disappear */
1858 scg_object_unselect (scg
, NULL
);
1860 for (ptr
= sheet
->sheet_objects
; ptr
!= NULL
; ptr
= ptr
->next
)
1861 SCG_FOREACH_PANE (scg
, pane
,
1863 sheet_object_get_view (ptr
->data
, (SheetObjectViewContainer
*)pane
));
1866 if (scg
->col_group
.buttons
) {
1867 g_ptr_array_free (scg
->col_group
.buttons
, TRUE
);
1868 g_ptr_array_free (scg
->row_group
.buttons
, TRUE
);
1871 if (scg
->pane_drag_handler
) {
1872 g_source_remove (scg
->pane_drag_handler
);
1873 scg
->pane_drag_handler
= 0;
1876 if (scg
->scroll_bar_timer
) {
1877 g_source_remove (scg
->scroll_bar_timer
);
1878 scg
->scroll_bar_timer
= 0;
1881 scg_comment_timer_clear (scg
);
1883 if (scg
->delayedMovement
.timer
!= 0) {
1884 g_source_remove (scg
->delayedMovement
.timer
);
1885 scg
->delayedMovement
.timer
= 0;
1887 scg_comment_unselect (scg
, scg
->comment
.selected
);
1889 scg_im_destroy (scg
);
1892 Sheet
*sheet
= sv_sheet (sc
->view
);
1893 g_signal_handlers_disconnect_by_func (sheet
, scg_adjust_preferences
, scg
);
1894 g_signal_handlers_disconnect_by_func (sheet
, cb_scg_redraw
, scg
);
1895 g_signal_handlers_disconnect_by_func (sheet
, cb_scg_redraw_resize
, scg
);
1896 g_signal_handlers_disconnect_by_func (sheet
, cb_scg_sheet_resized
, scg
);
1897 g_signal_handlers_disconnect_by_func (sheet
, cb_scg_direction_changed
, scg
);
1898 gnm_sheet_view_detach_control (sc
->view
, sc
);
1902 gtk_widget_destroy (GTK_WIDGET (scg
->grid
));
1903 g_object_unref (scg
->grid
);
1907 g_clear_object (&scg
->label
);
1909 if (scg
->wbcg
!= NULL
)
1910 g_object_weak_unref (G_OBJECT (scg
->wbcg
),
1911 (GWeakNotify
) cb_wbc_destroyed
,
1914 (*scg_parent_class
->finalize
) (object
);
1918 scg_unant (SheetControl
*sc
)
1920 SheetControlGUI
*scg
= (SheetControlGUI
*)sc
;
1922 g_return_if_fail (GNM_IS_SCG (scg
));
1924 /* Always have a pane 0 */
1925 if (scg
->active_panes
== 0 || scg
->pane
[0]->cursor
.animated
== NULL
)
1928 SCG_FOREACH_PANE (scg
, pane
, {
1931 for (l
= pane
->cursor
.animated
; l
; l
= l
->next
) {
1932 GocItem
*item
= l
->data
;
1933 goc_item_destroy (item
);
1936 g_slist_free (pane
->cursor
.animated
);
1937 pane
->cursor
.animated
= NULL
;
1942 scg_ant (SheetControl
*sc
)
1944 SheetControlGUI
*scg
= (SheetControlGUI
*)sc
;
1947 g_return_if_fail (GNM_IS_SCG (scg
));
1949 if (scg
->active_panes
== 0)
1952 /* Always have a grid 0 */
1953 if (NULL
!= scg
->pane
[0]->cursor
.animated
)
1956 for (l
= sc
->view
->ants
; l
; l
= l
->next
) {
1957 GnmRange
const *r
= l
->data
;
1959 SCG_FOREACH_PANE (scg
, pane
, {
1960 GnmItemCursor
*ic
= GNM_ITEM_CURSOR (goc_item_new (
1962 gnm_item_cursor_get_type (),
1963 "SheetControlGUI", scg
,
1964 "style", GNM_ITEM_CURSOR_ANTED
,
1966 gnm_item_cursor_bound_set (ic
, r
);
1967 pane
->cursor
.animated
=
1968 g_slist_prepend (pane
->cursor
.animated
, ic
);
1974 scg_adjust_preferences (SheetControlGUI
*scg
)
1976 Sheet
const *sheet
= scg_sheet (scg
);
1978 SCG_FOREACH_PANE (scg
, pane
, {
1979 if (pane
->col
.canvas
!= NULL
) {
1980 gtk_widget_set_visible (GTK_WIDGET (pane
->col
.canvas
),
1981 !sheet
->hide_col_header
);
1984 if (pane
->row
.canvas
!= NULL
) {
1985 gtk_widget_set_visible (GTK_WIDGET (pane
->row
.canvas
),
1986 !sheet
->hide_row_header
);
1990 if (scg
->select_all_btn
) {
1991 /* we used to test for the corner table existence, why??? */
1992 gboolean visible
= !(sheet
->hide_col_header
|| sheet
->hide_row_header
);
1993 gtk_widget_set_visible (scg
->select_all_btn
, visible
);
1994 gtk_widget_set_visible (scg
->row_group
.button_box
, visible
);
1995 gtk_widget_set_visible (scg
->col_group
.button_box
, visible
);
1997 if (scg_wbc (scg
) != NULL
) {
1998 WorkbookView
*wbv
= wb_control_view (scg_wbc (scg
));
1999 gtk_widget_set_visible (scg
->hs
,
2000 wbv
->show_horizontal_scrollbar
);
2002 gtk_widget_set_visible (scg
->vs
,
2003 wbv
->show_vertical_scrollbar
);
2008 /***************************************************************************/
2014 CONTEXT_PASTE_SPECIAL
,
2017 CONTEXT_CLEAR_CONTENT
,
2018 CONTEXT_FORMAT_CELL
,
2019 CONTEXT_FORMAT_CELL_COND
,
2020 CONTEXT_CELL_AUTOFIT_WIDTH
,
2021 CONTEXT_CELL_AUTOFIT_HEIGHT
,
2023 CONTEXT_CELL_UNMERGE
,
2027 CONTEXT_COL_AUTOFIT
,
2031 CONTEXT_ROW_AUTOFIT
,
2032 CONTEXT_COMMENT_EDIT
,
2033 CONTEXT_COMMENT_ADD
,
2034 CONTEXT_COMMENT_REMOVE
,
2035 CONTEXT_HYPERLINK_EDIT
,
2036 CONTEXT_HYPERLINK_ADD
,
2037 CONTEXT_HYPERLINK_REMOVE
,
2038 CONTEXT_DATA_SLICER_REFRESH
, /* refresh and redraw */
2039 CONTEXT_DATA_SLICER_EDIT
/* prop dialog */
2042 context_menu_handler (GnmPopupMenuElement
const *element
,
2045 SheetControlGUI
*scg
= user_data
;
2046 SheetControl
*sc
= (SheetControl
*) scg
;
2047 SheetView
*sv
= sc
->view
;
2048 Sheet
*sheet
= sv
->sheet
;
2049 WBCGtk
*wbcg
= scg
->wbcg
;
2050 WorkbookControl
*wbc
= sc
->wbc
;
2052 g_return_if_fail (element
!= NULL
);
2053 g_return_if_fail (IS_SHEET (sheet
));
2055 switch (element
->index
) {
2057 gnm_sheet_view_selection_cut (sv
, wbc
);
2060 gnm_sheet_view_selection_copy (sv
, wbc
);
2063 cmd_paste_to_selection (wbc
, sv
, PASTE_DEFAULT
);
2065 case CONTEXT_PASTE_SPECIAL
:
2066 dialog_paste_special (wbcg
);
2068 case CONTEXT_INSERT
:
2069 dialog_insert_cells (wbcg
);
2071 case CONTEXT_DELETE
:
2072 dialog_delete_cells (wbcg
);
2074 case CONTEXT_CLEAR_CONTENT
:
2075 cmd_selection_clear (wbc
, CLEAR_VALUES
);
2077 case CONTEXT_FORMAT_CELL
:
2078 dialog_cell_format (wbcg
, FD_CURRENT
, 0);
2080 case CONTEXT_FORMAT_CELL_COND
:
2081 dialog_cell_format_cond (wbcg
);
2083 case CONTEXT_CELL_AUTOFIT_HEIGHT
:
2084 workbook_cmd_autofit_selection
2085 (wbc
, wb_control_cur_sheet (wbc
), FALSE
);
2087 case CONTEXT_CELL_AUTOFIT_WIDTH
:
2088 workbook_cmd_autofit_selection
2089 (wbc
, wb_control_cur_sheet (wbc
), TRUE
);
2091 case CONTEXT_CELL_MERGE
: {
2092 GSList
*range_list
= selection_get_ranges
2093 (wb_control_cur_sheet_view (wbc
), FALSE
);
2094 cmd_merge_cells (wbc
, wb_control_cur_sheet (wbc
), range_list
, FALSE
);
2095 range_fragment_free (range_list
);
2098 case CONTEXT_CELL_UNMERGE
: {
2099 GSList
*range_list
= selection_get_ranges
2100 (wb_control_cur_sheet_view (wbc
), FALSE
);
2101 cmd_unmerge_cells (wbc
, wb_control_cur_sheet (wbc
), range_list
);
2102 range_fragment_free (range_list
);
2106 case CONTEXT_COL_WIDTH
:
2107 dialog_col_width (wbcg
, FALSE
);
2109 case CONTEXT_COL_AUTOFIT
:
2110 workbook_cmd_resize_selected_colrow
2111 (wbc
, wb_control_cur_sheet (wbc
), TRUE
, -1);
2113 case CONTEXT_COL_HIDE
:
2114 cmd_selection_colrow_hide (wbc
, TRUE
, FALSE
);
2116 case CONTEXT_COL_UNHIDE
:
2117 cmd_selection_colrow_hide (wbc
, TRUE
, TRUE
);
2119 case CONTEXT_ROW_HEIGHT
:
2120 dialog_row_height (wbcg
, FALSE
);
2122 case CONTEXT_ROW_AUTOFIT
:
2123 workbook_cmd_resize_selected_colrow
2124 (wbc
, wb_control_cur_sheet (wbc
), FALSE
, -1);
2126 case CONTEXT_ROW_HIDE
:
2127 cmd_selection_colrow_hide (wbc
, FALSE
, FALSE
);
2129 case CONTEXT_ROW_UNHIDE
:
2130 cmd_selection_colrow_hide (wbc
, FALSE
, TRUE
);
2132 case CONTEXT_COMMENT_EDIT
:
2133 case CONTEXT_COMMENT_ADD
:
2134 dialog_cell_comment (wbcg
, sheet
, &sv
->edit_pos
);
2136 case CONTEXT_COMMENT_REMOVE
:
2137 cmd_selection_clear (GNM_WBC (wbcg
), CLEAR_COMMENTS
);
2139 case CONTEXT_HYPERLINK_EDIT
:
2140 case CONTEXT_HYPERLINK_ADD
:
2141 dialog_hyperlink (wbcg
, sc
);
2144 case CONTEXT_HYPERLINK_REMOVE
: {
2145 GnmStyle
*style
= gnm_style_new ();
2148 gchar
const *format
;
2151 for (l
= scg_view (scg
)->selections
; l
!= NULL
; l
= l
->next
) {
2152 GnmRange
const *r
= l
->data
;
2153 GnmStyleList
*styles
;
2155 styles
= sheet_style_collect_hlinks (sheet
, r
);
2156 n_links
+= g_slist_length (styles
);
2157 style_list_free (styles
);
2159 format
= ngettext ("Remove %d Link", "Remove %d Links", n_links
);
2160 name
= g_strdup_printf (format
, n_links
);
2161 gnm_style_set_hlink (style
, NULL
);
2162 cmd_selection_format (wbc
, style
, NULL
, name
);
2166 case CONTEXT_DATA_SLICER_REFRESH
:
2167 cmd_slicer_refresh (wbc
);
2169 case CONTEXT_DATA_SLICER_EDIT
:
2170 dialog_data_slicer (wbcg
, FALSE
);
2179 scg_context_menu (SheetControlGUI
*scg
, GdkEvent
*event
,
2180 gboolean is_col
, gboolean is_row
)
2182 SheetView
*sv
= scg_view (scg
);
2183 Sheet
*sheet
= sv_sheet (sv
);
2186 CONTEXT_DISPLAY_FOR_CELLS
= 1 << 0,
2187 CONTEXT_DISPLAY_FOR_ROWS
= 1 << 1,
2188 CONTEXT_DISPLAY_FOR_COLS
= 1 << 2,
2189 CONTEXT_DISPLAY_WITH_HYPERLINK
= 1 << 3,
2190 CONTEXT_DISPLAY_WITHOUT_HYPERLINK
= 1 << 4,
2191 CONTEXT_DISPLAY_WITH_HYPERLINK_IN_RANGE
= 1 << 5,
2192 CONTEXT_DISPLAY_WITH_DATA_SLICER
= 1 << 6,
2193 CONTEXT_DISPLAY_WITH_DATA_SLICER_ROW
= 1 << 7,
2194 CONTEXT_DISPLAY_WITH_DATA_SLICER_COL
= 1 << 8,
2195 CONTEXT_DISPLAY_WITH_COMMENT
= 1 << 9,
2196 CONTEXT_DISPLAY_WITHOUT_COMMENT
= 1 << 10,
2197 CONTEXT_DISPLAY_WITH_COMMENT_IN_RANGE
= 1 << 11
2200 CONTEXT_DISABLE_PASTE_SPECIAL
= 1 << 0,
2201 CONTEXT_DISABLE_FOR_ROWS
= 1 << 1,
2202 CONTEXT_DISABLE_FOR_COLS
= 1 << 2,
2203 CONTEXT_DISABLE_FOR_CELLS
= 1 << 3,
2204 CONTEXT_DISABLE_FOR_DISCONTIGUOUS_SELECTION
= 1 << 4,
2205 CONTEXT_DISABLE_FOR_ALL_COLS
= 1 << 5,
2206 CONTEXT_DISABLE_FOR_ALL_ROWS
= 1 << 6,
2207 CONTEXT_DISABLE_FOR_NOMERGES
= 1 << 7,
2208 CONTEXT_DISABLE_FOR_ONLYMERGES
= 1 << 8
2211 /* Note: keep the following two in sync!*/
2216 POPUPITEM_PASTESPECIAL
,
2218 POPUPITEM_INSERT_CELL
,
2219 POPUPITEM_DELETE_CELL
,
2220 POPUPITEM_INSERT_COLUMN
,
2221 POPUPITEM_DELETE_COLUMN
,
2222 POPUPITEM_INSERT_ROW
,
2223 POPUPITEM_DELETE_ROW
,
2224 POPUPITEM_CLEAR_CONTENTS
,
2226 POPUPITEM_COMMENT_ADD
,
2227 POPUPITEM_COMMENT_EDIT
,
2228 POPUPITEM_COMMENT_REMOVE
,
2230 POPUPITEM_LINK_EDIT
,
2231 POPUPITEM_LINK_REMOVE
,
2233 POPUPITEM_DATASLICER_EDIT
,
2234 POPUPITEM_DATASLICER_REFRESH
,
2235 POPUPITEM_DATASLICER_FIELD_ORDER
,
2236 POPUPITEM_DATASLICER_LEFT
,
2237 POPUPITEM_DATASLICER_RIGHT
,
2238 POPUPITEM_DATASLICER_UP
,
2239 POPUPITEM_DATASLICER_DOWN
,
2240 POPUPITEM_DATASLICER_SUBMENU
,
2244 static GnmPopupMenuElement popup_elements
[] = {
2245 { N_("Cu_t"), "edit-cut",
2246 0, 0, CONTEXT_CUT
, NULL
},
2247 { N_("_Copy"), "edit-copy",
2248 0, 0, CONTEXT_COPY
, NULL
},
2249 { N_("_Paste"), "edit-paste",
2250 0, 0, CONTEXT_PASTE
, NULL
},
2251 { N_("Paste _Special"), "edit-paste",
2252 0, CONTEXT_DISABLE_PASTE_SPECIAL
, CONTEXT_PASTE_SPECIAL
, NULL
},
2254 { "", NULL
, 0, 0, 0, NULL
},
2256 { N_("_Insert Cells..."), NULL
,
2257 CONTEXT_DISPLAY_FOR_CELLS
,
2258 CONTEXT_DISABLE_FOR_DISCONTIGUOUS_SELECTION
, CONTEXT_INSERT
, NULL
},
2259 { N_("_Delete Cells..."), "edit-delete",
2260 CONTEXT_DISPLAY_FOR_CELLS
,
2261 CONTEXT_DISABLE_FOR_DISCONTIGUOUS_SELECTION
, CONTEXT_DELETE
, NULL
},
2262 { N_("_Insert Column(s)"), "gnumeric-column-add",
2263 CONTEXT_DISPLAY_FOR_COLS
,
2264 CONTEXT_DISABLE_FOR_DISCONTIGUOUS_SELECTION
,
2265 CONTEXT_INSERT
, NULL
},
2266 { N_("_Delete Column(s)"), "gnumeric-column-delete",
2267 CONTEXT_DISPLAY_FOR_COLS
,
2268 CONTEXT_DISABLE_FOR_DISCONTIGUOUS_SELECTION
,
2269 CONTEXT_DELETE
, NULL
},
2270 { N_("_Insert Row(s)"), "gnumeric-row-add",
2271 CONTEXT_DISPLAY_FOR_ROWS
,
2272 CONTEXT_DISABLE_FOR_DISCONTIGUOUS_SELECTION
,
2273 CONTEXT_INSERT
, NULL
},
2274 { N_("_Delete Row(s)"), "gnumeric-row-delete",
2275 CONTEXT_DISPLAY_FOR_ROWS
,
2276 CONTEXT_DISABLE_FOR_DISCONTIGUOUS_SELECTION
,
2277 CONTEXT_DELETE
, NULL
},
2279 { N_("Clear Co_ntents"), "edit-clear",
2280 0, 0, CONTEXT_CLEAR_CONTENT
, NULL
},
2282 { "", NULL
, CONTEXT_DISPLAY_FOR_CELLS
, 0, 0, NULL
},
2284 { N_("Add _Comment..."), "gnumeric-comment-add",
2285 CONTEXT_DISPLAY_WITHOUT_COMMENT
, 0, CONTEXT_COMMENT_ADD
, NULL
},
2286 { N_("Edit Co_mment..."),"gnumeric-comment-edit",
2287 CONTEXT_DISPLAY_WITH_COMMENT
, 0, CONTEXT_COMMENT_EDIT
, NULL
},
2288 { N_("_Remove Comments"), "gnumeric-comment-delete",
2289 CONTEXT_DISPLAY_WITH_COMMENT_IN_RANGE
, 0, CONTEXT_COMMENT_REMOVE
, NULL
},
2291 { N_("Add _Hyperlink..."), "gnumeric-link-add",
2292 CONTEXT_DISPLAY_WITHOUT_HYPERLINK
, 0,
2293 CONTEXT_HYPERLINK_ADD
, NULL
},
2294 { N_("Edit _Hyperlink..."), "gnumeric-link-edit",
2295 CONTEXT_DISPLAY_WITH_HYPERLINK
, 0,
2296 CONTEXT_HYPERLINK_EDIT
, NULL
},
2297 { N_("_Remove Hyperlink"), "gnumeric-link-delete",
2298 CONTEXT_DISPLAY_WITH_HYPERLINK_IN_RANGE
, 0,
2299 CONTEXT_HYPERLINK_REMOVE
, NULL
},
2301 { "", NULL
, 0, 0, 0, NULL
},
2303 { N_("_Edit DataSlicer"), NULL
,
2304 CONTEXT_DISPLAY_WITH_DATA_SLICER
, 0,
2305 CONTEXT_DATA_SLICER_EDIT
, NULL
},
2306 { N_("_Refresh DataSlicer"), NULL
,
2307 CONTEXT_DISPLAY_WITH_DATA_SLICER
, 0,
2308 CONTEXT_DATA_SLICER_REFRESH
, NULL
},
2310 { N_("DataSlicer Field _Order "), NULL
,
2311 CONTEXT_DISPLAY_WITH_DATA_SLICER_ROW
| CONTEXT_DISPLAY_WITH_DATA_SLICER_COL
, 0,
2312 -1, NULL
}, /* start sub menu */
2313 { N_("Left"), "go-previous",
2314 CONTEXT_DISPLAY_WITH_DATA_SLICER_ROW
, 0,
2315 CONTEXT_DATA_SLICER_REFRESH
, NULL
},
2316 { N_("Right"), "go-next",
2317 CONTEXT_DISPLAY_WITH_DATA_SLICER_ROW
, 0,
2318 CONTEXT_DATA_SLICER_REFRESH
, NULL
},
2319 { N_("Up"), "go-up",
2320 CONTEXT_DISPLAY_WITH_DATA_SLICER_COL
, 0,
2321 CONTEXT_DATA_SLICER_REFRESH
, NULL
},
2322 { N_("Down"), "go-down",
2323 CONTEXT_DISPLAY_WITH_DATA_SLICER_COL
, 0,
2324 CONTEXT_DATA_SLICER_REFRESH
, NULL
},
2326 CONTEXT_DISPLAY_WITH_DATA_SLICER_ROW
| CONTEXT_DISPLAY_WITH_DATA_SLICER_COL
, 0,
2327 -1, NULL
}, /* end sub menu */
2329 { N_("_Format All Cells..."), GTK_STOCK_PROPERTIES
,
2330 0, 0, CONTEXT_FORMAT_CELL
, NULL
},
2331 { N_("C_onditional Formatting..."), GTK_STOCK_PROPERTIES
,
2332 0, 0, CONTEXT_FORMAT_CELL_COND
, NULL
},
2333 { N_("Cell"), NULL
, 0, 0, -1, NULL
},/* start sub menu */
2334 { N_("_Merge"), "gnumeric-cells-merge", 0,
2335 CONTEXT_DISABLE_FOR_ONLYMERGES
, CONTEXT_CELL_MERGE
, NULL
},
2336 { N_("_Unmerge"), "gnumeric-cells-split", 0,
2337 CONTEXT_DISABLE_FOR_NOMERGES
, CONTEXT_CELL_UNMERGE
, NULL
},
2338 { N_("Auto Fit _Width"), "gnumeric-column-size", 0, 0, CONTEXT_CELL_AUTOFIT_WIDTH
, NULL
},
2339 { N_("Auto Fit _Height"), "gnumeric-row-size", 0, 0, CONTEXT_CELL_AUTOFIT_HEIGHT
, NULL
},
2340 { "", NULL
, 0, 0, -1, NULL
},/* end sub menu */
2343 /* Column specific (Note some labels duplicate row labels) */
2344 { N_("Column"), NULL
, 0, 0, -1, NULL
},/* start sub menu */
2345 { N_("_Width..."), "gnumeric-column-size", 0, 0, CONTEXT_COL_WIDTH
, NULL
},
2346 { N_("_Auto Fit Width"), "gnumeric-column-size", 0, 0, CONTEXT_COL_AUTOFIT
, NULL
},
2347 { N_("_Hide"), "gnumeric-column-hide", 0, CONTEXT_DISABLE_FOR_ALL_COLS
, CONTEXT_COL_HIDE
, NULL
},
2348 { N_("_Unhide"), "gnumeric-column-unhide", 0, 0, CONTEXT_COL_UNHIDE
, NULL
},
2349 { "", NULL
, 0, 0, -1, NULL
},/* end sub menu */
2351 /* Row specific (Note some labels duplicate col labels) */
2352 { N_("Row"), NULL
, 0, 0, -1, NULL
},/* start sub menu */
2353 { N_("Hei_ght..."), "gnumeric-row-size", 0, 0, CONTEXT_ROW_HEIGHT
, NULL
},
2354 { N_("_Auto Fit Height"), "gnumeric-row-size", 0, 0, CONTEXT_ROW_AUTOFIT
, NULL
},
2355 { N_("_Hide"), "gnumeric-row-hide", 0, CONTEXT_DISABLE_FOR_ALL_ROWS
, CONTEXT_ROW_HIDE
, NULL
},
2356 { N_("_Unhide"), "gnumeric-row-unhide", 0, 0, CONTEXT_ROW_UNHIDE
, NULL
},
2357 { "", NULL
, 0, 0, -1, NULL
},/* end sub menu */
2359 { NULL
, NULL
, 0, 0, 0, NULL
},
2362 /* row and column specific operations */
2363 int display_filter
=
2364 ((!is_col
&& !is_row
) ? CONTEXT_DISPLAY_FOR_CELLS
: 0) |
2365 (is_col
? CONTEXT_DISPLAY_FOR_COLS
: 0) |
2366 (is_row
? CONTEXT_DISPLAY_FOR_ROWS
: 0);
2368 /* Paste special only applies to local copies, not cuts, or remote
2371 int sensitivity_filter
=
2372 (!gnm_app_clipboard_is_empty () &&
2373 !gnm_app_clipboard_is_cut ())
2374 ? 0 : CONTEXT_DISABLE_PASTE_SPECIAL
;
2377 gboolean has_link
= FALSE
, has_comment
= FALSE
;
2378 int n_comments
= 0, n_links
= 0, n_cols
= 0, n_rows
= 0, n_cells
= 0;
2379 GnmSheetSlicer
*slicer
;
2382 gboolean full_sheet
= FALSE
, only_merges
= TRUE
, no_merges
= TRUE
;
2384 wbcg_edit_finish (scg
->wbcg
, WBC_EDIT_REJECT
, NULL
);
2386 /* Now see if there is some selection which selects a whole row or a
2387 * whole column and disable the insert/delete col/row menu items
2390 for (l
= scg_view (scg
)->selections
; l
!= NULL
; l
= l
->next
) {
2391 GnmRange
const *r
= l
->data
;
2392 GnmRange
const *merge
;
2393 GSList
*objs
, *merges
;
2394 GnmStyleList
*styles
;
2396 gboolean rfull_h
= range_is_full (r
, sheet
, TRUE
);
2397 gboolean rfull_v
= range_is_full (r
, sheet
, FALSE
);
2401 if (!range_is_singleton (r
)) {
2402 merge
= gnm_sheet_merge_is_corner (sheet
, &(r
->start
));
2403 if (NULL
== merge
|| !range_equal (merge
, r
))
2404 only_merges
= FALSE
;
2405 merges
= gnm_sheet_merge_get_overlap (sheet
, r
);
2406 if (merges
!= NULL
) {
2408 g_slist_free (merges
);
2413 display_filter
|= CONTEXT_DISPLAY_FOR_COLS
;
2414 display_filter
&= ~CONTEXT_DISPLAY_FOR_CELLS
;
2415 sensitivity_filter
|= CONTEXT_DISABLE_FOR_ALL_ROWS
;
2417 sensitivity_filter
|= CONTEXT_DISABLE_FOR_ROWS
;
2421 display_filter
|= CONTEXT_DISPLAY_FOR_ROWS
;
2422 display_filter
&= ~CONTEXT_DISPLAY_FOR_CELLS
;
2423 sensitivity_filter
|= CONTEXT_DISABLE_FOR_ALL_COLS
;
2425 sensitivity_filter
|= CONTEXT_DISABLE_FOR_COLS
;
2427 if (!(rfull_h
|| rfull_v
))
2428 sensitivity_filter
|= CONTEXT_DISABLE_FOR_CELLS
;
2430 full_sheet
= full_sheet
|| (rfull_h
&& rfull_v
);
2432 h
= range_height (r
);
2433 w
= range_width (r
);
2438 styles
= sheet_style_collect_hlinks (sheet
, r
);
2439 n_links
+= g_slist_length (styles
);
2440 style_list_free (styles
);
2442 objs
= sheet_objects_get (sheet
, r
, GNM_CELL_COMMENT_TYPE
);
2443 n_comments
+= g_slist_length (objs
);
2444 g_slist_free (objs
);
2448 sensitivity_filter
|= CONTEXT_DISABLE_FOR_ONLYMERGES
;
2450 sensitivity_filter
|= CONTEXT_DISABLE_FOR_NOMERGES
;
2453 if ((display_filter
& CONTEXT_DISPLAY_FOR_COLS
) &&
2454 (display_filter
& CONTEXT_DISPLAY_FOR_ROWS
))
2457 sensitivity_filter
|= CONTEXT_DISABLE_FOR_DISCONTIGUOUS_SELECTION
;
2459 has_comment
= (sheet_get_comment (sheet
, &sv
->edit_pos
) != NULL
);
2460 range_init_cellpos (&rge
, &sv
->edit_pos
);
2461 has_link
= (NULL
!= sheet_style_region_contains_link (sheet
, &rge
));
2463 slicer
= gnm_sheet_view_editpos_in_slicer (scg_view (scg
));
2464 /* FIXME: disabled for now */
2466 GODataSlicerField
*dsf
= gnm_sheet_slicer_field_header_at_pos (slicer
, &sv
->edit_pos
);
2468 if (go_data_slicer_field_get_field_type_pos (dsf
, GDS_FIELD_TYPE_COL
) >= 0)
2469 display_filter
|= CONTEXT_DISPLAY_WITH_DATA_SLICER_COL
;
2470 if (go_data_slicer_field_get_field_type_pos (dsf
, GDS_FIELD_TYPE_ROW
) >= 0)
2471 display_filter
|= CONTEXT_DISPLAY_WITH_DATA_SLICER_ROW
;
2473 display_filter
|= CONTEXT_DISPLAY_WITH_DATA_SLICER
;
2474 display_filter
&= ~CONTEXT_DISPLAY_FOR_CELLS
;
2477 if (display_filter
& CONTEXT_DISPLAY_FOR_CELLS
) {
2479 display_filter
|= ((has_link
) ?
2480 CONTEXT_DISPLAY_WITH_HYPERLINK
: CONTEXT_DISPLAY_WITHOUT_HYPERLINK
);
2481 display_filter
|= ((n_links
> 0) ?
2482 CONTEXT_DISPLAY_WITH_HYPERLINK_IN_RANGE
: CONTEXT_DISPLAY_WITHOUT_HYPERLINK
);
2483 display_filter
|= ((has_comment
) ?
2484 CONTEXT_DISPLAY_WITH_COMMENT
: CONTEXT_DISPLAY_WITHOUT_COMMENT
);
2485 display_filter
|= ((n_comments
> 0) ?
2486 CONTEXT_DISPLAY_WITH_COMMENT_IN_RANGE
: CONTEXT_DISPLAY_WITHOUT_COMMENT
);
2488 /* xgettext : %d gives the number of links. This is input to ngettext. */
2489 format
= ngettext ("_Remove %d Link", "_Remove %d Links", n_links
);
2490 popup_elements
[POPUPITEM_LINK_REMOVE
].allocated_name
= g_strdup_printf (format
, n_links
);
2492 if (n_comments
> 0) {
2493 /* xgettext : %d gives the number of comments. This is input to ngettext. */
2494 format
= ngettext ("_Remove %d Comment", "_Remove %d Comments", n_comments
);
2495 popup_elements
[POPUPITEM_COMMENT_REMOVE
].allocated_name
= g_strdup_printf (format
, n_comments
);
2497 format
= ngettext ("_Insert %d Cell...", "_Insert %d Cells...", n_cells
);
2498 popup_elements
[POPUPITEM_INSERT_CELL
].allocated_name
= g_strdup_printf (format
, n_cells
);
2499 format
= ngettext ("_Delete %d Cell...", "_Delete %d Cells...", n_cells
);
2500 popup_elements
[POPUPITEM_DELETE_CELL
].allocated_name
= g_strdup_printf (format
, n_cells
);
2503 if (display_filter
& CONTEXT_DISPLAY_FOR_COLS
) {
2505 format
= ngettext ("_Insert %d Column", "_Insert %d Columns", n_cols
);
2506 popup_elements
[POPUPITEM_INSERT_COLUMN
].allocated_name
= g_strdup_printf (format
, n_cols
);
2507 format
= ngettext ("_Delete %d Column", "_Delete %d Columns", n_cols
);
2508 popup_elements
[POPUPITEM_DELETE_COLUMN
].allocated_name
= g_strdup_printf (format
, n_cols
);
2509 if (!(sensitivity_filter
& (CONTEXT_DISABLE_FOR_CELLS
| CONTEXT_DISABLE_FOR_ROWS
))) {
2510 format
= ngettext ("_Format %d Column", "_Format %d Columns", n_cols
);
2511 popup_elements
[POPUPITEM_FORMAT
].allocated_name
2512 = g_strdup_printf (format
, n_cols
);
2515 if (display_filter
& CONTEXT_DISPLAY_FOR_ROWS
) {
2517 format
= ngettext ("_Insert %d Row", "_Insert %d Rows", n_rows
);
2518 popup_elements
[POPUPITEM_INSERT_ROW
].allocated_name
= g_strdup_printf (format
, n_rows
);
2519 format
= ngettext ("_Delete %d Row", "_Delete %d Rows", n_rows
);
2520 popup_elements
[POPUPITEM_DELETE_ROW
].allocated_name
= g_strdup_printf (format
, n_rows
);
2522 if (!(sensitivity_filter
& (CONTEXT_DISABLE_FOR_CELLS
| CONTEXT_DISABLE_FOR_COLS
))) {
2523 format
= ngettext ("_Format %d Row", "_Format %d Rows", n_rows
);
2524 popup_elements
[POPUPITEM_FORMAT
].allocated_name
2525 = g_strdup_printf (format
, n_rows
);
2528 if (!popup_elements
[POPUPITEM_FORMAT
].allocated_name
&& !full_sheet
) {
2530 format
= ngettext ("_Format %d Cell...", "_Format %d Cells", n_cells
);
2531 popup_elements
[POPUPITEM_FORMAT
].allocated_name
= g_strdup_printf (format
, n_cells
);
2535 gnm_create_popup_menu (popup_elements
,
2536 &context_menu_handler
, scg
, NULL
,
2537 display_filter
, sensitivity_filter
, event
);
2541 cb_redraw_sel (G_GNUC_UNUSED SheetView
*sv
, GnmRange
const *r
, gpointer user_data
)
2543 SheetControl
*sc
= user_data
;
2544 scg_redraw_range (sc
, r
);
2545 scg_redraw_headers (sc
, TRUE
, TRUE
, r
);
2550 scg_cursor_visible (SheetControlGUI
*scg
, gboolean is_visible
)
2552 SheetControl
*sc
= (SheetControl
*) scg
;
2554 /* there is always a grid 0 */
2555 if (NULL
== scg
->pane
[0])
2558 SCG_FOREACH_PANE (scg
, pane
,
2559 gnm_item_cursor_set_visibility (pane
->cursor
.std
, is_visible
););
2561 sv_selection_foreach (sc
->view
, cb_redraw_sel
, sc
);
2564 /***************************************************************************/
2568 * @scg: The sheet control
2570 * Put @sheet into the standard state 'edit mode'. This shuts down
2571 * any object editing and frees any objects that are created but not
2575 scg_mode_edit (SheetControlGUI
*scg
)
2578 g_return_if_fail (GNM_IS_SCG (scg
));
2582 if (wbcg
!= NULL
) /* Can be NULL during destruction */
2583 wbcg_insert_object_clear (wbcg
);
2585 scg_object_unselect (scg
, NULL
);
2587 /* During destruction we have already been disconnected
2588 * so don't bother changing the cursor */
2589 if (scg
->grid
!= NULL
&&
2590 scg_sheet (scg
) != NULL
&&
2591 scg_view (scg
) != NULL
) {
2592 scg_set_display_cursor (scg
);
2593 scg_cursor_visible (scg
, TRUE
);
2596 if (wbcg
!= NULL
&& wbc_gtk_get_guru (wbcg
) != NULL
&&
2597 scg
== wbcg_cur_scg (wbcg
))
2598 wbcg_edit_finish (wbcg
, WBC_EDIT_REJECT
, NULL
);
2601 wb_control_update_action_sensitivity (GNM_WBC (wbcg
));
2605 scg_mode_edit_virt (SheetControl
*sc
)
2607 scg_mode_edit ((SheetControlGUI
*)sc
);
2611 calc_obj_place (GnmPane
*pane
, gint64 canvas_coord
, gboolean is_col
,
2616 ColRowInfo
const *cri
;
2617 Sheet
const *sheet
= scg_sheet (pane
->simple
.scg
);
2620 colrow
= gnm_pane_find_col (pane
, canvas_coord
, &origin
);
2621 cri
= sheet_col_get_info (sheet
, colrow
);
2623 colrow
= gnm_pane_find_row (pane
, canvas_coord
, &origin
);
2624 cri
= sheet_row_get_info (sheet
, colrow
);
2627 /* TODO : handle other anchor types */
2628 *offset
= (canvas_coord
- origin
) / (double)cri
->size_pixels
;
2632 #define SO_CLASS(so) GNM_SO_CLASS (G_OBJECT_GET_CLASS(so))
2635 * scg_object_select:
2636 * @scg: The #SheetControl to edit in.
2637 * @so: The #SheetObject to select.
2639 * Adds @so to the set of selected objects and prepares it for user editing.
2640 * Adds a reference to @ref if it is selected.
2643 scg_object_select (SheetControlGUI
*scg
, SheetObject
*so
)
2647 if (scg
->selected_objects
== NULL
) {
2648 if (wb_view_is_protected (sv_wbv (scg_view (scg
)), TRUE
) ||
2649 !wbcg_edit_finish (scg
->wbcg
, WBC_EDIT_ACCEPT
, NULL
))
2653 wbcg_insert_object_clear (scg
->wbcg
);
2654 scg_cursor_visible (scg
, FALSE
);
2655 scg_set_display_cursor (scg
);
2656 scg_unant (GNM_SHEET_CONTROL (scg
));
2658 scg
->selected_objects
= g_hash_table_new_full (
2659 g_direct_hash
, g_direct_equal
,
2660 (GDestroyNotify
) g_object_unref
, (GDestroyNotify
) g_free
);
2661 wb_control_update_action_sensitivity (scg_wbc (scg
));
2663 g_return_if_fail (g_hash_table_lookup (scg
->selected_objects
, so
) == NULL
);
2667 coords
= g_new (double, 4);
2668 scg_object_anchor_to_coords (scg
, sheet_object_get_anchor (so
), coords
);
2669 g_hash_table_insert (scg
->selected_objects
, so
, coords
);
2670 g_signal_connect_object (so
, "unrealized",
2671 G_CALLBACK (scg_mode_edit
), scg
, G_CONNECT_SWAPPED
);
2673 SCG_FOREACH_PANE (scg
, pane
,
2674 gnm_pane_object_update_bbox (pane
, so
););
2678 cb_scg_object_unselect (SheetObject
*so
, G_GNUC_UNUSED
double *coords
, SheetControlGUI
*scg
)
2680 SCG_FOREACH_PANE (scg
, pane
, gnm_pane_object_unselect (pane
, so
););
2681 g_signal_handlers_disconnect_by_func (so
,
2682 scg_mode_edit
, scg
);
2686 * scg_object_unselect:
2687 * @scg: #SheetControlGUI
2688 * @so: #SheetObject (optionally NULL)
2690 * unselect the supplied object, and drop out of edit mode if this is the last
2691 * one. If @so == NULL unselect _all_ objects.
2694 scg_object_unselect (SheetControlGUI
*scg
, SheetObject
*so
)
2696 WorkbookControl
*wbc
= scg_wbc (scg
);
2698 /* cheesy cycle avoidance */
2699 if (scg
->selected_objects
== NULL
)
2703 double *pts
= g_hash_table_lookup (scg
->selected_objects
, so
);
2704 g_return_if_fail (pts
!= NULL
);
2705 cb_scg_object_unselect (so
, pts
, scg
);
2706 g_hash_table_remove (scg
->selected_objects
, so
);
2707 if (g_hash_table_size (scg
->selected_objects
) > 0)
2710 g_hash_table_foreach (scg
->selected_objects
,
2711 (GHFunc
) cb_scg_object_unselect
, scg
);
2713 g_hash_table_destroy (scg
->selected_objects
);
2714 scg
->selected_objects
= NULL
;
2715 scg_mode_edit (scg
);
2717 wb_control_update_action_sensitivity (wbc
);
2721 scg_object_select_next (SheetControlGUI
*scg
, gboolean reverse
)
2723 Sheet
*sheet
= scg_sheet (scg
);
2724 GSList
*ptr
= sheet
->sheet_objects
;
2726 g_return_if_fail (ptr
!= NULL
);
2728 if ((scg
->selected_objects
== NULL
) ||
2729 (g_hash_table_size (scg
->selected_objects
) == 0)) {
2730 scg_object_select (scg
, ptr
->data
);
2733 GSList
*prev
= NULL
;
2734 for (; ptr
!= NULL
; prev
= ptr
, ptr
= ptr
->next
)
2735 if (NULL
!= g_hash_table_lookup
2736 (scg
->selected_objects
, ptr
->data
)) {
2737 SheetObject
*target
;
2739 if (ptr
->next
== NULL
)
2740 target
= sheet
->sheet_objects
->data
;
2742 target
= ptr
->next
->data
;
2745 GSList
*last
= g_slist_last (ptr
);
2746 target
= last
->data
;
2748 target
= prev
->data
;
2750 if (ptr
->data
!= target
) {
2751 scg_object_unselect (scg
, NULL
);
2752 scg_object_select (scg
, target
);
2760 SheetControlGUI
*scg
;
2762 SheetObject
*primary_object
;
2766 gboolean snap_to_grid
;
2767 gboolean is_mouse_move
;
2771 snap_pos_to_grid (ObjDragInfo
const *info
, gboolean is_col
, double pos
,
2774 GnmPane
const *pane
= info
->pane
;
2775 Sheet
const *sheet
= scg_sheet (info
->scg
);
2776 int cell
= is_col
? pane
->first
.col
: pane
->first
.row
;
2777 gint64 pixel
= is_col
? pane
->first_offset
.x
: pane
->first_offset
.y
;
2778 gboolean snap
= FALSE
;
2780 ColRowInfo
const *cr_info
;
2781 int sheet_max
= colrow_max (is_col
, sheet
);
2784 while (cell
> 0 && pos
< pixel
) {
2785 cr_info
= sheet_colrow_get_info (sheet
, --cell
, is_col
);
2786 if (cr_info
->visible
) {
2787 length
= cr_info
->size_pixels
;
2795 cr_info
= sheet_colrow_get_info (sheet
, cell
, is_col
);
2796 if (cr_info
->visible
) {
2797 length
= cr_info
->size_pixels
;
2798 if (pixel
<= pos
&& pos
<= pixel
+ length
)
2802 } while (++cell
< sheet_max
&& !snap
);
2805 if (info
->is_mouse_move
)
2806 pos
= (fabs (pos
- pixel
) < fabs (pos
- pixel
- length
)) ? pixel
: pixel
+ length
;
2808 pos
= (pixel
== pos
) ? pixel
: (to_min
? pixel
: pixel
+ length
);
2811 return/* sign */ pos
;
2815 apply_move (SheetObject
*so
, int x_idx
, int y_idx
, double *coords
,
2816 ObjDragInfo
*info
, gboolean snap_to_grid
)
2818 gboolean move_x
= (x_idx
>= 0);
2819 gboolean move_y
= (y_idx
>= 0);
2822 x
= move_x
? coords
[x_idx
] + info
->dx
: 0;
2823 y
= move_y
? coords
[y_idx
] + info
->dy
: 0;
2826 g_return_if_fail (info
->pane
!= NULL
);
2829 x
= snap_pos_to_grid (info
, TRUE
, x
, info
->dx
< 0.);
2831 y
= snap_pos_to_grid (info
, FALSE
, y
, info
->dy
< 0.);
2832 if (info
->primary_object
== so
|| NULL
== info
->primary_object
) {
2833 if (move_x
) info
->dx
= x
- coords
[x_idx
];
2834 if (move_y
) info
->dy
= y
- coords
[y_idx
];
2838 if (move_x
) coords
[x_idx
] = x
;
2839 if (move_y
) coords
[y_idx
] = y
;
2841 if (info
->symmetric
&& !snap_to_grid
) {
2842 if (move_x
) coords
[x_idx
== 0 ? 2 : 0] -= info
->dx
;
2843 if (move_y
) coords
[y_idx
== 1 ? 3 : 1] -= info
->dy
;
2848 drag_object (SheetObject
*so
, double *coords
, ObjDragInfo
*info
)
2852 } const idx_info
[8] = {
2853 { 0, 1}, {-1, 1}, { 2, 1}, { 0,-1},
2854 { 2,-1}, { 0, 3}, {-1, 3}, { 2, 3}
2857 g_return_if_fail (info
->drag_type
<= 8);
2859 if (info
->drag_type
== 8) {
2860 apply_move (so
, 0, 1, coords
, info
, info
->snap_to_grid
);
2861 apply_move (so
, 2, 3, coords
, info
, FALSE
);
2864 idx_info
[info
->drag_type
].x_idx
,
2865 idx_info
[info
->drag_type
].y_idx
,
2866 coords
, info
, info
->snap_to_grid
);
2867 SCG_FOREACH_PANE (info
->scg
, pane
,
2868 gnm_pane_object_update_bbox (pane
, so
););
2872 cb_drag_selected_objects (SheetObject
*so
, double *coords
, ObjDragInfo
*info
)
2874 if (so
!= info
->primary_object
)
2875 drag_object (so
, coords
, info
);
2880 * @scg: #SheetControlGUI
2881 * @primary: #SheetObject (optionally NULL)
2887 * Move the control points and drag views of the currently selected objects to
2888 * a new position. This movement is only made in @scg not in the actual
2892 scg_objects_drag (SheetControlGUI
*scg
, GnmPane
*pane
,
2893 SheetObject
*primary
,
2894 gdouble
*dx
, gdouble
*dy
,
2895 int drag_type
, gboolean symmetric
,
2896 gboolean snap_to_grid
,
2897 gboolean is_mouse_move
)
2904 info
.primary_object
= primary
;
2907 info
.symmetric
= symmetric
;
2908 info
.drag_type
= drag_type
;
2909 info
.snap_to_grid
= snap_to_grid
;
2910 info
.is_mouse_move
= is_mouse_move
;
2912 if (primary
!= NULL
) {
2913 coords
= g_hash_table_lookup (scg
->selected_objects
, primary
);
2914 drag_object (primary
, coords
, &info
);
2917 g_hash_table_foreach (scg
->selected_objects
,
2918 (GHFunc
) cb_drag_selected_objects
, &info
);
2925 SheetControlGUI
*scg
;
2926 GSList
*objects
, *anchors
;
2927 } CollectObjectsData
;
2929 cb_collect_objects_to_commit (SheetObject
*so
, double *coords
, CollectObjectsData
*data
)
2931 SheetObjectAnchor
*anchor
= sheet_object_anchor_dup (
2932 sheet_object_get_anchor (so
));
2933 if (!sheet_object_can_resize (so
)) {
2934 /* FIXME: that code should be invalid */
2935 double scale
= goc_canvas_get_pixels_per_unit (GOC_CANVAS (data
->scg
->pane
[0])) / 72.;
2936 sheet_object_default_size (so
, coords
+ 2, coords
+ 3);
2937 coords
[2] *= gnm_app_display_dpi_get (TRUE
) * scale
;
2938 coords
[3] *= gnm_app_display_dpi_get (FALSE
) * scale
;
2939 coords
[2] += coords
[0];
2940 coords
[3] += coords
[1];
2942 scg_object_coords_to_anchor (data
->scg
, coords
, anchor
);
2943 data
->objects
= g_slist_prepend (data
->objects
, so
);
2944 data
->anchors
= g_slist_prepend (data
->anchors
, anchor
);
2946 if (!sheet_object_rubber_band_directly (so
)) {
2947 SCG_FOREACH_PANE (data
->scg
, pane
, {
2948 GocItem
**ctrl_pts
= g_hash_table_lookup (pane
->drag
.ctrl_pts
, so
);
2949 if (NULL
!= ctrl_pts
[9]) {
2950 double const *pts
= g_hash_table_lookup (
2951 pane
->simple
.scg
->selected_objects
, so
);
2952 SheetObjectView
*sov
= sheet_object_get_view (so
,
2953 (SheetObjectViewContainer
*)pane
);
2955 g_object_unref (ctrl_pts
[9]);
2959 sov
= sheet_object_new_view (so
, (SheetObjectViewContainer
*) pane
);
2961 sheet_object_view_set_bounds (sov
, pts
, TRUE
);
2968 scg_objects_drag_commit_get_undo_text (int drag_type
, int n
,
2969 gboolean created_objects
)
2973 if (created_objects
) {
2975 /* xgettext : %d gives the number of objects. This is input to ngettext. */
2976 format
= ngettext ("Duplicate %d Object", "Duplicate %d Objects", n
);
2978 /* xgettext : %d gives the number of objects. This is input to ngettext. */
2979 format
= ngettext ("Insert %d Object", "Insert %d Objects", n
);
2982 /* xgettext : %d gives the number of objects. This is input to ngettext. */
2983 format
= ngettext ("Move %d Object", "Move %d Objects", n
);
2985 /* xgettext : %d gives the number of objects. This is input to ngettext. */
2986 format
= ngettext ("Resize %d Object", "Resize %d Objects", n
);
2989 return g_strdup_printf (format
, n
);
2994 scg_objects_drag_commit (SheetControlGUI
*scg
, int drag_type
,
2995 gboolean created_objects
,
2996 GOUndo
**pundo
, GOUndo
**predo
, gchar
**undo_title
)
2998 CollectObjectsData data
;
3000 GOUndo
*undo
= NULL
;
3001 GOUndo
*redo
= NULL
;
3003 data
.objects
= data
.anchors
= NULL
;
3005 g_hash_table_foreach (scg
->selected_objects
,
3006 (GHFunc
) cb_collect_objects_to_commit
, &data
);
3008 undo
= sheet_object_move_undo (data
.objects
, created_objects
);
3009 redo
= sheet_object_move_do (data
.objects
, data
.anchors
, created_objects
);
3010 text
= scg_objects_drag_commit_get_undo_text
3011 (drag_type
, g_slist_length (data
.objects
), created_objects
);
3013 if (pundo
&& predo
) {
3019 cmd_generic (GNM_WBC (scg_wbcg (scg
)),
3023 g_slist_free (data
.objects
);
3024 g_slist_free_full (data
.anchors
, g_free
);
3028 scg_objects_nudge (SheetControlGUI
*scg
, GnmPane
*pane
,
3029 int drag_type
, double dx
, double dy
, gboolean symmetric
, gboolean snap_to_grid
)
3031 /* no nudging if we are creating an object */
3032 if (!scg
->wbcg
->new_object
) {
3033 scg_objects_drag (scg
, pane
, NULL
, &dx
, &dy
, drag_type
, symmetric
, snap_to_grid
, FALSE
);
3034 scg_objects_drag_commit (scg
, drag_type
, FALSE
, NULL
, NULL
, NULL
);
3039 scg_object_coords_to_anchor (SheetControlGUI
const *scg
,
3040 double const *coords
, SheetObjectAnchor
*in_out
)
3042 Sheet
*sheet
= scg_sheet (scg
);
3043 /* pane 0 always exists and the others are always use the same basis */
3044 GnmPane
*pane
= scg_pane ((SheetControlGUI
*)scg
, 0);
3046 g_return_if_fail (GNM_IS_SCG (scg
));
3047 g_return_if_fail (coords
!= NULL
);
3049 in_out
->base
.direction
= GOD_ANCHOR_DIR_NONE_MASK
;
3050 if (coords
[0] > coords
[2]) {
3056 in_out
->base
.direction
= GOD_ANCHOR_DIR_RIGHT
;
3058 if (coords
[1] > coords
[3]) {
3064 in_out
->base
.direction
|= GOD_ANCHOR_DIR_DOWN
;
3067 switch (in_out
->mode
) {
3068 case GNM_SO_ANCHOR_TWO_CELLS
:
3069 in_out
->cell_bound
.start
.col
= calc_obj_place (pane
, tmp
[0], TRUE
,
3070 in_out
->offset
+ 0);
3071 in_out
->cell_bound
.start
.row
= calc_obj_place (pane
, tmp
[1], FALSE
,
3072 in_out
->offset
+ 1);
3073 in_out
->cell_bound
.end
.col
= calc_obj_place (pane
, tmp
[2], TRUE
,
3074 in_out
->offset
+ 2);
3075 in_out
->cell_bound
.end
.row
= calc_obj_place (pane
, tmp
[3], FALSE
,
3076 in_out
->offset
+ 3);
3078 case GNM_SO_ANCHOR_ONE_CELL
:
3079 in_out
->cell_bound
.start
.col
= calc_obj_place (pane
, tmp
[0], TRUE
,
3080 in_out
->offset
+ 0);
3081 in_out
->cell_bound
.start
.row
= calc_obj_place (pane
, tmp
[1], FALSE
,
3082 in_out
->offset
+ 1);
3083 in_out
->cell_bound
.end
= in_out
->cell_bound
.start
;
3084 in_out
->offset
[2] = (tmp
[2] - tmp
[0]) / colrow_compute_pixel_scale (sheet
, TRUE
);
3085 in_out
->offset
[3] = (tmp
[3] - tmp
[1]) / colrow_compute_pixel_scale (sheet
, FALSE
);
3087 case GNM_SO_ANCHOR_ABSOLUTE
: {
3089 range_init (&in_out
->cell_bound
, 0, 0, 0, 0);
3090 h
= colrow_compute_pixel_scale (sheet
, TRUE
);
3091 v
= colrow_compute_pixel_scale (sheet
, FALSE
);
3092 in_out
->offset
[0] = tmp
[0] / h
;
3093 in_out
->offset
[1] = tmp
[1] / v
;
3094 in_out
->offset
[2] = (tmp
[2] - tmp
[0]) / h
;
3095 in_out
->offset
[3] = (tmp
[3] - tmp
[1]) / v
;
3102 cell_offset_calc_pixel (Sheet
const *sheet
, int i
, gboolean is_col
,
3105 ColRowInfo
const *cri
= sheet_colrow_get_info (sheet
, i
, is_col
);
3106 return offset
* cri
->size_pixels
;
3110 scg_object_anchor_to_coords (SheetControlGUI
const *scg
,
3111 SheetObjectAnchor
const *anchor
, double *coords
)
3113 Sheet
*sheet
= scg_sheet (scg
);
3114 GODrawingAnchorDir direction
;
3118 g_return_if_fail (GNM_IS_SCG (scg
));
3119 g_return_if_fail (anchor
!= NULL
);
3120 g_return_if_fail (coords
!= NULL
);
3122 r
= &anchor
->cell_bound
;
3123 if (anchor
->mode
!= GNM_SO_ANCHOR_ABSOLUTE
) {
3124 pixels
[0] = scg_colrow_distance_get (scg
, TRUE
, 0, r
->start
.col
);
3125 pixels
[1] = scg_colrow_distance_get (scg
, FALSE
, 0, r
->start
.row
);
3126 if (anchor
->mode
== GNM_SO_ANCHOR_TWO_CELLS
) {
3127 pixels
[2] = pixels
[0] + scg_colrow_distance_get (scg
, TRUE
,
3128 r
->start
.col
, r
->end
.col
);
3129 pixels
[3] = pixels
[1] + scg_colrow_distance_get (scg
, FALSE
,
3130 r
->start
.row
, r
->end
.row
);
3131 /* add .5 to offsets so that the rounding is optimal */
3132 pixels
[0] += cell_offset_calc_pixel (sheet
, r
->start
.col
,
3133 TRUE
, anchor
->offset
[0]) + .5;
3134 pixels
[1] += cell_offset_calc_pixel (sheet
, r
->start
.row
,
3135 FALSE
, anchor
->offset
[1]) + .5;
3136 pixels
[2] += cell_offset_calc_pixel (sheet
, r
->end
.col
,
3137 TRUE
, anchor
->offset
[2]) + .5;
3138 pixels
[3] += cell_offset_calc_pixel (sheet
, r
->end
.row
,
3139 FALSE
, anchor
->offset
[3]) + .5;
3141 /* add .5 to offsets so that the rounding is optimal */
3142 pixels
[0] += cell_offset_calc_pixel (sheet
, r
->start
.col
,
3143 TRUE
, anchor
->offset
[0]) + .5;
3144 pixels
[1] += cell_offset_calc_pixel (sheet
, r
->start
.row
,
3145 FALSE
, anchor
->offset
[1]) + .5;
3146 pixels
[2] = pixels
[0] + go_fake_floor (anchor
->offset
[2] * colrow_compute_pixel_scale (sheet
, TRUE
) + .5);
3147 pixels
[3] = pixels
[1] + go_fake_floor (anchor
->offset
[3] * colrow_compute_pixel_scale (sheet
, TRUE
) + .5);
3151 h
= colrow_compute_pixel_scale (sheet
, TRUE
);
3152 v
= colrow_compute_pixel_scale (sheet
, FALSE
);
3153 pixels
[0] = go_fake_floor (anchor
->offset
[0] * h
);
3154 pixels
[1] = go_fake_floor (anchor
->offset
[1] * v
);
3155 pixels
[2] = go_fake_floor ((anchor
->offset
[0] + anchor
->offset
[2]) * h
);
3156 pixels
[3] = go_fake_floor ((anchor
->offset
[1] + anchor
->offset
[3]) * v
);
3159 direction
= anchor
->base
.direction
;
3160 if (direction
== GOD_ANCHOR_DIR_UNKNOWN
)
3161 direction
= GOD_ANCHOR_DIR_DOWN_RIGHT
;
3163 coords
[0] = pixels
[direction
& GOD_ANCHOR_DIR_H_MASK
? 0 : 2];
3164 coords
[1] = pixels
[direction
& GOD_ANCHOR_DIR_V_MASK
? 1 : 3];
3165 coords
[2] = pixels
[direction
& GOD_ANCHOR_DIR_H_MASK
? 2 : 0];
3166 coords
[3] = pixels
[direction
& GOD_ANCHOR_DIR_V_MASK
? 3 : 1];
3169 /***************************************************************************/
3172 scg_comment_display_filter_cb (PangoAttribute
*attribute
, gboolean
*state
)
3174 if (attribute
->klass
->type
== PANGO_ATTR_FOREGROUND
&&
3175 attribute
->start_index
!= attribute
->end_index
)
3181 * scg_comment_display:
3182 * @scg: The SheetControl
3183 * @cc: A cell comment
3187 scg_comment_display (SheetControlGUI
*scg
, GnmComment
*cc
,
3190 g_return_if_fail (GNM_IS_SCG (scg
));
3192 scg_comment_timer_clear (scg
);
3194 /* If someone clicked and dragged the comment marker this may be NULL */
3195 if (scg
->comment
.selected
== NULL
)
3199 cc
= scg
->comment
.selected
;
3200 else if (scg
->comment
.selected
!= cc
)
3201 scg_comment_unselect (scg
, scg
->comment
.selected
);
3203 g_return_if_fail (GNM_IS_CELL_COMMENT (cc
));
3205 if (scg
->comment
.item
== NULL
) {
3206 GtkWidget
*label
, *box
;
3208 PangoAttrList
*comment_markup
;
3209 char const *comment_author
;
3211 g_object_get (G_OBJECT (cc
),
3212 "text", &comment_text
,
3213 "markup", &comment_markup
,
3215 comment_author
= cell_comment_author_get (cc
);
3217 box
= gtk_box_new (GTK_ORIENTATION_VERTICAL
, FALSE
);
3219 if (comment_author
!= NULL
) {
3221 PangoAttrList
*attrs
;
3222 PangoAttribute
*attr
;
3224 /* xgettext: this is a by-line for cell comments */
3225 text
= g_strdup_printf (_("By %s:"), comment_author
);
3226 label
= gtk_label_new (text
);
3229 attrs
= pango_attr_list_new ();
3230 attr
= pango_attr_weight_new (PANGO_WEIGHT_BOLD
);
3231 attr
->start_index
= 0;
3232 attr
->end_index
= G_MAXINT
;
3233 pango_attr_list_insert (attrs
, attr
);
3234 gtk_label_set_attributes (GTK_LABEL (label
), attrs
);
3235 pango_attr_list_unref (attrs
);
3237 gtk_widget_set_halign (label
, GTK_ALIGN_START
);
3238 gtk_box_pack_start (GTK_BOX (box
), label
, FALSE
, TRUE
, 0);
3239 gtk_box_set_spacing (GTK_BOX (box
), 10);
3242 label
= gtk_label_new (comment_text
);
3243 if (comment_markup
) {
3244 gboolean font_colour_set
= FALSE
;
3245 pango_attr_list_filter
3247 (PangoAttrFilterFunc
) scg_comment_display_filter_cb
,
3249 if (font_colour_set
) {
3250 /* Imported comments may have a font colour set. */
3251 /* If that is the case, we set a background colour. */
3252 guint length
= strlen (comment_text
);
3253 PangoAttribute
*attr
= pango_attr_foreground_new (0,0,0);
3254 attr
->start_index
= 0;
3255 attr
->end_index
= length
;
3256 pango_attr_list_insert_before (comment_markup
, attr
);
3257 attr
= pango_attr_background_new (255*255, 255*255, 224*255 );
3258 attr
->start_index
= 0;
3259 attr
->end_index
= length
;
3260 pango_attr_list_insert_before (comment_markup
, attr
);
3262 gtk_label_set_attributes (GTK_LABEL (label
), comment_markup
);
3264 g_free (comment_text
);
3265 gtk_widget_set_halign (label
, GTK_ALIGN_START
);
3266 gtk_box_pack_start (GTK_BOX (box
), label
, TRUE
, TRUE
, 0);
3268 gnm_convert_to_tooltip (GTK_WIDGET (scg
->grid
), box
);
3270 scg
->comment
.item
= gtk_widget_get_toplevel (box
);
3271 gtk_window_move (GTK_WINDOW (scg
->comment
.item
),
3274 gtk_widget_show_all (scg
->comment
.item
);
3279 cb_cell_comment_timer (SheetControlGUI
*scg
)
3281 g_return_val_if_fail (GNM_IS_SCG (scg
), FALSE
);
3282 g_return_val_if_fail (scg
->comment
.timer
!= 0, FALSE
);
3284 scg
->comment
.timer
= 0;
3285 scg_comment_display (scg
, scg
->comment
.selected
,
3286 scg
->comment
.x
, scg
->comment
.y
);
3291 * scg_comment_select:
3292 * @scg: The SheetControl
3293 * @cc: A cell comment
3295 * Prepare @cc for display.
3298 scg_comment_select (SheetControlGUI
*scg
, GnmComment
*cc
, int x
, int y
)
3300 g_return_if_fail (GNM_IS_SCG (scg
));
3302 if (scg
->comment
.selected
!= NULL
)
3303 scg_comment_unselect (scg
, scg
->comment
.selected
);
3305 g_return_if_fail (scg
->comment
.timer
== 0);
3307 scg
->comment
.selected
= cc
;
3308 scg
->comment
.timer
= g_timeout_add (1000,
3309 (GSourceFunc
)cb_cell_comment_timer
, scg
);
3315 * scg_comment_unselect:
3316 * @scg: The SheetControl
3317 * @cc: A cell comment
3319 * If @cc is the current cell comment being edited/displayed shutdown the
3320 * display mechanism.
3323 scg_comment_unselect (SheetControlGUI
*scg
, GnmComment
*cc
)
3325 g_return_if_fail (GNM_IS_SCG (scg
));
3327 if (cc
== scg
->comment
.selected
) {
3328 scg
->comment
.selected
= NULL
;
3329 scg_comment_timer_clear (scg
);
3331 if (scg
->comment
.item
!= NULL
) {
3332 gtk_widget_destroy (scg
->comment
.item
);
3333 scg
->comment
.item
= NULL
;
3338 /************************************************************************/
3339 /* Col/Row size support routines. */
3342 scg_colrow_distance_get (SheetControlGUI
const *scg
, gboolean is_cols
,
3345 Sheet
*sheet
= scg_sheet (scg
);
3346 ColRowCollection
const *collection
;
3352 g_return_val_if_fail (GNM_IS_SCG (scg
), 1);
3361 g_return_val_if_fail (from
>= 0, 1);
3364 g_return_val_if_fail (to
<= gnm_sheet_get_max_cols (sheet
), 1);
3365 collection
= &sheet
->cols
;
3367 g_return_val_if_fail (to
<= gnm_sheet_get_max_rows (sheet
), 1);
3368 collection
= &sheet
->rows
;
3371 /* Do not use col_row_foreach, it ignores empties.
3372 * Optimize this so that long jumps are not quite so horrific
3375 default_size
= collection
->default_style
.size_pixels
;
3376 for (i
= from
; i
< to
; ++i
) {
3377 ColRowSegment
const *segment
=
3378 COLROW_GET_SEGMENT(collection
, i
);
3380 if (segment
!= NULL
) {
3381 ColRowInfo
const *cri
= segment
->info
[COLROW_SUB_INDEX (i
)];
3383 pixels
+= default_size
;
3384 else if (cri
->visible
)
3385 pixels
+= cri
->size_pixels
;
3387 int segment_end
= COLROW_SEGMENT_END (i
)+1;
3388 if (segment_end
> to
)
3390 pixels
+= default_size
* (segment_end
- i
);
3391 i
= segment_end
- 1;
3398 /*************************************************************************/
3401 scg_cursor_bound (SheetControl
*sc
, GnmRange
const *r
)
3403 SheetControlGUI
*scg
= (SheetControlGUI
*) sc
;
3404 SCG_FOREACH_PANE (scg
, pane
, gnm_pane_cursor_bound_set (pane
, r
););
3408 scg_recompute_visible_region (SheetControl
*sc
, gboolean full_recompute
)
3410 SheetControlGUI
*scg
= (SheetControlGUI
*) sc
;
3412 SCG_FOREACH_PANE (scg
, pane
,
3413 gnm_pane_compute_visible_region (pane
, full_recompute
););
3417 scg_edit_start (SheetControlGUI
*scg
)
3419 g_return_if_fail (GNM_IS_SCG (scg
));
3421 SCG_FOREACH_PANE (scg
, pane
, gnm_pane_edit_start (pane
););
3425 scg_edit_stop (SheetControlGUI
*scg
)
3427 g_return_if_fail (GNM_IS_SCG (scg
));
3429 scg_rangesel_stop (scg
, FALSE
);
3430 SCG_FOREACH_PANE (scg
, pane
, gnm_pane_edit_stop (pane
););
3434 * scg_rangesel_changed:
3437 * Notify expr_entry that the expression range has changed.
3440 scg_rangesel_changed (SheetControlGUI
*scg
,
3441 int base_col
, int base_row
,
3442 int move_col
, int move_row
)
3444 GnmExprEntry
*expr_entry
;
3445 gboolean ic_changed
;
3446 GnmRange
*r
, last_r
;
3449 g_return_if_fail (GNM_IS_SCG (scg
));
3451 scg
->rangesel
.base_corner
.col
= base_col
;
3452 scg
->rangesel
.base_corner
.row
= base_row
;
3453 scg
->rangesel
.move_corner
.col
= move_col
;
3454 scg
->rangesel
.move_corner
.row
= move_row
;
3456 r
= &scg
->rangesel
.displayed
;
3457 if (base_col
< move_col
) {
3458 r
->start
.col
= base_col
;
3459 r
->end
.col
= move_col
;
3461 r
->end
.col
= base_col
;
3462 r
->start
.col
= move_col
;
3464 if (base_row
< move_row
) {
3465 r
->start
.row
= base_row
;
3466 r
->end
.row
= move_row
;
3468 r
->end
.row
= base_row
;
3469 r
->start
.row
= move_row
;
3472 sheet
= scg_sheet (scg
);
3473 expr_entry
= wbcg_get_entry_logical (scg
->wbcg
);
3475 gnm_expr_entry_freeze (expr_entry
);
3476 /* The order here is tricky.
3477 * 1) Assign the range to the expr entry.
3479 ic_changed
= gnm_expr_entry_load_from_range (
3480 expr_entry
, sheet
, r
);
3482 /* 2) if the expr entry changed the region get the new region */
3484 gnm_expr_entry_get_rangesel (expr_entry
, r
, NULL
);
3486 /* 3) now double check that all merged regions are fully contained */
3488 gnm_sheet_merge_find_bounding_box (sheet
, r
);
3489 if (!range_equal (&last_r
, r
))
3490 gnm_expr_entry_load_from_range (expr_entry
, sheet
, r
);
3492 gnm_expr_entry_thaw (expr_entry
);
3494 SCG_FOREACH_PANE (scg
, pane
, gnm_pane_rangesel_bound_set (pane
, r
););
3498 scg_rangesel_start (SheetControlGUI
*scg
,
3499 int base_col
, int base_row
,
3500 int move_col
, int move_row
)
3504 g_return_if_fail (GNM_IS_SCG (scg
));
3506 if (scg
->rangesel
.active
)
3509 if (scg
->wbcg
->rangesel
!= NULL
)
3510 g_warning ("misconfiged rangesel");
3512 scg
->wbcg
->rangesel
= scg
;
3513 scg
->rangesel
.active
= TRUE
;
3515 gnm_expr_entry_find_range (wbcg_get_entry_logical (scg
->wbcg
));
3517 range_init (&r
, base_col
, base_row
, move_col
, move_row
);
3518 SCG_FOREACH_PANE (scg
, pane
, gnm_pane_rangesel_start (pane
, &r
););
3519 scg_rangesel_changed (scg
, base_col
, base_row
, move_col
, move_row
);
3523 scg_rangesel_stop (SheetControlGUI
*scg
, gboolean clear_string
)
3525 g_return_if_fail (GNM_IS_SCG (scg
));
3527 if (!scg
->rangesel
.active
)
3529 if (scg
->wbcg
->rangesel
!= scg
)
3530 g_warning ("misconfiged rangesel");
3532 scg
->wbcg
->rangesel
= NULL
;
3533 scg
->rangesel
.active
= FALSE
;
3534 SCG_FOREACH_PANE (scg
, pane
, gnm_pane_rangesel_stop (pane
););
3536 gnm_expr_entry_rangesel_stop (wbcg_get_entry_logical (scg
->wbcg
),
3541 * scg_set_display_cursor:
3544 * Set the displayed cursor type.
3547 scg_set_display_cursor (SheetControlGUI
*scg
)
3549 GdkCursorType cursor
= GDK_CURSOR_IS_PIXMAP
;
3551 g_return_if_fail (GNM_IS_SCG (scg
));
3553 if (scg
->wbcg
->new_object
!= NULL
)
3554 cursor
= GDK_CROSSHAIR
;
3556 SCG_FOREACH_PANE (scg
, pane
, {
3557 GtkWidget
*w
= GTK_WIDGET (pane
);
3558 if (gtk_widget_get_window (w
)) {
3559 if (cursor
== GDK_CURSOR_IS_PIXMAP
)
3560 gnm_widget_set_cursor (w
, pane
->mouse_cursor
);
3562 gnm_widget_set_cursor_type (w
, cursor
);
3568 scg_rangesel_extend_to (SheetControlGUI
*scg
, int col
, int row
)
3570 int base_col
, base_row
;
3574 col
= gnm_sheet_get_last_col (scg_sheet (scg
));
3576 base_col
= scg
->rangesel
.base_corner
.col
;
3579 row
= gnm_sheet_get_last_row (scg_sheet (scg
));
3581 base_row
= scg
->rangesel
.base_corner
.row
;
3583 if (scg
->rangesel
.active
)
3584 scg_rangesel_changed (scg
, base_col
, base_row
, col
, row
);
3586 scg_rangesel_start (scg
, base_col
, base_row
, col
, row
);
3590 scg_rangesel_bound (SheetControlGUI
*scg
,
3591 int base_col
, int base_row
,
3592 int move_col
, int move_row
)
3594 if (scg
->rangesel
.active
)
3595 scg_rangesel_changed (scg
, base_col
, base_row
, move_col
, move_row
);
3597 scg_rangesel_start (scg
, base_col
, base_row
, move_col
, move_row
);
3601 scg_rangesel_move (SheetControlGUI
*scg
, int n
, gboolean jump_to_bound
,
3604 SheetView
*sv
= scg_view (scg
);
3607 if (!scg
->rangesel
.active
) {
3608 tmp
.col
= sv
->edit_pos_real
.col
;
3609 tmp
.row
= sv
->edit_pos_real
.row
;
3611 tmp
= scg
->rangesel
.base_corner
;
3614 tmp
.col
= sheet_find_boundary_horizontal (
3615 sv_sheet (sv
), tmp
.col
, tmp
.row
, tmp
.row
, n
, jump_to_bound
);
3617 tmp
.row
= sheet_find_boundary_vertical (
3618 sv_sheet (sv
), tmp
.col
, tmp
.row
, tmp
.col
, n
, jump_to_bound
);
3620 if (scg
->rangesel
.active
)
3621 scg_rangesel_changed (scg
, tmp
.col
, tmp
.row
, tmp
.col
, tmp
.row
);
3623 scg_rangesel_start (scg
, tmp
.col
, tmp
.row
, tmp
.col
, tmp
.row
);
3624 scg_make_cell_visible (scg
, tmp
.col
, tmp
.row
, FALSE
, FALSE
);
3625 gnm_expr_entry_signal_update (
3626 wbcg_get_entry_logical (scg
->wbcg
), FALSE
);
3630 scg_rangesel_extend (SheetControlGUI
*scg
, int n
,
3631 gboolean jump_to_bound
, gboolean horiz
)
3633 Sheet
*sheet
= scg_sheet (scg
);
3635 if (scg
->rangesel
.active
) {
3636 GnmCellPos tmp
= scg
->rangesel
.move_corner
;
3639 tmp
.col
= sheet_find_boundary_horizontal (sheet
,
3640 tmp
.col
, tmp
.row
, scg
->rangesel
.base_corner
.row
,
3643 tmp
.row
= sheet_find_boundary_vertical (sheet
,
3644 tmp
.col
, tmp
.row
, scg
->rangesel
.base_corner
.col
,
3647 scg_rangesel_changed (scg
,
3648 scg
->rangesel
.base_corner
.col
,
3649 scg
->rangesel
.base_corner
.row
, tmp
.col
, tmp
.row
);
3651 scg_make_cell_visible (scg
,
3652 scg
->rangesel
.move_corner
.col
,
3653 scg
->rangesel
.move_corner
.row
, FALSE
, TRUE
);
3654 gnm_expr_entry_signal_update (
3655 wbcg_get_entry_logical (scg
->wbcg
), FALSE
);
3657 scg_rangesel_move (scg
, n
, jump_to_bound
, horiz
);
3663 * @dir: Number of units to move the cursor
3664 * @jump_to_bound: skip from the start to the end of ranges
3665 * of filled or unfilled cells.
3666 * @horiz: is the movement horizontal or vertical
3668 * Moves the cursor count rows
3671 scg_cursor_move (SheetControlGUI
*scg
, int n
,
3672 gboolean jump_to_bound
, gboolean horiz
)
3674 SheetView
*sv
= scg_view (scg
);
3675 GnmCellPos tmp
= sv
->edit_pos_real
;
3676 int step
= (n
>0) ? 1 : -1;
3678 if (!wbcg_edit_finish (scg
->wbcg
, WBC_EDIT_ACCEPT
, NULL
))
3682 tmp
.col
= sheet_find_boundary_horizontal (sv
->sheet
,
3683 tmp
.col
+ n
- step
, tmp
.row
, tmp
.row
,
3684 step
, jump_to_bound
);
3686 tmp
.row
= sheet_find_boundary_vertical
3688 tmp
.col
, tmp
.row
+ n
- step
,
3690 step
, jump_to_bound
);
3692 sv_selection_reset (sv
);
3693 gnm_sheet_view_cursor_set (sv
, &tmp
,
3694 tmp
.col
, tmp
.row
, tmp
.col
, tmp
.row
, NULL
);
3695 gnm_sheet_view_make_cell_visible (sv
, tmp
.col
, tmp
.row
, FALSE
);
3696 sv_selection_add_pos (sv
, tmp
.col
, tmp
.row
, GNM_SELECTION_MODE_ADD
);
3700 * scg_cursor_extend:
3702 * @n: Units to extend the selection
3703 * @jump_to_bound: Move to transitions between cells and blanks,
3704 * or move in single steps.
3705 * @horiz: extend vertically or horizontally.
3708 scg_cursor_extend (SheetControlGUI
*scg
, int n
,
3709 gboolean jump_to_bound
, gboolean horiz
)
3711 SheetView
*sv
= scg_view (scg
);
3712 GnmCellPos move
= sv
->cursor
.move_corner
;
3713 GnmCellPos visible
= scg
->pane
[0]->first
;
3715 if (!wbcg_edit_finish (scg
->wbcg
, WBC_EDIT_ACCEPT
, NULL
))
3719 visible
.col
= move
.col
= sheet_find_boundary_horizontal (sv
->sheet
,
3720 move
.col
, move
.row
, sv
->cursor
.base_corner
.row
,
3723 visible
.row
= move
.row
= sheet_find_boundary_vertical (sv
->sheet
,
3724 move
.col
, move
.row
, sv
->cursor
.base_corner
.col
,
3727 sv_selection_extend_to (sv
, move
.col
, move
.row
);
3728 gnm_sheet_view_make_cell_visible (sv
, visible
.col
, visible
.row
, FALSE
);
3732 scg_take_focus (SheetControlGUI
*scg
)
3734 g_return_if_fail (GNM_IS_SCG (scg
));
3736 /* FIXME: Slightly hackish. */
3737 if (wbcg_toplevel (scg
->wbcg
))
3738 gtk_window_set_focus (wbcg_toplevel (scg
->wbcg
),
3739 (scg_sheet (scg
)->sheet_type
== GNM_SHEET_OBJECT
)?
3740 GTK_WIDGET (scg
->vs
): GTK_WIDGET (scg_pane (scg
, 0)));
3743 /*********************************************************************************/
3745 scg_size_guide_start (SheetControlGUI
*scg
,
3746 gboolean vert
, int colrow
, gboolean is_colrow_resize
)
3748 g_return_if_fail (GNM_IS_SCG (scg
));
3749 SCG_FOREACH_PANE (scg
, pane
,
3750 gnm_pane_size_guide_start (pane
, vert
, colrow
, is_colrow_resize
););
3753 scg_size_guide_motion (SheetControlGUI
*scg
, gboolean vert
, gint64 guide_pos
)
3755 g_return_if_fail (GNM_IS_SCG (scg
));
3756 SCG_FOREACH_PANE (scg
, pane
,
3757 gnm_pane_size_guide_motion (pane
, vert
, guide_pos
););
3760 scg_size_guide_stop (SheetControlGUI
*scg
)
3762 g_return_if_fail (GNM_IS_SCG (scg
));
3763 SCG_FOREACH_PANE (scg
, pane
,
3764 gnm_pane_size_guide_stop (pane
););
3766 /*********************************************************************************/
3769 scg_special_cursor_start (SheetControlGUI
*scg
, int style
, int button
)
3771 g_return_if_fail (GNM_IS_SCG (scg
));
3773 SCG_FOREACH_PANE (scg
, pane
,
3774 gnm_pane_special_cursor_start (pane
, style
, button
););
3778 scg_special_cursor_stop (SheetControlGUI
*scg
)
3780 g_return_if_fail (GNM_IS_SCG (scg
));
3782 SCG_FOREACH_PANE (scg
, pane
,
3783 gnm_pane_special_cursor_stop (pane
););
3787 scg_special_cursor_bound_set (SheetControlGUI
*scg
, GnmRange
const *r
)
3789 gboolean changed
= FALSE
;
3791 g_return_val_if_fail (GNM_IS_SCG (scg
), FALSE
);
3793 SCG_FOREACH_PANE (scg
, pane
,
3794 changed
|= gnm_pane_special_cursor_bound_set (pane
, r
););
3799 scg_object_create_view (SheetControl
*sc
, SheetObject
*so
)
3801 SheetControlGUI
*scg
= GNM_SCG (sc
);
3802 if (scg
->active_panes
)
3803 SCG_FOREACH_PANE (scg
, pane
,
3804 sheet_object_new_view (so
, (SheetObjectViewContainer
*)pane
););
3806 sheet_object_new_view (so
, (SheetObjectViewContainer
*)scg
->vs
);
3810 scg_scale_changed (SheetControl
*sc
)
3812 SheetControlGUI
*scg
= (SheetControlGUI
*)sc
;
3813 Sheet
*sheet
= scg_sheet (scg
);
3817 g_return_if_fail (GNM_IS_SCG (scg
));
3819 z
= sheet
->last_zoom_factor_used
;
3821 SCG_FOREACH_PANE (scg
, pane
, {
3822 if (pane
->col
.canvas
!= NULL
)
3823 goc_canvas_set_pixels_per_unit (pane
->col
.canvas
, z
);
3824 if (pane
->row
.canvas
!= NULL
)
3825 goc_canvas_set_pixels_per_unit (pane
->row
.canvas
, z
);
3826 goc_canvas_set_pixels_per_unit (GOC_CANVAS (pane
), z
);
3829 scg_resize (scg
, TRUE
);
3830 set_resize_pane_pos (scg
, scg
->vpane
);
3831 set_resize_pane_pos (scg
, scg
->hpane
);
3832 /* now, update sheet objects positions and sizes */
3833 for (ptr
= sheet
->sheet_objects
; ptr
; ptr
= ptr
->next
)
3834 sheet_object_update_bounds (GNM_SO (ptr
->data
), NULL
);
3838 cb_cell_im_timer (SheetControlGUI
*scg
)
3840 g_return_val_if_fail (GNM_IS_SCG (scg
), FALSE
);
3841 g_return_val_if_fail (scg
->im
.timer
!= 0, FALSE
);
3844 scg_im_destroy (scg
);
3849 scg_find_pane (SheetControlGUI
*scg
, GnmCellPos
*pos
)
3853 for (i
= 0; i
< scg
->active_panes
; i
++) {
3854 GnmPane
*pane
= scg
->pane
[i
];
3857 pane
->first
.col
<= pos
->col
&&
3858 pane
->first
.row
<= pos
->row
&&
3859 pane
->last_visible
.col
>= pos
->col
&&
3860 pane
->last_visible
.row
>= pos
->row
)
3867 scg_show_im_tooltip (SheetControl
*sc
, GnmInputMsg
*im
, GnmCellPos
*pos
)
3869 SheetControlGUI
*scg
= (SheetControlGUI
*)sc
;
3872 g_return_if_fail (GNM_IS_SCG (scg
));
3874 scg_im_destroy (scg
);
3876 pane
= scg_find_pane (scg
, pos
);
3879 GtkWidget
*label
, *box
;
3880 char const *text
, *title
;
3881 int len_text
, len_title
;
3882 int x
, y
, x_origin
, y_origin
;
3883 GtkAllocation allocation
;
3884 Sheet
*sheet
= scg_sheet (scg
);
3885 gboolean rtl
= sheet
->text_is_rtl
;
3887 text
= gnm_input_msg_get_msg (im
);
3888 title
= gnm_input_msg_get_title (im
);
3889 len_text
= (text
== NULL
) ? 0 : strlen (text
);
3890 len_title
= (title
== NULL
) ? 0 : strlen (title
);
3892 if ((len_text
== 0) && (len_title
== 0))
3895 box
= gtk_box_new (GTK_ORIENTATION_VERTICAL
, FALSE
);
3897 if (len_title
> 0) {
3898 PangoAttrList
*attrs
;
3899 PangoAttribute
*attr
;
3901 label
= gtk_label_new (title
);
3903 attrs
= pango_attr_list_new ();
3904 attr
= pango_attr_weight_new (PANGO_WEIGHT_BOLD
);
3905 attr
->start_index
= 0;
3906 attr
->end_index
= G_MAXINT
;
3907 pango_attr_list_insert (attrs
, attr
);
3908 gtk_label_set_attributes (GTK_LABEL (label
), attrs
);
3909 pango_attr_list_unref (attrs
);
3911 gtk_widget_set_halign (label
, GTK_ALIGN_START
);
3912 gtk_box_pack_start (GTK_BOX (box
), label
, FALSE
, TRUE
, 0);
3915 label
= gtk_label_new (text
);
3917 gtk_widget_set_halign (label
, GTK_ALIGN_START
);
3918 gtk_box_pack_start (GTK_BOX (box
), label
, FALSE
, TRUE
, 0);
3920 gtk_box_set_spacing (GTK_BOX (box
), 10);
3922 gnm_convert_to_tooltip (GTK_WIDGET (scg
->grid
), box
);
3923 scg
->im
.item
= gtk_widget_get_toplevel (box
);
3925 x
= sheet_col_get_distance_pixels
3926 (sheet
, pane
->first
.col
, pos
->col
+ (rtl
? 1 : 0));
3928 y
= sheet_row_get_distance_pixels
3929 (sheet
, pane
->first
.row
, pos
->row
+ 1);
3931 gtk_widget_get_allocation (GTK_WIDGET (pane
), &allocation
);
3933 x
= allocation
.width
- x
;
3937 gdk_window_get_position
3938 (gtk_widget_get_parent_window (GTK_WIDGET (pane
)),
3939 &x_origin
, &y_origin
);
3943 gtk_window_move (GTK_WINDOW (scg
->im
.item
), x
+ 10, y
+ 10);
3944 gtk_widget_show_all (scg
->im
.item
);
3945 scg
->im
.timer
= g_timeout_add (1500, (GSourceFunc
)cb_cell_im_timer
, scg
);
3951 scg_class_init (GObjectClass
*object_class
)
3953 SheetControlClass
*sc_class
= SHEET_CONTROL_CLASS (object_class
);
3955 g_return_if_fail (sc_class
!= NULL
);
3957 scg_parent_class
= g_type_class_peek_parent (object_class
);
3959 object_class
->finalize
= scg_finalize
;
3961 sc_class
->resize
= scg_resize_virt
;
3962 sc_class
->redraw_all
= scg_redraw_all
;
3963 sc_class
->redraw_range
= scg_redraw_range
;
3964 sc_class
->redraw_headers
= scg_redraw_headers
;
3965 sc_class
->ant
= scg_ant
;
3966 sc_class
->unant
= scg_unant
;
3967 sc_class
->scrollbar_config
= scg_scrollbar_config
;
3968 sc_class
->mode_edit
= scg_mode_edit_virt
;
3969 sc_class
->set_top_left
= scg_set_top_left
;
3970 sc_class
->recompute_visible_region
= scg_recompute_visible_region
;
3971 sc_class
->make_cell_visible
= scg_make_cell_visible_virt
;
3972 sc_class
->cursor_bound
= scg_cursor_bound
;
3973 sc_class
->set_panes
= scg_set_panes
;
3974 sc_class
->object_create_view
= scg_object_create_view
;
3975 sc_class
->scale_changed
= scg_scale_changed
;
3976 sc_class
->show_im_tooltip
= scg_show_im_tooltip
;
3979 GSF_CLASS (SheetControlGUI
, sheet_control_gui
,
3980 scg_class_init
, scg_init
, GNM_SHEET_CONTROL_TYPE
)
3983 cb_scg_queued_movement (SheetControlGUI
*scg
)
3985 Sheet
const *sheet
= scg_sheet (scg
);
3986 scg
->delayedMovement
.timer
= 0;
3987 (*scg
->delayedMovement
.handler
) (scg
,
3988 scg
->delayedMovement
.n
, FALSE
,
3989 scg
->delayedMovement
.horiz
);
3990 if (wbcg_is_editing (scg
->wbcg
))
3991 sheet_update_only_grid (sheet
);
3993 sheet_update (sheet
);
3998 * scg_queue_movement:
4000 * @handler: (scope async): The movement handler
4002 * @jump: TRUE jump to bound
4003 * @horiz: TRUE move by cols
4005 * Do motion compression when possible to avoid redrawing an area that will
4006 * disappear when we scroll again.
4009 scg_queue_movement (SheetControlGUI
*scg
,
4010 SCGUIMoveFunc handler
,
4011 int n
, gboolean jump
, gboolean horiz
)
4013 g_return_if_fail (GNM_IS_SCG (scg
));
4015 /* do we need to flush a pending movement */
4016 if (scg
->delayedMovement
.timer
!= 0) {
4018 /* do not skip more than 3 requests at a time */
4019 scg
->delayedMovement
.counter
> 3 ||
4020 scg
->delayedMovement
.handler
!= handler
||
4021 scg
->delayedMovement
.horiz
!= horiz
) {
4022 g_source_remove (scg
->delayedMovement
.timer
);
4023 (*scg
->delayedMovement
.handler
) (scg
,
4024 scg
->delayedMovement
.n
, FALSE
,
4025 scg
->delayedMovement
.horiz
);
4026 scg
->delayedMovement
.handler
= NULL
;
4027 scg
->delayedMovement
.timer
= 0;
4029 scg
->delayedMovement
.counter
++;
4030 scg
->delayedMovement
.n
+= n
;
4035 /* jumps are always immediate */
4037 Sheet
const *sheet
= scg_sheet (scg
);
4038 (*handler
) (scg
, n
, TRUE
, horiz
);
4039 if (wbcg_is_editing (scg
->wbcg
))
4040 sheet_update_only_grid (sheet
);
4042 sheet_update (sheet
);
4046 scg
->delayedMovement
.counter
= 1;
4047 scg
->delayedMovement
.handler
= handler
;
4048 scg
->delayedMovement
.horiz
= horiz
;
4049 scg
->delayedMovement
.n
= n
;
4050 scg
->delayedMovement
.timer
= g_timeout_add (10,
4051 (GSourceFunc
)cb_scg_queued_movement
, scg
);
4055 scg_image_create (SheetControlGUI
*scg
, SheetObjectAnchor
*anchor
,
4056 guint8
const *data
, unsigned len
)
4058 SheetObjectImage
*soi
;
4062 /* ensure that we are not editing anything else */
4063 scg_mode_edit (scg
);
4065 soi
= g_object_new (GNM_SO_IMAGE_TYPE
, NULL
);
4066 sheet_object_image_set_image (soi
, "", data
, len
);
4069 sheet_object_set_anchor (so
, anchor
);
4070 sheet_object_set_sheet (so
, scg_sheet (scg
));
4071 scg_object_select (scg
, so
);
4072 sheet_object_default_size (so
, &w
, &h
);
4073 scg_objects_drag (scg
, NULL
, NULL
, &w
, &h
, 7, FALSE
, FALSE
, FALSE
);
4074 scg_objects_drag_commit (scg
, 7, TRUE
, NULL
, NULL
, NULL
);
4078 scg_paste_image (SheetControlGUI
*scg
, GnmRange
*where
,
4079 guint8
const *data
, unsigned len
)
4081 SheetObjectAnchor anchor
;
4083 sheet_object_anchor_init (&anchor
, where
, NULL
,
4084 GOD_ANCHOR_DIR_DOWN_RIGHT
, GNM_SO_ANCHOR_TWO_CELLS
);
4085 scg_image_create (scg
, &anchor
, data
, len
);
4089 scg_drag_receive_img_data (SheetControlGUI
*scg
, double x
, double y
,
4090 guint8
const *data
, unsigned len
)
4093 SheetObjectAnchor anchor
;
4095 sheet_object_anchor_init (&anchor
, NULL
, NULL
,
4096 GOD_ANCHOR_DIR_DOWN_RIGHT
, GNM_SO_ANCHOR_TWO_CELLS
);
4097 coords
[0] = coords
[2] = x
;
4098 coords
[1] = coords
[3] = y
;
4099 scg_object_coords_to_anchor (scg
, coords
, &anchor
);
4100 scg_image_create (scg
, &anchor
, data
, len
);
4104 scg_drag_receive_img_uri (SheetControlGUI
*scg
, double x
, double y
, const gchar
*uri
)
4107 GsfInput
*input
= go_file_open (uri
, &err
);
4108 GOIOContext
*ioc
= go_io_context_new (GO_CMD_CONTEXT (scg
->wbcg
));
4110 if (input
!= NULL
) {
4111 unsigned len
= gsf_input_size (input
);
4112 guint8
const *data
= gsf_input_read (input
, len
, NULL
);
4114 scg_drag_receive_img_data (scg
, x
, y
, data
, len
);
4115 g_object_unref (input
);
4117 go_cmd_context_error (GO_CMD_CONTEXT (ioc
), err
);
4119 if (go_io_error_occurred (ioc
) ||
4120 go_io_warning_occurred (ioc
)) {
4121 go_io_error_display (ioc
);
4122 go_io_error_clear (ioc
);
4124 g_object_unref (ioc
);
4128 scg_drag_receive_spreadsheet (SheetControlGUI
*scg
, const gchar
*uri
)
4131 GsfInput
*input
= go_file_open (uri
, &err
);
4132 GOIOContext
*ioc
= go_io_context_new (GO_CMD_CONTEXT (scg
->wbcg
));
4134 if (input
!= NULL
) {
4137 wbv
= workbook_view_new_from_input (input
, uri
, NULL
, ioc
, NULL
);
4139 gui_wb_view_show (scg
->wbcg
,
4143 go_cmd_context_error (GO_CMD_CONTEXT (ioc
), err
);
4145 if (go_io_error_occurred (ioc
) ||
4146 go_io_warning_occurred (ioc
)) {
4147 go_io_error_display (ioc
);
4148 go_io_error_clear (ioc
);
4150 g_object_unref (ioc
);
4154 scg_paste_cellregion (SheetControlGUI
*scg
, double x
, double y
,
4155 GnmCellRegion
*content
)
4157 WorkbookControl
*wbc
= scg_wbc (scg
);
4158 Sheet
*sheet
= scg_sheet (scg
) ;
4160 SheetObjectAnchor anchor
;
4163 sheet_object_anchor_init (&anchor
, NULL
, NULL
,
4164 GOD_ANCHOR_DIR_DOWN_RIGHT
, GNM_SO_ANCHOR_TWO_CELLS
);
4165 coords
[0] = coords
[2] = x
;
4166 coords
[1] = coords
[3] = y
;
4167 scg_object_coords_to_anchor (scg
, coords
, &anchor
);
4168 paste_target_init (&pt
, sheet
, &anchor
.cell_bound
, PASTE_ALL_SHEET
);
4169 if (content
&& ((content
->cols
> 0 && content
->rows
> 0) ||
4170 content
->objects
!= NULL
))
4171 cmd_paste_copy (wbc
, &pt
, content
);
4175 scg_drag_receive_cellregion (SheetControlGUI
*scg
, double x
, double y
,
4176 const char *data
, unsigned len
)
4178 GnmCellRegion
*content
;
4179 GOIOContext
*io_context
=
4180 go_io_context_new (GO_CMD_CONTEXT (scg
->wbcg
));
4182 content
= gnm_xml_cellregion_read (scg_wbc (scg
), io_context
,
4183 scg_sheet (scg
), data
, len
);
4184 g_object_unref (io_context
);
4185 if (content
!= NULL
) {
4186 scg_paste_cellregion (scg
, x
, y
, content
);
4187 cellregion_unref (content
);
4192 scg_drag_receive_uri_list (SheetControlGUI
*scg
, double x
, double y
,
4193 const char *data
, unsigned len
)
4195 char *cdata
= g_strndup (data
, len
);
4196 GSList
*urls
= go_file_split_urls (cdata
);
4200 for (l
= urls
; l
; l
= l
-> next
) {
4201 char const *uri_str
= l
->data
;
4202 gchar
*mime
= go_get_mime_type (uri_str
);
4203 /* Note that we have imperfect detection of mime-type with some
4204 * platforms, e.g. Win32. In the worst case if
4205 * go_get_mime_type() doesn't return "application/x-gnumeric"
4206 * (registry corruption?) it will give "text/plain" and a
4207 * spreadsheet file is assumed. */
4211 if (!strncmp (mime
, "image/", 6))
4212 scg_drag_receive_img_uri (scg
, x
, y
, uri_str
);
4213 else if (!strcmp (mime
, "application/x-gnumeric") ||
4214 !strcmp (mime
, "application/vnd.ms-excel") ||
4215 !strcmp (mime
, "application/vnd.sun.xml.calc") ||
4216 !strcmp (mime
, "application/vnd.oasis.opendocument.spreadsheet") ||
4217 !strcmp (mime
, "application/vnd.lotus-1-2-3") ||
4218 !strcmp (mime
, "application/x-applix-spreadsheet") ||
4219 !strcmp (mime
, "application/x-dbase") ||
4220 !strcmp (mime
, "application/x-oleo") ||
4221 !strcmp (mime
, "application/x-quattropro") ||
4222 !strcmp (mime
, "application/x-sc") ||
4223 /* !strcmp (mime, "application/xhtml+xml") || */
4224 !strcmp (mime
, "text/spreadsheet") ||
4225 !strcmp (mime
, "text/tab-separated-values") ||
4226 !strcmp (mime
, "text/x-comma-separated-values") ||
4227 !strcmp (mime
, "text/html") ||
4228 !strcmp (mime
, "text/plain")) {
4229 scg_drag_receive_spreadsheet (scg
, uri_str
);
4231 g_printerr ("Received URI %s with mime type %s.\n", uri_str
, mime
);
4232 g_printerr ("I have no idea what to do with that.\n");
4236 g_slist_free_full (urls
, (GDestroyNotify
) g_free
);
4240 scg_drag_receive_same_process (SheetControlGUI
*scg
, GtkWidget
*source_widget
,
4243 SheetControlGUI
*source_scg
= NULL
;
4246 g_return_if_fail (source_widget
!= NULL
);
4247 g_return_if_fail (GNM_IS_PANE (source_widget
));
4249 pane
= GNM_PANE (source_widget
);
4250 x
*= goc_canvas_get_pixels_per_unit (GOC_CANVAS (pane
));
4251 y
*= goc_canvas_get_pixels_per_unit (GOC_CANVAS (pane
));
4252 source_scg
= pane
->simple
.scg
;
4253 if (source_scg
== scg
) {
4255 GdkModifierType mask
;
4256 gint64 xx
= x
, yy
= y
;
4257 gint64 origin_x
= 0, origin_y
= 0;
4259 GOUndo
*undo
= NULL
;
4260 GOUndo
*redo
= NULL
;
4261 gchar
*title
= NULL
;
4263 window
= gtk_widget_get_parent_window (GTK_WIDGET (pane
));
4264 gdk_window_get_device_position (window
,
4265 gdk_device_manager_get_client_pointer (gdk_display_get_device_manager (gdk_window_get_display (window
))),
4268 make_dup
= ((mask
& GDK_CONTROL_MASK
) != 0);
4270 /* When copying objects, we have to create a copy of current selection.
4271 * Since new objects are on top of canvas, we have to move current selection
4272 * back to original position, create a copy of selected objects, make them
4273 * the current selection, then move these objects to drop location. */
4276 xx
= origin_x
= pane
->drag
.origin_x
;
4277 yy
= origin_y
= pane
->drag
.origin_y
;
4280 gnm_pane_objects_drag (pane
, NULL
, xx
, yy
, 8, FALSE
,
4281 (mask
& GDK_SHIFT_MASK
) != 0);
4282 pane
->drag
.origin_x
= pane
->drag
.last_x
;
4283 pane
->drag
.origin_y
= pane
->drag
.last_y
;
4286 GSList
*ptr
, *objs
= go_hash_keys (scg
->selected_objects
);
4287 GOUndo
*nudge_undo
= NULL
;
4288 GOUndo
*nudge_redo
= NULL
;
4291 for (ptr
= objs
; ptr
!= NULL
; ptr
= ptr
->next
) {
4292 SheetObject
*dup_obj
= sheet_object_dup (ptr
->data
);
4293 if (dup_obj
!= NULL
) {
4294 sheet_object_set_sheet (dup_obj
, scg_sheet (scg
));
4295 scg_object_select (scg
, dup_obj
);
4296 g_object_unref (dup_obj
);
4297 scg_object_unselect (scg
, ptr
->data
);
4300 g_slist_free (objs
);
4301 scg_objects_drag_commit (scg
, 8, TRUE
, &undo
, &redo
, &title
);
4304 scg_objects_drag (scg
, pane
, NULL
, &dx
, &dy
, 8, FALSE
, FALSE
, FALSE
);
4305 scg_objects_drag_commit (scg
, 8, FALSE
, &nudge_undo
, &nudge_redo
, NULL
);
4306 undo
= go_undo_combine (undo
, nudge_undo
);
4307 redo
= go_undo_combine (nudge_redo
, redo
);
4309 scg_objects_drag_commit (scg
, 8, FALSE
, &undo
, &redo
, &title
);
4310 cmd_generic (GNM_WBC (scg_wbcg (scg
)), title
, undo
, redo
);
4313 GnmCellRegion
*content
;
4316 g_return_if_fail (GNM_IS_SCG (source_scg
));
4318 objects
= go_hash_keys (source_scg
->selected_objects
);
4319 content
= clipboard_copy_obj (scg_sheet (source_scg
),
4321 if (content
!= NULL
) {
4322 scg_paste_cellregion (scg
, x
, y
, content
);
4323 cellregion_unref (content
);
4325 g_slist_free (objects
);
4329 /* Keep in sync with gtk_selection_data_targets_include_text() */
4331 is_text_target (gchar
*target_type
)
4333 const gchar
*charset
;
4334 gchar
*text_plain_locale
;
4337 g_get_charset (&charset
);
4338 text_plain_locale
= g_strdup_printf ("text/plain;charset=%s", charset
);
4339 ret
= !strcmp (target_type
, "UTF8_STRING") ||
4340 !strcmp (target_type
, "COMPOUND_TEXT") ||
4341 !strcmp (target_type
, "TEXT") ||
4342 !strcmp (target_type
, "STRING") ||
4343 !strcmp (target_type
, "text/plain;charset=utf-8") ||
4344 !strcmp (target_type
, text_plain_locale
) ||
4345 !strcmp (target_type
, "text/plain");
4346 g_free (text_plain_locale
);
4351 scg_drag_data_received (SheetControlGUI
*scg
, GtkWidget
*source_widget
,
4352 double x
, double y
, GtkSelectionData
*selection_data
)
4354 gchar
*target_type
= gdk_atom_name (gtk_selection_data_get_target (selection_data
));
4355 const char *sel_data
= (const char *)gtk_selection_data_get_data (selection_data
);
4356 gsize sel_len
= gtk_selection_data_get_length (selection_data
);
4358 if (!strcmp (target_type
, "text/uri-list")) {
4359 scg_drag_receive_uri_list (scg
, x
, y
, sel_data
, sel_len
);
4361 } else if (!strncmp (target_type
, "image/", 6)) {
4362 scg_drag_receive_img_data (scg
, x
, y
, sel_data
, sel_len
);
4363 } else if (!strcmp (target_type
, "GNUMERIC_SAME_PROC")) {
4364 scg_drag_receive_same_process (scg
, source_widget
, x
, y
);
4365 } else if (!strcmp (target_type
, "application/x-gnumeric")) {
4366 scg_drag_receive_cellregion (scg
, x
, y
, sel_data
, sel_len
);
4368 g_warning ("Unknown target type '%s'!", target_type
);
4370 if (gnm_debug_flag ("dnd")) {
4371 if (!strcmp (target_type
, "x-special/gnome-copied-files")) {
4372 char *cdata
= g_strndup (sel_data
, sel_len
);
4373 g_print ("data length: %d, data: %s\n",
4374 (int)sel_len
, cdata
);
4376 } else if (!strcmp (target_type
, "_NETSCAPE_URL")) {
4377 char *cdata
= g_strndup (sel_data
, sel_len
);
4378 g_print ("data length: %d, data: %s\n",
4379 (int)sel_len
, cdata
);
4381 } else if (is_text_target (target_type
)) {
4382 char *cdata
= g_strndup (sel_data
, sel_len
);
4383 g_print ("data length: %d, data: %s\n",
4384 (int)sel_len
, cdata
);
4386 } else if (!strcmp (target_type
, "text/html")) {
4387 char *cdata
= g_strndup (sel_data
, sel_len
);
4388 /* For mozilla, need to convert the encoding */
4389 g_print ("data length: %d, data: %s\n", (int)sel_len
, cdata
);
4394 g_free (target_type
);
4398 scg_drag_send_image (G_GNUC_UNUSED SheetControlGUI
*scg
,
4399 GtkSelectionData
*selection_data
,
4401 gchar
const *mime_type
)
4403 SheetObject
*so
= NULL
;
4405 GsfOutputMemory
*omem
;
4410 for (ptr
= objects
; ptr
!= NULL
; ptr
= ptr
->next
) {
4411 if (GNM_IS_SO_IMAGEABLE (GNM_SO (ptr
->data
))) {
4412 so
= GNM_SO (ptr
->data
);
4417 g_warning ("non imageable object requested as image\n");
4421 format
= go_mime_to_image_format (mime_type
);
4423 g_warning ("No image format for %s\n", mime_type
);
4428 output
= gsf_output_memory_new ();
4429 omem
= GSF_OUTPUT_MEMORY (output
);
4430 sheet_object_write_image (so
, format
, -1.0, output
, NULL
);
4431 osize
= gsf_output_size (output
);
4433 gtk_selection_data_set
4435 gtk_selection_data_get_target (selection_data
),
4436 8, gsf_output_memory_get_bytes (omem
), osize
);
4437 gsf_output_close (output
);
4438 g_object_unref (output
);
4443 scg_drag_send_graph (G_GNUC_UNUSED SheetControlGUI
*scg
,
4444 GtkSelectionData
*selection_data
,
4446 gchar
const *mime_type
)
4448 SheetObject
*so
= NULL
;
4450 GsfOutputMemory
*omem
;
4454 for (ptr
= objects
; ptr
!= NULL
; ptr
= ptr
->next
)
4455 if (GNM_IS_SO_EXPORTABLE (GNM_SO (ptr
->data
))) {
4456 so
= GNM_SO (ptr
->data
);
4461 g_warning ("non exportable object requested\n");
4465 output
= gsf_output_memory_new ();
4466 omem
= GSF_OUTPUT_MEMORY (output
);
4467 sheet_object_write_object (so
, mime_type
, output
, NULL
,
4468 gnm_conventions_default
);
4469 osize
= gsf_output_size (output
);
4471 gtk_selection_data_set
4473 gtk_selection_data_get_target (selection_data
),
4474 8, gsf_output_memory_get_bytes (omem
), osize
);
4475 gsf_output_close (output
);
4476 g_object_unref (output
);
4480 scg_drag_send_clipboard_objects (SheetControl
*sc
,
4481 GtkSelectionData
*selection_data
,
4484 GnmCellRegion
*content
= clipboard_copy_obj (sc_sheet (sc
), objects
);
4485 GsfOutputMemory
*output
;
4487 if (content
== NULL
)
4490 output
= gnm_cellregion_to_xml (content
);
4491 gtk_selection_data_set
4493 gtk_selection_data_get_target (selection_data
),
4495 gsf_output_memory_get_bytes (output
),
4496 gsf_output_size (GSF_OUTPUT (output
)));
4497 g_object_unref (output
);
4498 cellregion_unref (content
);
4502 scg_drag_send_text (SheetControlGUI
*scg
, GtkSelectionData
*sd
)
4504 Sheet
*sheet
= scg_sheet (scg
);
4505 GnmRange range
= sheet_get_extent (sheet
, TRUE
, TRUE
);
4506 GnmCellRegion
*reg
= clipboard_copy_range (sheet
, &range
);
4507 GString
*s
= cellregion_to_string (reg
, TRUE
, sheet_date_conv (sheet
));
4509 cellregion_unref (reg
);
4512 gtk_selection_data_set (sd
, gtk_selection_data_get_target (sd
),
4514 g_string_free (s
, TRUE
);
4518 scg_drag_data_get (SheetControlGUI
*scg
, GtkSelectionData
*selection_data
)
4520 GdkAtom target
= gtk_selection_data_get_target (selection_data
);
4521 gchar
*target_name
= gdk_atom_name (target
);
4522 GSList
*objects
= scg
->selected_objects
4523 ? go_hash_keys (scg
->selected_objects
)
4526 if (strcmp (target_name
, "GNUMERIC_SAME_PROC") == 0)
4527 /* Set dummy selection for process internal dnd */
4528 gtk_selection_data_set (selection_data
, target
,
4529 8, (const guint8
*)"", 1);
4530 else if (strcmp (target_name
, "GNUMERIC_SHEET") == 0)
4531 gtk_selection_data_set (selection_data
, target
,
4532 8, (void *)scg
, sizeof (scg
));
4533 else if (strcmp (target_name
, "application/x-gnumeric") == 0)
4534 scg_drag_send_clipboard_objects (GNM_SHEET_CONTROL (scg
),
4535 selection_data
, objects
);
4536 else if (strcmp (target_name
, "application/x-goffice-graph") == 0)
4537 scg_drag_send_graph (scg
, selection_data
, objects
, target_name
);
4538 else if (strncmp (target_name
, "image/", 6) == 0)
4539 scg_drag_send_image (scg
, selection_data
, objects
, target_name
);
4540 else if (strcmp (target_name
, "UTF8_STRING") == 0)
4541 scg_drag_send_text (scg
, selection_data
);
4543 g_free (target_name
);
4544 g_slist_free (objects
);
4548 scg_delete_sheet_if_possible (SheetControlGUI
*scg
)
4550 SheetControl
*sc
= (SheetControl
*) scg
;
4551 Sheet
*sheet
= scg_sheet (scg
);
4552 Workbook
*wb
= sheet
->workbook
;
4554 /* If this is the last sheet left, ignore the request */
4555 if (workbook_sheet_count (wb
) != 1) {
4556 WorkbookSheetState
*old_state
= workbook_sheet_state_new (wb
);
4557 WorkbookControl
*wbc
= sc
->wbc
;
4558 workbook_sheet_delete (sheet
);
4559 /* Careful: sc just ceased to be valid. */
4560 cmd_reorganize_sheets (wbc
, old_state
, sheet
);
4565 scg_reload_item_edits (SheetControlGUI
*scg
)
4567 SCG_FOREACH_PANE (scg
, pane
, {
4568 if (pane
->editor
!= NULL
)
4569 goc_item_bounds_changed
4570 (GOC_ITEM (pane
->editor
));