1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * sheet-control-gui.c: Implements a graphic control for a sheet.
5 * Copyright (C) 2000-2006 Jody Goldberg (jody@gnome.org)
6 * Copyright (C) 1997-1999 Miguel de Icaza (miguel@kernel.org)
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) version 3.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
23 #include <gnumeric-config.h>
24 #include <glib/gi18n-lib.h>
26 #include "sheet-control-gui-priv.h"
29 #include "sheet-private.h"
30 #include "sheet-view.h"
31 #include "sheet-merge.h"
33 #include "workbook-view.h"
34 #include "workbook-cmd-format.h"
35 #include "wbc-gtk-impl.h"
37 #include "selection.h"
39 #include "sheet-style.h"
40 #include "sheet-object-impl.h"
41 #include "sheet-object-cell-comment.h"
42 #include "sheet-object-image.h"
45 #include "parse-util.h"
46 #include "selection.h"
47 #include "application.h"
51 #include "gnm-commands-slicer.h"
52 #include "clipboard.h"
55 #include "sheet-merge.h"
58 #include "style-color.h"
59 #include "gnumeric-conf.h"
61 #include "gnm-pane-impl.h"
63 #include "item-cursor.h"
64 #include "widgets/gnumeric-expr-entry.h"
65 #include "gnm-sheet-slicer.h"
66 #include "input-msg.h"
68 #include <go-data-slicer-field.h>
69 #include <goffice/goffice.h>
71 #include <gdk/gdkkeysyms.h>
72 #include <gsf/gsf-impl-utils.h>
73 #include <gsf/gsf-input.h>
74 #include <gsf/gsf-output-memory.h>
79 static GObjectClass
*scg_parent_class
;
81 static void scg_unant (SheetControl
*sc
);
82 static void set_resize_pane_pos (SheetControlGUI
*scg
, GtkPaned
*p
);
83 static void cb_resize_pane_motion (GtkPaned
*p
, GParamSpec
*pspec
, SheetControlGUI
*scg
);
88 * @scg: #SheetControlGUI
89 * @pane: the pane index.
91 * Returns: (transfer none): the pane.
94 scg_pane (SheetControlGUI
*scg
, int p
)
96 /* it is ok to request a pane when we are not frozen */
97 g_return_val_if_fail (GNM_IS_SCG (scg
), NULL
);
98 g_return_val_if_fail (p
>= 0, NULL
);
99 g_return_val_if_fail (p
< 4, NULL
);
106 * @scg: #SheetControlGUI
108 * Returns: (transfer none): the sheet view.
111 scg_view (SheetControlGUI
const *scg
)
113 g_return_val_if_fail (GNM_IS_SCG (scg
), NULL
);
114 return scg
->sheet_control
.view
;
120 * @scg: #SheetControlGUI
122 * Returns: (transfer none): the sheet.
125 scg_sheet (SheetControlGUI
const *scg
)
127 return sc_sheet ((SheetControl
*)scg
);
133 * @scg: #SheetControlGUI
135 * Returns: (transfer none): the workbook control.
138 scg_wbc (SheetControlGUI
const *scg
)
140 g_return_val_if_fail (GNM_IS_SCG (scg
), NULL
);
141 return scg
->sheet_control
.wbc
;
147 * @scg: #SheetControlGUI
149 * Returns: (transfer none): the #WBCGtk.
153 scg_wbcg (SheetControlGUI
const *scg
)
155 g_return_val_if_fail (GNM_IS_SCG (scg
), NULL
);
160 scg_redraw_all (SheetControl
*sc
, gboolean headers
)
162 SheetControlGUI
*scg
= (SheetControlGUI
*)sc
;
164 g_return_if_fail (GNM_IS_SCG (scg
));
166 SCG_FOREACH_PANE (scg
, pane
, {
167 goc_canvas_invalidate (GOC_CANVAS (pane
),
168 G_MININT64
, 0, G_MAXINT64
, G_MAXINT64
);
170 if (NULL
!= pane
->col
.canvas
)
171 goc_canvas_invalidate (pane
->col
.canvas
,
172 0, 0, G_MAXINT64
, G_MAXINT64
);
173 if (NULL
!= pane
->row
.canvas
)
174 goc_canvas_invalidate (pane
->row
.canvas
,
175 0, 0, G_MAXINT64
, G_MAXINT64
);
181 scg_redraw_range (SheetControl
*sc
, GnmRange
const *r
)
183 SheetControlGUI
*scg
= (SheetControlGUI
*)sc
;
184 Sheet
const *sheet
= scg_sheet (scg
);
185 GnmRange visible
, area
;
188 * Getting the bounding box causes row respans to be done if
189 * needed. That can be expensive, so just redraw the whole
190 * sheet if the row count is too big.
192 if (r
->end
.row
- r
->start
.row
> 500) {
193 scg_redraw_all (sc
, FALSE
);
197 /* We potentially do a lot of recalcs as part of this, so make sure
198 stuff that caches sub-computations see the whole thing instead
199 of clearing between cells. */
200 gnm_app_recalc_start ();
202 SCG_FOREACH_PANE (scg
, pane
, {
203 visible
.start
= pane
->first
;
204 visible
.end
= pane
->last_visible
;
206 if (range_intersection (&area
, r
, &visible
)) {
207 sheet_range_bounding_box (sheet
, &area
);
208 gnm_pane_redraw_range (pane
, &area
);
212 gnm_app_recalc_finish ();
216 scg_redraw_headers (SheetControl
*sc
,
217 gboolean
const col
, gboolean
const row
,
218 GnmRange
const * r
/* optional == NULL */)
220 SheetControlGUI
*scg
= (SheetControlGUI
*)sc
;
226 * A rough guess of the trade off point between of redrawing all
227 * and calculating the redraw size
229 const int COL_HEURISTIC
= 20;
230 const int ROW_HEURISTIC
= 50;
232 for (i
= scg
->active_panes
; i
-- > 0 ; ) {
233 if (NULL
== (pane
= scg
->pane
[i
]))
236 if (col
&& pane
->col
.canvas
!= NULL
) {
237 int left
= 0, right
= G_MAXINT
- 1;
238 GocCanvas
* const col_canvas
= GOC_CANVAS (pane
->col
.canvas
);
239 scale
= goc_canvas_get_pixels_per_unit (col_canvas
);
242 int const size
= r
->end
.col
- r
->start
.col
;
243 if (-COL_HEURISTIC
< size
&& size
< COL_HEURISTIC
) {
244 left
= pane
->first_offset
.x
+
245 scg_colrow_distance_get (scg
, TRUE
,
246 pane
->first
.col
, r
->start
.col
);
248 scg_colrow_distance_get (scg
, TRUE
,
249 r
->start
.col
, r
->end
.col
+1);
252 goc_canvas_invalidate (col_canvas
,
253 left
/ scale
, 0, right
/ scale
, G_MAXINT64
);
256 if (row
&& pane
->row
.canvas
!= NULL
) {
257 gint64 top
= 0, bottom
= G_MAXINT64
- 1;
258 scale
= goc_canvas_get_pixels_per_unit (pane
->row
.canvas
);
260 int const size
= r
->end
.row
- r
->start
.row
;
261 if (-ROW_HEURISTIC
< size
&& size
< ROW_HEURISTIC
) {
262 top
= pane
->first_offset
.y
+
263 scg_colrow_distance_get (scg
, FALSE
,
264 pane
->first
.row
, r
->start
.row
);
266 scg_colrow_distance_get (scg
, FALSE
,
267 r
->start
.row
, r
->end
.row
+1);
270 goc_canvas_invalidate (GOC_CANVAS (pane
->row
.canvas
),
271 0, top
/ scale
, G_MAXINT64
, bottom
/ scale
);
277 cb_outline_button (GtkWidget
*btn
, SheetControlGUI
*scg
)
279 SheetControl
*sc
= (SheetControl
*) scg
;
280 WorkbookControl
*wbc
= sc
->wbc
;
281 GPtrArray
const *btns
;
283 gboolean is_cols
= g_object_get_data (G_OBJECT (btn
), "is_cols") != NULL
;
286 btns
= is_cols
? scg
->col_group
.buttons
: scg
->row_group
.buttons
;
287 for (i
= 0; i
< btns
->len
; i
++)
288 if (g_ptr_array_index (btns
, i
) == btn
)
291 g_return_if_fail (i
< btns
->len
);
293 cmd_global_outline_change (wbc
, is_cols
, i
+1);
297 scg_setup_group_buttons (SheetControlGUI
*scg
, unsigned max_outline
,
298 GnmItemBar
const *ib
, gboolean is_cols
, int w
, int h
,
299 GPtrArray
*btns
, GtkWidget
*box
)
301 PangoFontDescription
*font_desc
;
303 Sheet
const *sheet
= scg_sheet (scg
);
305 if (!sheet
->display_outlines
)
307 else if (max_outline
> 0)
310 while (btns
->len
> max_outline
) {
311 GtkWidget
*w
= g_ptr_array_remove_index_fast (btns
, btns
->len
- 1);
312 gtk_container_remove (GTK_CONTAINER (box
),
313 gtk_widget_get_parent (w
));
316 while (btns
->len
< max_outline
) {
317 GtkWidget
*out
= gtk_alignment_new (.5, .5, 1., 1.);
318 GtkWidget
*in
= gtk_alignment_new (.5, .5, 0., 0.);
319 GtkWidget
*btn
= gtk_button_new ();
320 char *tmp
= g_strdup_printf ("<small>%d</small>", btns
->len
+1);
321 GtkWidget
*label
= gtk_label_new (NULL
);
322 gtk_label_set_markup (GTK_LABEL (label
), tmp
);
325 gtk_widget_set_can_focus (btn
, FALSE
);
326 gtk_container_add (GTK_CONTAINER (in
), label
);
327 gtk_container_add (GTK_CONTAINER (btn
), in
);
328 gtk_container_add (GTK_CONTAINER (out
), btn
);
329 gtk_box_pack_start (GTK_BOX (box
), out
, TRUE
, TRUE
, 0);
330 g_ptr_array_add (btns
, btn
);
332 g_signal_connect (G_OBJECT (btn
),
334 G_CALLBACK (cb_outline_button
), scg
);
336 g_object_set_data (G_OBJECT (btn
),
337 "is_cols", GINT_TO_POINTER (1));
340 font_desc
= item_bar_normal_font (ib
);
342 /* size all of the button so things work after a zoom */
343 for (i
= 0 ; i
< btns
->len
; i
++) {
344 GtkWidget
*btn
= g_ptr_array_index (btns
, i
);
345 GtkWidget
*label
= gtk_bin_get_child (GTK_BIN (gtk_bin_get_child (GTK_BIN (btn
))));
346 gtk_widget_set_size_request (GTK_WIDGET (btn
), w
, h
);
347 gtk_widget_override_font (label
, font_desc
);
350 pango_font_description_free (font_desc
);
351 gtk_widget_show_all (box
);
355 scg_resize (SheetControlGUI
*scg
, G_GNUC_UNUSED gboolean force_scroll
)
357 Sheet
const *sheet
= scg_sheet (scg
);
358 GnmPane
*pane
= scg_pane (scg
, 0);
359 int h
, w
, btn_h
, btn_w
, tmp
;
364 /* Recalibrate the starting offsets */
365 pane
->first_offset
.x
= scg_colrow_distance_get (scg
,
366 TRUE
, 0, pane
->first
.col
);
367 pane
->first_offset
.y
= scg_colrow_distance_get (scg
,
368 FALSE
, 0, pane
->first
.row
);
370 /* resize Pane[0] headers */
371 h
= gnm_item_bar_calc_size (scg
->pane
[0]->col
.item
);
372 btn_h
= h
- gnm_item_bar_indent (scg
->pane
[0]->col
.item
);
373 w
= gnm_item_bar_calc_size (scg
->pane
[0]->row
.item
);
374 btn_w
= w
- gnm_item_bar_indent (scg
->pane
[0]->row
.item
);
375 gtk_widget_set_size_request (scg
->select_all_btn
, btn_w
, btn_h
);
376 gtk_widget_set_size_request (GTK_WIDGET (scg
->pane
[0]->col
.canvas
), -1, h
);
377 gtk_widget_set_size_request (GTK_WIDGET (scg
->pane
[0]->row
.canvas
), w
, -1);
379 tmp
= gnm_item_bar_group_size (scg
->pane
[0]->col
.item
,
380 sheet
->cols
.max_outline_level
);
381 scg_setup_group_buttons (scg
, sheet
->cols
.max_outline_level
,
382 scg
->pane
[0]->col
.item
, TRUE
,
383 tmp
, tmp
, scg
->col_group
.buttons
, scg
->col_group
.button_box
);
384 scg_setup_group_buttons (scg
, sheet
->rows
.max_outline_level
,
385 scg
->pane
[0]->row
.item
, FALSE
,
386 -1, btn_h
, scg
->row_group
.buttons
, scg
->row_group
.button_box
);
388 if (scg
->active_panes
!= 1 && sv_is_frozen (scg_view (scg
))) {
389 GnmCellPos
const *tl
= &scg_view (scg
)->frozen_top_left
;
390 GnmCellPos
const *br
= &scg_view (scg
)->unfrozen_top_left
;
391 int const l
= scg_colrow_distance_get (scg
, TRUE
,
393 int const r
= scg_colrow_distance_get (scg
, TRUE
,
394 tl
->col
, br
->col
) + l
;
395 int const t
= scg_colrow_distance_get (scg
, FALSE
,
397 int const b
= scg_colrow_distance_get (scg
, FALSE
,
398 tl
->row
, br
->row
) + t
;
400 int fw
= MIN (scg
->screen_width
, r
- l
);
401 int fh
= MIN (scg
->screen_height
, b
- t
);
403 /* pane 0 has already been done */
404 for (i
= scg
->active_panes
; i
-- > 1 ; ) {
405 GnmPane
*pane
= scg
->pane
[i
];
407 pane
->first_offset
.x
= scg_colrow_distance_get (
408 scg
, TRUE
, 0, pane
->first
.col
);
409 pane
->first_offset
.y
= scg_colrow_distance_get (
410 scg
, FALSE
, 0, pane
->first
.row
);
415 if (gnm_debug_flag ("frozen-panes"))
416 g_printerr ("Pane 1: %d\n", r
- l
);
418 gtk_widget_set_size_request (GTK_WIDGET (scg
->pane
[1]), fw
, -1);
419 /* The item_bar_calcs should be equal */
420 /* FIXME : The canvas gets confused when the initial scroll
421 * region is set too early in its life cycle.
422 * It likes it to be at the origin, we can live with that for now.
423 * However, we really should track the bug eventually.
425 h
= gnm_item_bar_calc_size (scg
->pane
[1]->col
.item
);
426 gtk_widget_set_size_request (GTK_WIDGET (scg
->pane
[1]->col
.canvas
), fw
, h
);
430 if (gnm_debug_flag ("frozen-panes"))
431 g_printerr ("Pane 2: %d\n", b
- t
);
433 gtk_widget_set_size_request (GTK_WIDGET (scg
->pane
[3]), -1, fh
);
434 /* The item_bar_calcs should be equal */
435 w
= gnm_item_bar_calc_size (scg
->pane
[3]->row
.item
);
436 gtk_widget_set_size_request (GTK_WIDGET (scg
->pane
[3]->row
.canvas
), w
, fh
);
440 if (gnm_debug_flag ("frozen-panes"))
441 g_printerr ("Pane 3: %d %d\n", r
- l
, b
- t
);
443 gtk_widget_set_size_request (GTK_WIDGET (scg
->pane
[2]), fw
, fh
);
447 SCG_FOREACH_PANE (scg
, pane
, {
448 gnm_pane_reposition_cursors (pane
);
453 scg_resize_virt (SheetControl
*sc
, gboolean force_scroll
)
455 scg_resize ((SheetControlGUI
*)sc
, force_scroll
);
459 gnm_adjustment_configure (GtkAdjustment
*adjustment
,
463 gdouble step_increment
,
464 gdouble page_increment
,
467 g_object_freeze_notify (G_OBJECT (adjustment
));
469 // These do nothing if value isn't changed
470 gtk_adjustment_set_lower (adjustment
, lower
);
471 gtk_adjustment_set_upper (adjustment
, upper
);
472 gtk_adjustment_set_step_increment (adjustment
, step_increment
);
473 gtk_adjustment_set_page_increment (adjustment
, page_increment
);
474 gtk_adjustment_set_page_size (adjustment
, page_size
);
476 g_object_thaw_notify (G_OBJECT (adjustment
));
478 // These fire signals if nothing changes, so check by hand
479 if (!(gtk_adjustment_get_value (adjustment
) == value
))
480 gtk_adjustment_set_value (adjustment
, value
);
485 * scg_scrollbar_config:
488 * Manages the scrollbar dimensions and paging parameters.
489 * Currently sizes things based on the cols/rows visible in pane-0. This has
490 * several subtleties.
492 * 1) Using cols/rows instead of pixels means that the scrollbar changes size
493 * as it passes through regions with different sized cols/rows.
495 * 2) It does NOT take into account hidden rows/cols So a region that contains
496 * a large hidden segment will appear larger.
498 * 3) It only uses pane-0 because that is the only one that can scroll in both
499 * dimensions. The others are of fixed size.
502 scg_scrollbar_config_real (SheetControl
const *sc
)
504 SheetControlGUI
*scg
= GNM_SCG (sc
);
505 GtkAdjustment
*va
= scg
->va
;
506 GtkAdjustment
*ha
= scg
->ha
;
507 GnmPane
*pane
= scg_pane (scg
, 0);
508 SheetView
const *sv
= sc
->view
;
509 Sheet
const *sheet
= sv
->sheet
;
512 int const last_col
= pane
->last_full
.col
;
513 int const last_row
= pane
->last_full
.row
;
514 int max_col
= last_col
;
515 int max_row
= last_row
;
517 if (max_row
< sheet
->rows
.max_used
)
518 max_row
= sheet
->rows
.max_used
;
519 if (max_row
< sheet
->max_object_extent
.row
)
520 max_row
= sheet
->max_object_extent
.row
;
521 gnm_adjustment_configure
524 sv_is_frozen (sv
) ? sv
->unfrozen_top_left
.row
: 0,
527 MAX (gtk_adjustment_get_page_size (va
) - 3.0, 1.0),
528 last_row
- pane
->first
.row
+ 1);
530 if (max_col
< sheet
->cols
.max_used
)
531 max_col
= sheet
->cols
.max_used
;
532 if (max_col
< sheet
->max_object_extent
.col
)
533 max_col
= sheet
->max_object_extent
.col
;
534 gnm_adjustment_configure
537 sv_is_frozen (sv
) ? sv
->unfrozen_top_left
.col
: 0,
540 MAX (gtk_adjustment_get_page_size (ha
) - 3.0, 1.0),
541 last_col
- pane
->first
.col
+ 1);
544 scg
->scroll_bar_timer
= 0;
550 scg_scrollbar_config (SheetControl
*sc
)
552 SheetControlGUI
*scg
= GNM_SCG (sc
);
554 if (!scg
->scroll_bar_timer
)
555 scg
->scroll_bar_timer
=
557 (GSourceFunc
) scg_scrollbar_config_real
,
562 scg_colrow_size_set (SheetControlGUI
*scg
,
563 gboolean is_cols
, int index
, int new_size_pixels
)
565 WorkbookControl
*wbc
= scg_wbc (scg
);
566 SheetView
*sv
= scg_view (scg
);
568 /* If all cols/rows in the selection are completely selected
569 * then resize all of them, otherwise just resize the selected col/row.
571 if (!sv_is_full_colrow_selected (sv
, is_cols
, index
))
572 cmd_resize_colrow (wbc
, sv
->sheet
, is_cols
,
573 colrow_get_index_list (index
, index
, NULL
), new_size_pixels
);
575 workbook_cmd_resize_selected_colrow (wbc
, sv
->sheet
, is_cols
,
580 scg_select_all (SheetControlGUI
*scg
)
582 Sheet
*sheet
= scg_sheet (scg
);
583 gboolean
const rangesel
= wbcg_rangesel_possible (scg
->wbcg
);
586 scg_rangesel_bound (scg
,
587 0, 0, gnm_sheet_get_last_col (sheet
), gnm_sheet_get_last_row (sheet
));
588 gnm_expr_entry_signal_update (
589 wbcg_get_entry_logical (scg
->wbcg
), TRUE
);
590 } else if (wbc_gtk_get_guru (scg
->wbcg
) == NULL
) {
591 SheetView
*sv
= scg_view (scg
);
594 wbcg_edit_finish (scg
->wbcg
, WBC_EDIT_REJECT
, NULL
);
595 sv_selection_reset (sv
);
596 sv_selection_add_full (sv
, sv
->edit_pos
.col
, sv
->edit_pos
.row
,
597 0, 0, gnm_sheet_get_last_col (sheet
),
598 gnm_sheet_get_last_row (sheet
),
599 GNM_SELECTION_MODE_ADD
);
601 sheet_update (sheet
);
605 scg_colrow_select (SheetControlGUI
*scg
, gboolean is_cols
,
606 int index
, int modifiers
)
608 SheetView
*sv
= scg_view (scg
);
609 gboolean
const rangesel
= wbcg_rangesel_possible (scg
->wbcg
);
612 !wbcg_edit_finish (scg
->wbcg
, WBC_EDIT_ACCEPT
, NULL
))
615 if (modifiers
& GDK_SHIFT_MASK
) {
618 scg_rangesel_extend_to (scg
, index
, -1);
620 scg_rangesel_extend_to (scg
, -1, index
);
623 sv_selection_extend_to (sv
, index
, -1);
625 sv_selection_extend_to (sv
, -1, index
);
628 if (!rangesel
&& !(modifiers
& GDK_CONTROL_MASK
))
629 sv_selection_reset (sv
);
633 scg_rangesel_bound (scg
,
634 index
, 0, index
, gnm_sheet_get_last_row (sv
->sheet
));
636 scg_rangesel_bound (scg
,
637 0, index
, gnm_sheet_get_last_col (sv
->sheet
), index
);
638 } else if (is_cols
) {
640 scg_pane (scg
, scg
->pane
[3] ? 3 : 0);
641 sv_selection_add_full (sv
,
642 index
, pane
->first
.row
,
644 index
, gnm_sheet_get_last_row (sv
->sheet
),
645 GNM_SELECTION_MODE_ADD
);
648 scg_pane (scg
, scg
->pane
[1] ? 1 : 0);
649 sv_selection_add_full (sv
,
650 pane
->first
.col
, index
,
652 gnm_sheet_get_last_col (sv
->sheet
), index
,
653 GNM_SELECTION_MODE_ADD
);
657 /* The edit pos, and the selection may have changed */
659 sheet_update (sv
->sheet
);
663 /***************************************************************************/
666 cb_select_all_btn_draw (GtkWidget
*widget
, cairo_t
*cr
, SheetControlGUI
*scg
)
668 int offset
= scg_sheet (scg
)->text_is_rtl
? -1 : 0;
670 GtkStyleContext
*ctxt
= gtk_widget_get_style_context (widget
);
672 gtk_widget_get_allocation (widget
, &a
);
674 gtk_style_context_save (ctxt
);
675 gtk_style_context_set_state (ctxt
, GTK_STATE_FLAG_NORMAL
);
676 gtk_render_background (ctxt
, cr
, offset
+ 1, 1,
677 a
.width
- 1, a
.height
- 1);
678 gtk_render_frame (ctxt
, cr
, offset
, 0, a
.width
+ 1, a
.height
+ 1);
679 gtk_style_context_restore (ctxt
);
683 cb_select_all_btn_event (G_GNUC_UNUSED GtkWidget
*widget
, GdkEvent
*event
, SheetControlGUI
*scg
)
685 if (event
->type
== GDK_BUTTON_PRESS
) {
686 scg_select_all (scg
);
694 cb_vscrollbar_value_changed (GtkRange
*range
, SheetControlGUI
*scg
)
696 GtkAdjustment
*adj
= gtk_range_get_adjustment (range
);
697 scg_set_top_row (scg
, gtk_adjustment_get_value (adj
));
701 cb_hscrollbar_value_changed (GtkRange
*range
, SheetControlGUI
*scg
)
703 GtkAdjustment
*adj
= gtk_range_get_adjustment (range
);
704 scg_set_left_col (scg
, gtk_adjustment_get_value (adj
));
708 cb_hscrollbar_adjust_bounds (GtkRange
*range
, gdouble new_value
, Sheet
*sheet
)
710 GtkAdjustment
*adj
= gtk_range_get_adjustment (range
);
711 double upper
= gtk_adjustment_get_upper (adj
);
712 double page_size
= gtk_adjustment_get_page_size (adj
);
713 gdouble limit
= upper
- page_size
;
714 if (upper
< gnm_sheet_get_max_cols (sheet
) && new_value
>= limit
) {
715 upper
= new_value
+ page_size
+ 1;
716 if (upper
> gnm_sheet_get_max_cols (sheet
))
717 upper
= gnm_sheet_get_max_cols (sheet
);
718 gtk_adjustment_set_upper (adj
, upper
);
722 cb_vscrollbar_adjust_bounds (GtkRange
*range
, gdouble new_value
, Sheet
*sheet
)
724 GtkAdjustment
*adj
= gtk_range_get_adjustment (range
);
725 double upper
= gtk_adjustment_get_upper (adj
);
726 double page_size
= gtk_adjustment_get_page_size (adj
);
727 gdouble limit
= upper
- page_size
;
728 if (upper
< gnm_sheet_get_max_rows (sheet
) && new_value
>= limit
) {
729 upper
= new_value
+ page_size
+ 1;
730 if (upper
> gnm_sheet_get_max_rows (sheet
))
731 upper
= gnm_sheet_get_max_rows (sheet
);
732 gtk_adjustment_set_upper (adj
, upper
);
737 cb_table_destroy (SheetControlGUI
*scg
)
739 SheetControl
*sc
= (SheetControl
*) scg
;
742 g_clear_object (&scg
->grid
);
744 scg_mode_edit (scg
); /* finish any object edits */
745 scg_unant (sc
); /* Make sure that everything is unanted */
748 GtkWindow
*toplevel
= wbcg_toplevel (scg
->wbcg
);
750 /* Only pane-0 ever gets focus */
751 if (NULL
!= toplevel
&&
752 gtk_window_get_focus (toplevel
) == GTK_WIDGET (scg_pane (scg
, 0)))
753 gtk_window_set_focus (toplevel
, NULL
);
756 for (i
= scg
->active_panes
; i
-- > 0 ; )
757 if (NULL
!= scg
->pane
[i
]) {
758 gtk_widget_destroy (GTK_WIDGET (scg
->pane
[i
]));
762 g_object_unref (scg
);
766 scg_init (SheetControlGUI
*scg
)
768 scg
->comment
.selected
= NULL
;
769 scg
->comment
.item
= NULL
;
770 scg
->comment
.timer
= 0;
772 scg
->delayedMovement
.timer
= 0;
773 scg
->delayedMovement
.handler
= NULL
;
776 scg
->selected_objects
= NULL
;
781 // These shouldn't matter and will be overwritten
782 scg
->screen_width
= 1920;
783 scg
->screen_height
= 1200;
786 /*************************************************************************/
789 * gnm_pane_update_inital_top_left:
790 * A convenience routine to store the new topleft back in the view.
793 gnm_pane_update_inital_top_left (GnmPane
const *pane
)
795 if (pane
->index
== 0) {
796 SheetView
*sv
= scg_view (pane
->simple
.scg
);
797 sv
->initial_top_left
= pane
->first
;
802 bar_set_left_col (GnmPane
*pane
, int new_first_col
)
808 col_offset
= pane
->first_offset
.x
+=
809 scg_colrow_distance_get (pane
->simple
.scg
, TRUE
, pane
->first
.col
, new_first_col
);
810 pane
->first
.col
= new_first_col
;
812 /* Scroll the column headers */
813 if (NULL
!= (colc
= pane
->col
.canvas
))
814 goc_canvas_scroll_to (colc
, col_offset
/ colc
->pixels_per_unit
, 0);
820 gnm_pane_set_left_col (GnmPane
*pane
, int new_first_col
)
823 g_return_if_fail (pane
!= NULL
);
824 sheet
= scg_sheet (pane
->simple
.scg
);
825 g_return_if_fail (0 <= new_first_col
&& new_first_col
< gnm_sheet_get_max_cols (sheet
));
827 if (pane
->first
.col
!= new_first_col
) {
828 GocCanvas
* const canvas
= GOC_CANVAS (pane
);
829 gint64
const col_offset
= bar_set_left_col (pane
, new_first_col
);
831 gnm_pane_compute_visible_region (pane
, FALSE
);
832 goc_canvas_scroll_to (canvas
, col_offset
/ canvas
->pixels_per_unit
, pane
->first_offset
.y
/ canvas
->pixels_per_unit
);
833 gnm_pane_update_inital_top_left (pane
);
838 scg_set_left_col (SheetControlGUI
*scg
, int col
)
841 GnmRange
const *bound
;
843 g_return_if_fail (GNM_IS_SCG (scg
));
845 sheet
= scg_sheet (scg
);
846 bound
= &sheet
->priv
->unhidden_region
;
847 if (col
< bound
->start
.col
)
848 col
= bound
->start
.col
;
849 else if (col
>= gnm_sheet_get_max_cols (sheet
))
850 col
= gnm_sheet_get_last_col (sheet
);
851 else if (col
> bound
->end
.col
)
852 col
= bound
->end
.col
;
855 int right
= scg_view (scg
)->unfrozen_top_left
.col
;
860 gnm_pane_set_left_col (scg_pane (scg
, 3), col
);
861 gnm_pane_set_left_col (scg_pane (scg
, 0), col
);
865 bar_set_top_row (GnmPane
*pane
, int new_first_row
)
870 row_offset
= pane
->first_offset
.y
+=
871 scg_colrow_distance_get (pane
->simple
.scg
, FALSE
, pane
->first
.row
, new_first_row
);
872 pane
->first
.row
= new_first_row
;
874 /* Scroll the row headers */
875 if (NULL
!= (rowc
= pane
->row
.canvas
))
876 goc_canvas_scroll_to (rowc
, 0, row_offset
/ rowc
->pixels_per_unit
);
882 gnm_pane_set_top_row (GnmPane
*pane
, int new_first_row
)
885 g_return_if_fail (pane
!= NULL
);
886 sheet
= scg_sheet (pane
->simple
.scg
);
887 g_return_if_fail (0 <= new_first_row
&& new_first_row
< gnm_sheet_get_max_rows (sheet
));
889 if (pane
->first
.row
!= new_first_row
) {
890 GocCanvas
* const canvas
= GOC_CANVAS(pane
);
891 gint64
const row_offset
= bar_set_top_row (pane
, new_first_row
);
892 gint64 col_offset
= pane
->first_offset
.x
;
894 gnm_pane_compute_visible_region (pane
, FALSE
);
895 goc_canvas_scroll_to (canvas
, col_offset
/ canvas
->pixels_per_unit
, row_offset
/ canvas
->pixels_per_unit
);
896 gnm_pane_update_inital_top_left (pane
);
901 scg_set_top_row (SheetControlGUI
*scg
, int row
)
904 GnmRange
const *bound
;
906 g_return_if_fail (GNM_IS_SCG (scg
));
908 sheet
= scg_sheet (scg
);
909 bound
= &sheet
->priv
->unhidden_region
;
910 if (row
< bound
->start
.row
)
911 row
= bound
->start
.row
;
912 else if (row
>= gnm_sheet_get_max_rows (sheet
))
913 row
= gnm_sheet_get_last_row (sheet
);
914 else if (row
> bound
->end
.row
)
915 row
= bound
->end
.row
;
918 int bottom
= scg_view (scg
)->unfrozen_top_left
.row
;
923 gnm_pane_set_top_row (scg_pane (scg
, 1), row
);
924 gnm_pane_set_top_row (scg_pane (scg
, 0), row
);
928 gnm_pane_set_top_left (GnmPane
*pane
,
929 int col
, int row
, gboolean force_scroll
)
931 gboolean changed
= FALSE
;
932 gint64 col_offset
, row_offset
;
935 g_return_if_fail (0 <= col
&&
936 col
< gnm_sheet_get_max_cols (scg_sheet (pane
->simple
.scg
)));
937 g_return_if_fail (0 <= row
&&
938 row
< gnm_sheet_get_max_rows (scg_sheet (pane
->simple
.scg
)));
940 if (pane
->first
.col
!= col
|| force_scroll
) {
942 /* Clear the offsets in case col/row size changed */
943 pane
->first_offset
.x
= 0;
947 col_offset
= bar_set_left_col (pane
, col
);
950 col_offset
= pane
->first_offset
.x
;
953 if (pane
->first
.row
!= row
|| force_scroll
) {
955 /* Clear the offsets in case col/row size changed */
956 pane
->first_offset
.y
= 0;
959 row_offset
= bar_set_top_row (pane
, row
);
962 row_offset
= pane
->first_offset
.y
;
967 gnm_pane_compute_visible_region (pane
, force_scroll
);
968 canvas
= GOC_CANVAS (pane
);
969 goc_canvas_scroll_to (canvas
, col_offset
/ canvas
->pixels_per_unit
, row_offset
/ canvas
->pixels_per_unit
);
970 gnm_pane_update_inital_top_left (pane
);
974 scg_set_top_left (SheetControl
*sc
, int col
, int row
)
976 SheetControlGUI
*scg
= (SheetControlGUI
*)sc
;
978 g_return_if_fail (GNM_IS_SCG (scg
));
982 /* We could be faster if necessary */
983 scg_set_left_col (scg
, col
);
984 scg_set_top_row (scg
, row
);
988 gnm_pane_make_cell_visible (GnmPane
*pane
, int col
, int row
,
989 gboolean
const force_scroll
)
993 int new_first_col
, new_first_row
;
997 g_return_if_fail (GNM_IS_PANE (pane
));
999 /* Avoid calling this before the canvas is realized: We do not know the
1000 * visible area, and would unconditionally scroll the cell to the top
1001 * left of the viewport.
1003 if (!gtk_widget_get_realized (GTK_WIDGET (pane
)))
1006 sheet
= scg_sheet (pane
->simple
.scg
);
1007 g_return_if_fail (col
>= 0);
1008 g_return_if_fail (row
>= 0);
1009 g_return_if_fail (col
< gnm_sheet_get_max_cols (sheet
));
1010 g_return_if_fail (row
< gnm_sheet_get_max_rows (sheet
));
1012 canvas
= GOC_CANVAS (pane
);
1013 range
.start
.col
= range
.end
.col
= col
;
1014 range
.start
.row
= range
.end
.row
= row
;
1015 gnm_sheet_merge_find_bounding_box (sheet
, &range
);
1017 gtk_widget_get_allocation (GTK_WIDGET (canvas
), &ca
);
1019 /* Find the new pane->first.col */
1020 if (range
.start
.col
< pane
->first
.col
) {
1021 new_first_col
= range
.start
.col
;
1022 } else if (range
.end
.col
> pane
->last_full
.col
) {
1023 int width
= ca
.width
;
1024 ColRowInfo
const * const ci
= sheet_col_get_info (sheet
, range
.end
.col
);
1025 if (ci
->size_pixels
< width
) {
1026 int first_col
= (pane
->last_visible
.col
== pane
->first
.col
)
1027 ? pane
->first
.col
: range
.end
.col
;
1029 for (; first_col
> 0; --first_col
) {
1030 ColRowInfo
const * const ci
= sheet_col_get_info (sheet
, first_col
);
1032 width
-= ci
->size_pixels
;
1037 new_first_col
= first_col
+1;
1038 if (new_first_col
> range
.start
.col
)
1039 new_first_col
= range
.start
.col
;
1041 new_first_col
= col
;
1043 new_first_col
= pane
->first
.col
;
1045 /* Find the new pane->first.row */
1046 if (range
.start
.row
< pane
->first
.row
) {
1047 new_first_row
= range
.start
.row
;
1048 } else if (range
.end
.row
> pane
->last_full
.row
) {
1049 int height
= ca
.height
;
1050 ColRowInfo
const * const ri
= sheet_row_get_info (sheet
, range
.end
.row
);
1051 if (ri
->size_pixels
< height
) {
1052 int first_row
= (pane
->last_visible
.row
== pane
->first
.row
)
1053 ? pane
->first
.row
: range
.end
.row
;
1055 for (; first_row
> 0; --first_row
) {
1056 ColRowInfo
const * const ri
= sheet_row_get_info (sheet
, first_row
);
1058 height
-= ri
->size_pixels
;
1063 new_first_row
= first_row
+1;
1064 if (new_first_row
> range
.start
.row
)
1065 new_first_row
= range
.start
.row
;
1067 new_first_row
= row
;
1069 new_first_row
= pane
->first
.row
;
1071 gnm_pane_set_top_left (pane
, new_first_col
, new_first_row
,
1076 * scg_make_cell_visible:
1077 * @scg: The gui control
1080 * @force_scroll: Completely recalibrate the offsets to the new position
1081 * @couple_panes: Scroll scroll dynamic panes back to bounds if target
1082 * is in frozen segment.
1084 * Ensure that cell (col, row) is visible.
1085 * Sheet is scrolled if cell is outside viewport.
1088 scg_make_cell_visible (SheetControlGUI
*scg
, int col
, int row
,
1089 gboolean force_scroll
, gboolean couple_panes
)
1091 SheetView
const *sv
= scg_view (scg
);
1092 GnmCellPos
const *tl
, *br
;
1094 g_return_if_fail (GNM_IS_SCG (scg
));
1096 if (!scg
->active_panes
)
1099 tl
= &sv
->frozen_top_left
;
1100 br
= &sv
->unfrozen_top_left
;
1101 if (col
< br
->col
) {
1102 if (row
>= br
->row
) { /* pane 1 */
1105 gnm_pane_make_cell_visible (scg
->pane
[1],
1106 col
, row
, force_scroll
);
1107 gnm_pane_set_top_left (scg
->pane
[0],
1108 couple_panes
? br
->col
: scg
->pane
[0]->first
.col
,
1109 scg
->pane
[1]->first
.row
,
1111 if (couple_panes
&& scg
->pane
[3])
1112 gnm_pane_set_left_col (scg
->pane
[3], br
->col
);
1113 } else if (couple_panes
) { /* pane 2 */
1114 /* FIXME : We may need to change the way this routine
1115 * is used to fix this. Because we only know what the
1116 * target cell is we cannot absolutely differentiate
1117 * between col & row scrolling. For now use the
1118 * heuristic that if the col was visible this is a
1121 if (scg
->pane
[2]->first
.col
<= col
&&
1122 scg
->pane
[2]->last_visible
.col
>= col
) {
1123 scg_set_top_row (scg
, row
);
1125 scg_set_left_col (scg
, col
);
1127 } else if (row
< br
->row
) { /* pane 3 */
1130 gnm_pane_make_cell_visible (scg
->pane
[3],
1131 col
, row
, force_scroll
);
1132 gnm_pane_set_top_left (scg
->pane
[0],
1133 scg
->pane
[3]->first
.col
,
1136 : scg
->pane
[0]->first
.row
,
1138 if (couple_panes
&& scg
->pane
[1])
1139 gnm_pane_set_top_row (scg
->pane
[1],
1141 } else { /* pane 0 */
1142 gnm_pane_make_cell_visible (scg
->pane
[0],
1143 col
, row
, force_scroll
);
1145 gnm_pane_set_top_left (scg
->pane
[1],
1146 tl
->col
, scg
->pane
[0]->first
.row
, force_scroll
);
1148 gnm_pane_set_top_left (scg
->pane
[3],
1149 scg
->pane
[0]->first
.col
, tl
->row
, force_scroll
);
1152 gnm_pane_set_top_left (scg
->pane
[2],
1153 tl
->col
, tl
->row
, force_scroll
);
1157 scg_make_cell_visible_virt (SheetControl
*sc
, int col
, int row
,
1158 gboolean couple_panes
)
1160 scg_make_cell_visible ((SheetControlGUI
*)sc
, col
, row
,
1161 FALSE
, couple_panes
);
1164 /*************************************************************************/
1167 scg_set_panes (SheetControl
*sc
)
1169 SheetControlGUI
*scg
= (SheetControlGUI
*) sc
;
1170 SheetView
*sv
= sc
->view
;
1171 gboolean
const being_frozen
= sv_is_frozen (sv
);
1172 GocDirection direction
= (sv_sheet (sv
)->text_is_rtl
)? GOC_DIRECTION_RTL
: GOC_DIRECTION_LTR
;
1174 g_return_if_fail (GNM_IS_SV (sv
));
1179 GnmCellPos
const *tl
= &sv
->frozen_top_left
;
1180 GnmCellPos
const *br
= &sv
->unfrozen_top_left
;
1181 gboolean
const freeze_h
= br
->col
> tl
->col
;
1182 gboolean
const freeze_v
= br
->row
> tl
->row
;
1184 gnm_pane_bound_set (scg
->pane
[0],
1186 gnm_sheet_get_last_col (sv
->sheet
), gnm_sheet_get_last_row (sv
->sheet
));
1189 scg
->active_panes
= 2;
1190 if (!scg
->pane
[1]) {
1191 scg
->pane
[1] = gnm_pane_new (scg
, TRUE
, FALSE
, 1);
1192 gnm_pane_set_direction (scg
->pane
[1], direction
);
1193 gtk_grid_attach (scg
->grid
,
1194 GTK_WIDGET (scg
->pane
[1]),
1196 gtk_grid_attach (scg
->grid
,
1197 GTK_WIDGET (scg
->pane
[1]->col
.canvas
),
1200 gnm_pane_bound_set (scg
->pane
[1],
1201 tl
->col
, br
->row
, br
->col
- 1, gnm_sheet_get_last_row (sv
->sheet
));
1203 if (freeze_h
&& freeze_v
) {
1204 scg
->active_panes
= 4;
1205 if (!scg
->pane
[2]) {
1206 scg
->pane
[2] = gnm_pane_new (scg
, FALSE
, FALSE
, 2);
1207 gnm_pane_set_direction (scg
->pane
[2], direction
);
1208 gtk_grid_attach (scg
->grid
,
1209 GTK_WIDGET (scg
->pane
[2]),
1212 gnm_pane_bound_set (scg
->pane
[2],
1213 tl
->col
, tl
->row
, br
->col
- 1, br
->row
- 1);
1216 scg
->active_panes
= 4;
1217 if (!scg
->pane
[3]) {
1218 scg
->pane
[3] = gnm_pane_new (scg
, FALSE
, TRUE
, 3);
1219 gnm_pane_set_direction (scg
->pane
[3], direction
);
1220 gtk_grid_attach (scg
->grid
,
1221 GTK_WIDGET (scg
->pane
[3]),
1223 gtk_grid_attach (scg
->grid
,
1224 GTK_WIDGET (scg
->pane
[3]->row
.canvas
),
1227 gnm_pane_bound_set (scg
->pane
[3],
1228 br
->col
, tl
->row
, gnm_sheet_get_last_col (sv
->sheet
), br
->row
- 1);
1232 for (i
= 1 ; i
<= 3 ; i
++)
1234 gtk_widget_destroy (GTK_WIDGET (scg
->pane
[i
]));
1235 scg
->pane
[i
] = NULL
;
1238 scg
->active_panes
= 1;
1239 gnm_pane_bound_set (scg
->pane
[0],
1240 0, 0, gnm_sheet_get_last_col (sv
->sheet
), gnm_sheet_get_last_row (sv
->sheet
));
1243 gtk_widget_show_all (GTK_WIDGET (scg
->grid
));
1245 /* in case headers are hidden */
1246 scg_adjust_preferences (scg
);
1247 scg_resize (scg
, TRUE
);
1250 GnmCellPos
const *tl
= &sc
->view
->frozen_top_left
;
1253 gnm_pane_set_left_col (scg
->pane
[1], tl
->col
);
1255 gnm_pane_set_top_left (scg
->pane
[2], tl
->col
, tl
->row
, TRUE
);
1257 gnm_pane_set_top_row (scg
->pane
[3], tl
->row
);
1259 set_resize_pane_pos (scg
, scg
->vpane
);
1260 set_resize_pane_pos (scg
, scg
->hpane
);
1264 cb_wbc_destroyed (SheetControlGUI
*scg
)
1267 scg
->sheet_control
.wbc
= NULL
;
1271 cb_scg_redraw (SheetControlGUI
*scg
)
1273 scg_adjust_preferences (scg
);
1274 scg_redraw_all (&scg
->sheet_control
, TRUE
);
1278 cb_scg_redraw_resize (SheetControlGUI
*scg
)
1280 cb_scg_redraw (scg
);
1281 scg_resize (scg
, FALSE
);
1285 cb_scg_sheet_resized (SheetControlGUI
*scg
)
1287 cb_scg_redraw_resize (scg
);
1288 sc_set_panes (&scg
->sheet_control
);
1292 cb_scg_direction_changed (SheetControlGUI
*scg
)
1294 /* set direction in the canvas */
1295 int i
= scg
->active_panes
;
1297 GnmPane
*pane
= scg
->pane
[i
];
1299 gnm_pane_set_direction (scg
->pane
[i
],
1300 scg_sheet (scg
)->text_is_rtl
? GOC_DIRECTION_RTL
: GOC_DIRECTION_LTR
);
1302 scg_resize (scg
, TRUE
);
1305 static GnmPane
const *
1306 resize_pane_pos (SheetControlGUI
*scg
, GtkPaned
*p
,
1307 int *colrow_result
, gint64
*guide_pos
)
1309 ColRowInfo
const *cri
;
1310 GnmPane
const *pane
= scg_pane (scg
, 0);
1311 gboolean
const vert
= (p
== scg
->hpane
);
1313 gint64 pos
= gtk_paned_get_position (p
);
1315 gtk_widget_style_get (GTK_WIDGET (p
), "handle-size", &handle
, NULL
);
1318 if (gtk_widget_get_visible (GTK_WIDGET (pane
->row
.canvas
))) {
1320 gtk_widget_get_allocation (GTK_WIDGET (pane
->row
.canvas
), &ca
);
1325 gtk_widget_get_allocation (GTK_WIDGET (scg
->pane
[1]),
1329 pane
= scg_pane (scg
, 1);
1334 pos
+= pane
->first_offset
.x
;
1335 colrow
= gnm_pane_find_col (pane
, pos
, guide_pos
);
1337 if (gtk_widget_get_visible (GTK_WIDGET (pane
->col
.canvas
))) {
1339 gtk_widget_get_allocation (GTK_WIDGET (pane
->col
.canvas
), &ca
);
1344 gtk_widget_get_allocation (GTK_WIDGET (scg
->pane
[3]),
1346 if (pos
< pa
.height
)
1347 pane
= scg_pane (scg
, 3);
1352 pos
+= pane
->first_offset
.y
;
1353 colrow
= gnm_pane_find_row (pane
, pos
, guide_pos
);
1355 cri
= sheet_colrow_get_info (scg_sheet (scg
), colrow
, vert
);
1356 if (pos
>= (*guide_pos
+ cri
->size_pixels
/ 2)) {
1357 *guide_pos
+= cri
->size_pixels
;
1360 if (NULL
!= colrow_result
)
1361 *colrow_result
= colrow
;
1367 scg_gtk_paned_set_position (SheetControlGUI
*scg
, GtkPaned
*p
, int pane_pos
)
1369 /* A negative position is special to GtkPaned. */
1370 pane_pos
= MAX (pane_pos
, 0);
1372 if (p
== scg
->vpane
)
1373 scg
->vpos
= pane_pos
;
1375 scg
->hpos
= pane_pos
;
1377 gtk_paned_set_position (p
, pane_pos
);
1381 set_resize_pane_pos (SheetControlGUI
*scg
, GtkPaned
*p
)
1383 int handle_size
, pane_pos
, size
;
1384 GnmPane
*pane0
= scg
->pane
[0];
1389 if (p
== scg
->vpane
) {
1390 if (gtk_widget_get_visible (GTK_WIDGET (pane0
->col
.canvas
))) {
1391 GtkAllocation alloc
;
1392 gtk_widget_get_allocation (GTK_WIDGET (pane0
->col
.canvas
), &alloc
);
1393 pane_pos
= alloc
.height
;
1397 gtk_widget_get_size_request (
1398 GTK_WIDGET (scg
->pane
[3]), NULL
, &size
);
1402 if (gtk_widget_get_visible (GTK_WIDGET (pane0
->row
.canvas
))) {
1403 GtkAllocation alloc
;
1404 gtk_widget_get_allocation (GTK_WIDGET (pane0
->row
.canvas
), &alloc
);
1405 pane_pos
= alloc
.width
;
1409 gtk_widget_get_size_request (
1410 GTK_WIDGET (scg
->pane
[1]), &size
, NULL
);
1414 gtk_widget_style_get (GTK_WIDGET (p
), "handle-size", &handle_size
, NULL
);
1415 pane_pos
-= handle_size
/ 2;
1417 g_signal_handlers_block_by_func (G_OBJECT (p
),
1418 G_CALLBACK (cb_resize_pane_motion
), scg
);
1419 scg_gtk_paned_set_position (scg
, p
, pane_pos
);
1420 g_signal_handlers_unblock_by_func (G_OBJECT (p
),
1421 G_CALLBACK (cb_resize_pane_motion
), scg
);
1425 cb_check_resize (GtkPaned
*p
, GtkAllocation
*allocation
, SheetControlGUI
*scg
);
1428 resize_pane_finish (SheetControlGUI
*scg
, GtkPaned
*p
)
1430 SheetView
*sv
= scg_view (scg
);
1431 GnmCellPos frozen_tl
, unfrozen_tl
;
1432 GnmPane
const *pane
;
1436 #warning GTK3: replace this?
1441 pane
= resize_pane_pos (scg
, p
, &colrow
, &guide_pos
);
1443 if (sv_is_frozen (sv
)) {
1444 frozen_tl
= sv
->frozen_top_left
;
1445 unfrozen_tl
= sv
->unfrozen_top_left
;
1447 frozen_tl
= pane
->first
;
1448 if (p
== scg
->hpane
) {
1449 unfrozen_tl
.col
= colrow
;
1450 if (!sv_is_frozen (sv
))
1451 unfrozen_tl
.row
= frozen_tl
.row
= 0;
1453 unfrozen_tl
.row
= colrow
;
1454 if (!sv_is_frozen (sv
))
1455 unfrozen_tl
.col
= frozen_tl
.col
= 0;
1457 sv_freeze_panes (sv
, &frozen_tl
, &unfrozen_tl
);
1459 scg
->pane_drag_handler
= 0;
1460 scg_size_guide_stop (scg
);
1462 set_resize_pane_pos (scg
, p
);
1464 g_signal_handlers_unblock_by_func
1466 G_CALLBACK (cb_check_resize
), scg
);
1471 cb_resize_vpane_finish (SheetControlGUI
*scg
)
1473 return resize_pane_finish (scg
, scg
->vpane
);
1476 cb_resize_hpane_finish (SheetControlGUI
*scg
)
1478 return resize_pane_finish (scg
, scg
->hpane
);
1482 cb_resize_pane_motion (GtkPaned
*p
,
1483 G_GNUC_UNUSED GParamSpec
*pspec
,
1484 SheetControlGUI
*scg
)
1486 gboolean
const vert
= (p
== scg
->hpane
);
1490 resize_pane_pos (scg
, p
, &colrow
, &guide_pos
);
1491 #warning GTK3: what replaces p->in_drag?
1492 if (scg
->pane_drag_handler
== 0/* && p->in_drag*/) {
1493 g_signal_handlers_block_by_func
1495 G_CALLBACK (cb_check_resize
), scg
);
1496 scg_size_guide_start (scg
, vert
, colrow
, FALSE
);
1497 scg
->pane_drag_handler
= g_timeout_add (250,
1498 vert
? (GSourceFunc
) cb_resize_hpane_finish
1499 : (GSourceFunc
) cb_resize_vpane_finish
,
1502 if (scg
->pane_drag_handler
)
1503 scg_size_guide_motion (scg
, vert
, guide_pos
);
1508 cb_check_resize (GtkPaned
*p
, G_GNUC_UNUSED GtkAllocation
*allocation
,
1509 SheetControlGUI
*scg
)
1511 gboolean
const vert
= (p
== scg
->vpane
);
1512 gint max
, pos
= vert
? scg
->vpos
: scg
->hpos
;
1514 g_object_get (G_OBJECT (p
), "max-position", &max
, NULL
);
1518 if (gtk_paned_get_position (p
) != pos
) {
1519 g_signal_handlers_block_by_func
1521 G_CALLBACK (cb_resize_pane_motion
), scg
);
1522 gtk_paned_set_position (p
, pos
);
1523 g_signal_handlers_unblock_by_func
1525 G_CALLBACK (cb_resize_pane_motion
), scg
);
1529 struct resize_closure
{
1531 SheetControlGUI
*scg
;
1535 idle_resize (struct resize_closure
*r
)
1538 set_resize_pane_pos (r
->scg
, r
->p
);
1544 cb_canvas_resize (GtkWidget
*w
, G_GNUC_UNUSED GtkAllocation
*allocation
,
1545 SheetControlGUI
*scg
)
1547 struct resize_closure
*r
= g_new (struct resize_closure
, 1);
1549 r
->p
= (w
== GTK_WIDGET (scg
->pane
[0]->col
.canvas
))? scg
->hpane
: scg
->vpane
;
1550 /* The allocation is not correct at this point, weird */
1551 g_idle_add ((GSourceFunc
) idle_resize
, r
);
1555 post_create_cb (SheetControlGUI
*scg
)
1557 Sheet
*sheet
= sc_sheet (GNM_SC (scg
));
1558 if (sheet
->sheet_objects
)
1559 scg_object_select (scg
, (SheetObject
*) sheet
->sheet_objects
->data
);
1564 sheet_object_key_pressed (G_GNUC_UNUSED GtkWidget
*w
, GdkEventKey
*event
, SheetControlGUI
*scg
)
1566 Sheet
*sheet
= scg_sheet (scg
);
1567 WorkbookControl
* wbc
= scg_wbc (scg
);
1568 Workbook
* wb
= wb_control_get_workbook (wbc
);
1569 switch (event
->keyval
) {
1570 case GDK_KEY_KP_Page_Up
:
1571 case GDK_KEY_Page_Up
:
1572 if ((event
->state
& GDK_CONTROL_MASK
) != 0){
1573 if ((event
->state
& GDK_SHIFT_MASK
) != 0){
1574 WorkbookSheetState
* old_state
= workbook_sheet_state_new(wb
);
1575 int old_pos
= sheet
->index_in_wb
;
1578 workbook_sheet_move(sheet
, -1);
1579 cmd_reorganize_sheets (wbc
, old_state
, sheet
);
1582 gnm_notebook_prev_page (scg
->wbcg
->bnotebook
);
1587 case GDK_KEY_KP_Page_Down
:
1588 case GDK_KEY_Page_Down
:
1590 if ((event
->state
& GDK_CONTROL_MASK
) != 0){
1591 if ((event
->state
& GDK_SHIFT_MASK
) != 0){
1592 WorkbookSheetState
* old_state
= workbook_sheet_state_new(wb
);
1593 int num_sheets
= workbook_sheet_count(wb
);
1594 gint old_pos
= sheet
->index_in_wb
;
1596 if (old_pos
< num_sheets
- 1){
1597 workbook_sheet_move(sheet
, 1);
1598 cmd_reorganize_sheets (wbc
, old_state
, sheet
);
1601 gnm_notebook_next_page (scg
->wbcg
->bnotebook
);
1611 cb_screen_changed (GtkWidget
*widget
, G_GNUC_UNUSED GdkScreen
*prev
,
1612 SheetControlGUI
*scg
)
1614 GdkScreen
*screen
= gtk_widget_get_screen (widget
);
1617 scg
->screen_width
= gdk_screen_get_width (screen
);
1618 scg
->screen_height
= gdk_screen_get_height (screen
);
1623 sheet_control_gui_new (SheetView
*sv
, WBCGtk
*wbcg
)
1625 SheetControlGUI
*scg
;
1627 GocDirection direction
;
1628 GdkRGBA cfore
, cback
;
1630 g_return_val_if_fail (GNM_IS_SV (sv
), NULL
);
1632 sheet
= sv_sheet (sv
);
1633 direction
= (sheet
->text_is_rtl
)? GOC_DIRECTION_RTL
: GOC_DIRECTION_LTR
;
1635 scg
= g_object_new (GNM_SCG_TYPE
, NULL
);
1637 scg
->sheet_control
.wbc
= GNM_WBC (wbcg
);
1639 g_object_weak_ref (G_OBJECT (wbcg
),
1640 (GWeakNotify
) cb_wbc_destroyed
,
1643 if (sheet
->sheet_type
== GNM_SHEET_DATA
) {
1644 scg
->active_panes
= 1;
1645 scg
->pane
[0] = NULL
;
1646 scg
->pane
[1] = NULL
;
1647 scg
->pane
[2] = NULL
;
1648 scg
->pane
[3] = NULL
;
1649 scg
->pane_drag_handler
= 0;
1651 scg
->col_group
.buttons
= g_ptr_array_new ();
1652 scg
->row_group
.buttons
= g_ptr_array_new ();
1653 scg
->col_group
.button_box
= gtk_box_new (GTK_ORIENTATION_VERTICAL
, 0);
1654 g_object_set (scg
->col_group
.button_box
,
1655 "halign", GTK_ALIGN_CENTER
,
1656 "homogeneous", TRUE
,
1658 scg
->row_group
.button_box
= gtk_box_new (GTK_ORIENTATION_HORIZONTAL
, 0);
1659 g_object_set (scg
->row_group
.button_box
,
1660 "valign", GTK_ALIGN_CENTER
,
1661 "homogeneous", TRUE
,
1663 scg
->select_all_btn
= gtk_drawing_area_new ();
1664 gtk_style_context_add_class (gtk_widget_get_style_context (scg
->select_all_btn
),
1665 GTK_STYLE_CLASS_BUTTON
);
1666 gtk_style_context_add_class (gtk_widget_get_style_context (scg
->select_all_btn
),
1668 gtk_widget_add_events (scg
->select_all_btn
, GDK_BUTTON_PRESS_MASK
);
1669 g_signal_connect (G_OBJECT (scg
->select_all_btn
), "draw",
1670 G_CALLBACK (cb_select_all_btn_draw
), scg
);
1671 g_signal_connect (G_OBJECT (scg
->select_all_btn
), "event",
1672 G_CALLBACK (cb_select_all_btn_event
), scg
);
1674 scg
->grid
= GTK_GRID (gtk_grid_new ());
1675 gtk_grid_attach (scg
->grid
, scg
->col_group
.button_box
,
1677 gtk_grid_attach (scg
->grid
, scg
->row_group
.button_box
,
1679 gtk_grid_attach (scg
->grid
, scg
->select_all_btn
, 1, 1, 1, 1);
1681 scg
->pane
[1] = scg
->pane
[2] = scg
->pane
[3] = NULL
;
1682 scg
->pane
[0] = gnm_pane_new (scg
, TRUE
, TRUE
, 0);
1683 gnm_pane_set_direction (scg
->pane
[0], direction
);
1684 gtk_grid_attach (scg
->grid
,
1685 GTK_WIDGET (scg
->pane
[0]->col
.canvas
),
1687 gtk_grid_attach (scg
->grid
,
1688 GTK_WIDGET (scg
->pane
[0]->row
.canvas
),
1690 g_object_set (scg
->pane
[0],
1694 gtk_grid_attach (scg
->grid
, GTK_WIDGET (scg
->pane
[0]),
1696 g_signal_connect_after (G_OBJECT (scg
->pane
[0]->col
.canvas
), "size-allocate",
1697 G_CALLBACK (cb_canvas_resize
), scg
);
1698 g_signal_connect_after (G_OBJECT (scg
->pane
[0]->row
.canvas
), "size-allocate",
1699 G_CALLBACK (cb_canvas_resize
), scg
);
1701 scg
->va
= (GtkAdjustment
*)gtk_adjustment_new (0., 0., 1, 1., 1., 1.);
1702 scg
->vs
= g_object_new (GTK_TYPE_SCROLLBAR
,
1703 "orientation", GTK_ORIENTATION_VERTICAL
,
1704 "adjustment", scg
->va
,
1706 g_signal_connect (G_OBJECT (scg
->vs
),
1708 G_CALLBACK (cb_vscrollbar_value_changed
), scg
);
1709 g_signal_connect (G_OBJECT (scg
->vs
),
1711 G_CALLBACK (cb_vscrollbar_adjust_bounds
), sheet
);
1713 scg
->ha
= (GtkAdjustment
*)gtk_adjustment_new (0., 0., 1, 1., 1., 1.);
1714 scg
->hs
= g_object_new (GTK_TYPE_SCROLLBAR
,
1715 "adjustment", scg
->ha
,
1717 g_signal_connect (G_OBJECT (scg
->hs
),
1719 G_CALLBACK (cb_hscrollbar_value_changed
), scg
);
1720 g_signal_connect (G_OBJECT (scg
->hs
),
1722 G_CALLBACK (cb_hscrollbar_adjust_bounds
), sheet
);
1724 g_object_ref (scg
->grid
);
1725 scg
->vpane
= g_object_new (GTK_TYPE_PANED
, "orientation", GTK_ORIENTATION_VERTICAL
, NULL
);
1726 gtk_paned_add1 (scg
->vpane
, gtk_label_new (NULL
)); /* use a spacer */
1727 gtk_paned_add2 (scg
->vpane
, scg
->vs
);
1728 scg_gtk_paned_set_position (scg
, scg
->vpane
, 0);
1729 gtk_widget_set_vexpand (GTK_WIDGET (scg
->vpane
), TRUE
);
1730 gtk_grid_attach (scg
->grid
,
1731 GTK_WIDGET (scg
->vpane
), 4, 0, 1, 4);
1732 scg
->hpane
= g_object_new (GTK_TYPE_PANED
, NULL
);
1733 gtk_paned_add1 (scg
->hpane
, gtk_label_new (NULL
)); /* use a spacer */
1734 gtk_paned_add2 (scg
->hpane
, scg
->hs
);
1735 scg_gtk_paned_set_position (scg
, scg
->hpane
, 0);
1736 gtk_widget_set_hexpand (GTK_WIDGET (scg
->hpane
), TRUE
);
1737 gtk_grid_attach (scg
->grid
,
1738 GTK_WIDGET (scg
->hpane
), 0, 4, 4, 1);
1739 /* do not connect until after setting position */
1740 g_signal_connect (G_OBJECT (scg
->vpane
), "notify::position",
1741 G_CALLBACK (cb_resize_pane_motion
), scg
);
1742 g_signal_connect (G_OBJECT (scg
->hpane
), "notify::position",
1743 G_CALLBACK (cb_resize_pane_motion
), scg
);
1744 g_signal_connect_after (G_OBJECT (scg
->vpane
), "size-allocate",
1745 G_CALLBACK (cb_check_resize
), scg
);
1746 g_signal_connect_after (G_OBJECT (scg
->hpane
), "size-allocate",
1747 G_CALLBACK (cb_check_resize
), scg
);
1749 g_signal_connect_data (G_OBJECT (scg
->grid
),
1751 G_CALLBACK (scg_scrollbar_config
), scg
, NULL
,
1752 G_CONNECT_AFTER
| G_CONNECT_SWAPPED
);
1753 g_signal_connect_object (G_OBJECT (scg
->grid
),
1755 G_CALLBACK (cb_table_destroy
), G_OBJECT (scg
),
1758 sv_attach_control (sv
, GNM_SC (scg
));
1760 g_object_connect (G_OBJECT (sheet
),
1761 "swapped_signal::notify::text-is-rtl", cb_scg_direction_changed
, scg
,
1762 "swapped_signal::notify::display-formulas", cb_scg_redraw
, scg
,
1763 "swapped_signal::notify::display-zeros", cb_scg_redraw
, scg
,
1764 "swapped_signal::notify::display-grid", cb_scg_redraw
, scg
,
1765 "swapped_signal::notify::display-column-header", scg_adjust_preferences
, scg
,
1766 "swapped_signal::notify::display-row-header", scg_adjust_preferences
, scg
,
1767 "swapped_signal::notify::use-r1c1", cb_scg_redraw
, scg
,
1768 "swapped_signal::notify::display-outlines", cb_scg_redraw_resize
, scg
,
1769 "swapped_signal::notify::display-outlines-below", cb_scg_redraw_resize
, scg
,
1770 "swapped_signal::notify::display-outlines-right", cb_scg_redraw_resize
, scg
,
1771 "swapped_signal::notify::columns", cb_scg_sheet_resized
, scg
,
1772 "swapped_signal::notify::rows", cb_scg_sheet_resized
, scg
,
1775 scg
->active_panes
= 0;
1776 scg
->grid
= GTK_GRID (gtk_grid_new ());
1777 g_object_ref (scg
->grid
);
1778 sheet
->hide_col_header
= sheet
->hide_row_header
= FALSE
;
1779 if (sheet
->sheet_type
== GNM_SHEET_OBJECT
) {
1780 /* WHY store this in ->vs? */
1781 scg
->vs
= g_object_new (GOC_TYPE_CANVAS
,
1785 gtk_style_context_add_class (gtk_widget_get_style_context (scg
->vs
),
1787 gtk_grid_attach (scg
->grid
, scg
->vs
, 0, 0, 1, 1);
1788 gtk_widget_set_can_focus (scg
->vs
, TRUE
);
1789 gtk_widget_set_can_default (scg
->vs
, TRUE
);
1790 g_signal_connect (G_OBJECT (scg
->vs
), "key-press-event",
1791 G_CALLBACK (sheet_object_key_pressed
), scg
);
1793 sv_attach_control (sv
, GNM_SC (scg
));
1795 g_object_set_data (G_OBJECT (scg
->vs
), "sheet-control", scg
);
1796 if (sheet
->sheet_objects
) {
1797 /* we need an idle function because not everything is initialized at this point */
1798 sheet_object_new_view ((SheetObject
*) sheet
->sheet_objects
->data
,
1799 (SheetObjectViewContainer
*) scg
->vs
);
1800 g_idle_add ((GSourceFunc
) post_create_cb
, scg
);
1805 scg
->label
= g_object_new
1806 (GNM_NOTEBOOK_BUTTON_TYPE
,
1807 "label", sheet
->name_unquoted
,
1808 //"valign", GTK_ALIGN_START,
1811 ? go_color_to_gdk_rgba (sheet
->tab_color
->go_color
,
1815 (sheet
->tab_text_color
1816 ? go_color_to_gdk_rgba (sheet
->tab_text_color
->go_color
,
1820 g_object_ref (scg
->label
);
1822 g_signal_connect (G_OBJECT (scg
->grid
),
1824 G_CALLBACK (cb_screen_changed
),
1831 scg_comment_timer_clear (SheetControlGUI
*scg
)
1833 if (scg
->comment
.timer
!= 0) {
1834 g_source_remove (scg
->comment
.timer
);
1835 scg
->comment
.timer
= 0;
1840 scg_im_destroy (SheetControlGUI
*scg
) {
1841 if (scg
->im
.timer
!= 0) {
1842 g_source_remove (scg
->im
.timer
);
1846 gtk_widget_destroy (scg
->im
.item
);
1847 scg
->im
.item
= NULL
;
1852 scg_finalize (GObject
*object
)
1854 SheetControlGUI
*scg
= GNM_SCG (object
);
1855 SheetControl
*sc
= (SheetControl
*) scg
;
1856 Sheet
*sheet
= scg_sheet (scg
);
1859 /* remove the object view before we disappear */
1860 scg_object_unselect (scg
, NULL
);
1862 for (ptr
= sheet
->sheet_objects
; ptr
!= NULL
; ptr
= ptr
->next
)
1863 SCG_FOREACH_PANE (scg
, pane
,
1865 sheet_object_get_view (ptr
->data
, (SheetObjectViewContainer
*)pane
));
1868 if (scg
->col_group
.buttons
) {
1869 g_ptr_array_free (scg
->col_group
.buttons
, TRUE
);
1870 g_ptr_array_free (scg
->row_group
.buttons
, TRUE
);
1873 if (scg
->pane_drag_handler
) {
1874 g_source_remove (scg
->pane_drag_handler
);
1875 scg
->pane_drag_handler
= 0;
1878 if (scg
->scroll_bar_timer
) {
1879 g_source_remove (scg
->scroll_bar_timer
);
1880 scg
->scroll_bar_timer
= 0;
1883 scg_comment_timer_clear (scg
);
1885 if (scg
->delayedMovement
.timer
!= 0) {
1886 g_source_remove (scg
->delayedMovement
.timer
);
1887 scg
->delayedMovement
.timer
= 0;
1889 scg_comment_unselect (scg
, scg
->comment
.selected
);
1891 scg_im_destroy (scg
);
1894 Sheet
*sheet
= sv_sheet (sc
->view
);
1895 g_signal_handlers_disconnect_by_func (sheet
, scg_adjust_preferences
, scg
);
1896 g_signal_handlers_disconnect_by_func (sheet
, cb_scg_redraw
, scg
);
1897 g_signal_handlers_disconnect_by_func (sheet
, cb_scg_redraw_resize
, scg
);
1898 g_signal_handlers_disconnect_by_func (sheet
, cb_scg_sheet_resized
, scg
);
1899 g_signal_handlers_disconnect_by_func (sheet
, cb_scg_direction_changed
, scg
);
1900 sv_detach_control (sc
);
1904 gtk_widget_destroy (GTK_WIDGET (scg
->grid
));
1905 g_object_unref (scg
->grid
);
1909 g_clear_object (&scg
->label
);
1911 if (scg
->wbcg
!= NULL
)
1912 g_object_weak_unref (G_OBJECT (scg
->wbcg
),
1913 (GWeakNotify
) cb_wbc_destroyed
,
1916 (*scg_parent_class
->finalize
) (object
);
1920 scg_unant (SheetControl
*sc
)
1922 SheetControlGUI
*scg
= (SheetControlGUI
*)sc
;
1924 g_return_if_fail (GNM_IS_SCG (scg
));
1926 /* Always have a pane 0 */
1927 if (scg
->active_panes
== 0 || scg
->pane
[0]->cursor
.animated
== NULL
)
1930 SCG_FOREACH_PANE (scg
, pane
, {
1933 for (l
= pane
->cursor
.animated
; l
; l
= l
->next
) {
1934 GocItem
*item
= l
->data
;
1935 goc_item_destroy (item
);
1938 g_slist_free (pane
->cursor
.animated
);
1939 pane
->cursor
.animated
= NULL
;
1944 scg_ant (SheetControl
*sc
)
1946 SheetControlGUI
*scg
= (SheetControlGUI
*)sc
;
1949 g_return_if_fail (GNM_IS_SCG (scg
));
1951 if (scg
->active_panes
== 0)
1954 /* Always have a grid 0 */
1955 if (NULL
!= scg
->pane
[0]->cursor
.animated
)
1958 for (l
= sc
->view
->ants
; l
; l
= l
->next
) {
1959 GnmRange
const *r
= l
->data
;
1961 SCG_FOREACH_PANE (scg
, pane
, {
1962 GnmItemCursor
*ic
= GNM_ITEM_CURSOR (goc_item_new (
1964 gnm_item_cursor_get_type (),
1965 "SheetControlGUI", scg
,
1966 "style", GNM_ITEM_CURSOR_ANTED
,
1968 gnm_item_cursor_bound_set (ic
, r
);
1969 pane
->cursor
.animated
=
1970 g_slist_prepend (pane
->cursor
.animated
, ic
);
1976 scg_adjust_preferences (SheetControlGUI
*scg
)
1978 Sheet
const *sheet
= scg_sheet (scg
);
1980 SCG_FOREACH_PANE (scg
, pane
, {
1981 if (pane
->col
.canvas
!= NULL
) {
1982 gtk_widget_set_visible (GTK_WIDGET (pane
->col
.canvas
),
1983 !sheet
->hide_col_header
);
1986 if (pane
->row
.canvas
!= NULL
) {
1987 gtk_widget_set_visible (GTK_WIDGET (pane
->row
.canvas
),
1988 !sheet
->hide_row_header
);
1992 if (scg
->select_all_btn
) {
1993 /* we used to test for the corner table existence, why??? */
1994 gboolean visible
= !(sheet
->hide_col_header
|| sheet
->hide_row_header
);
1995 gtk_widget_set_visible (scg
->select_all_btn
, visible
);
1996 gtk_widget_set_visible (scg
->row_group
.button_box
, visible
);
1997 gtk_widget_set_visible (scg
->col_group
.button_box
, visible
);
1999 if (scg_wbc (scg
) != NULL
) {
2000 WorkbookView
*wbv
= wb_control_view (scg_wbc (scg
));
2001 gtk_widget_set_visible (scg
->hs
,
2002 wbv
->show_horizontal_scrollbar
);
2004 gtk_widget_set_visible (scg
->vs
,
2005 wbv
->show_vertical_scrollbar
);
2010 /***************************************************************************/
2016 CONTEXT_PASTE_SPECIAL
,
2019 CONTEXT_CLEAR_CONTENT
,
2020 CONTEXT_FORMAT_CELL
,
2021 CONTEXT_FORMAT_CELL_COND
,
2022 CONTEXT_CELL_AUTOFIT_WIDTH
,
2023 CONTEXT_CELL_AUTOFIT_HEIGHT
,
2025 CONTEXT_CELL_UNMERGE
,
2029 CONTEXT_COL_AUTOFIT
,
2033 CONTEXT_ROW_AUTOFIT
,
2034 CONTEXT_COMMENT_EDIT
,
2035 CONTEXT_COMMENT_ADD
,
2036 CONTEXT_COMMENT_REMOVE
,
2037 CONTEXT_HYPERLINK_EDIT
,
2038 CONTEXT_HYPERLINK_ADD
,
2039 CONTEXT_HYPERLINK_REMOVE
,
2040 CONTEXT_DATA_SLICER_REFRESH
, /* refresh and redraw */
2041 CONTEXT_DATA_SLICER_EDIT
/* prop dialog */
2044 context_menu_handler (GnmPopupMenuElement
const *element
,
2047 SheetControlGUI
*scg
= user_data
;
2048 SheetControl
*sc
= (SheetControl
*) scg
;
2049 SheetView
*sv
= sc
->view
;
2050 Sheet
*sheet
= sv
->sheet
;
2051 WBCGtk
*wbcg
= scg
->wbcg
;
2052 WorkbookControl
*wbc
= sc
->wbc
;
2054 g_return_if_fail (element
!= NULL
);
2055 g_return_if_fail (IS_SHEET (sheet
));
2057 switch (element
->index
) {
2059 sv_selection_cut (sv
, wbc
);
2062 sv_selection_copy (sv
, wbc
);
2065 cmd_paste_to_selection (wbc
, sv
, PASTE_DEFAULT
);
2067 case CONTEXT_PASTE_SPECIAL
:
2068 dialog_paste_special (wbcg
);
2070 case CONTEXT_INSERT
:
2071 dialog_insert_cells (wbcg
);
2073 case CONTEXT_DELETE
:
2074 dialog_delete_cells (wbcg
);
2076 case CONTEXT_CLEAR_CONTENT
:
2077 cmd_selection_clear (wbc
, CLEAR_VALUES
);
2079 case CONTEXT_FORMAT_CELL
:
2080 dialog_cell_format (wbcg
, FD_CURRENT
, 0);
2082 case CONTEXT_FORMAT_CELL_COND
:
2083 dialog_cell_format_cond (wbcg
);
2085 case CONTEXT_CELL_AUTOFIT_HEIGHT
:
2086 workbook_cmd_autofit_selection
2087 (wbc
, wb_control_cur_sheet (wbc
), FALSE
);
2089 case CONTEXT_CELL_AUTOFIT_WIDTH
:
2090 workbook_cmd_autofit_selection
2091 (wbc
, wb_control_cur_sheet (wbc
), TRUE
);
2093 case CONTEXT_CELL_MERGE
: {
2094 GSList
*range_list
= selection_get_ranges
2095 (wb_control_cur_sheet_view (wbc
), FALSE
);
2096 cmd_merge_cells (wbc
, wb_control_cur_sheet (wbc
), range_list
, FALSE
);
2097 range_fragment_free (range_list
);
2100 case CONTEXT_CELL_UNMERGE
: {
2101 GSList
*range_list
= selection_get_ranges
2102 (wb_control_cur_sheet_view (wbc
), FALSE
);
2103 cmd_unmerge_cells (wbc
, wb_control_cur_sheet (wbc
), range_list
);
2104 range_fragment_free (range_list
);
2108 case CONTEXT_COL_WIDTH
:
2109 dialog_col_width (wbcg
, FALSE
);
2111 case CONTEXT_COL_AUTOFIT
:
2112 workbook_cmd_resize_selected_colrow
2113 (wbc
, wb_control_cur_sheet (wbc
), TRUE
, -1);
2115 case CONTEXT_COL_HIDE
:
2116 cmd_selection_colrow_hide (wbc
, TRUE
, FALSE
);
2118 case CONTEXT_COL_UNHIDE
:
2119 cmd_selection_colrow_hide (wbc
, TRUE
, TRUE
);
2121 case CONTEXT_ROW_HEIGHT
:
2122 dialog_row_height (wbcg
, FALSE
);
2124 case CONTEXT_ROW_AUTOFIT
:
2125 workbook_cmd_resize_selected_colrow
2126 (wbc
, wb_control_cur_sheet (wbc
), FALSE
, -1);
2128 case CONTEXT_ROW_HIDE
:
2129 cmd_selection_colrow_hide (wbc
, FALSE
, FALSE
);
2131 case CONTEXT_ROW_UNHIDE
:
2132 cmd_selection_colrow_hide (wbc
, FALSE
, TRUE
);
2134 case CONTEXT_COMMENT_EDIT
:
2135 case CONTEXT_COMMENT_ADD
:
2136 dialog_cell_comment (wbcg
, sheet
, &sv
->edit_pos
);
2138 case CONTEXT_COMMENT_REMOVE
:
2139 cmd_selection_clear (GNM_WBC (wbcg
), CLEAR_COMMENTS
);
2141 case CONTEXT_HYPERLINK_EDIT
:
2142 case CONTEXT_HYPERLINK_ADD
:
2143 dialog_hyperlink (wbcg
, sc
);
2146 case CONTEXT_HYPERLINK_REMOVE
: {
2147 GnmStyle
*style
= gnm_style_new ();
2150 gchar
const *format
;
2153 for (l
= scg_view (scg
)->selections
; l
!= NULL
; l
= l
->next
) {
2154 GnmRange
const *r
= l
->data
;
2155 GnmStyleList
*styles
;
2157 styles
= sheet_style_collect_hlinks (sheet
, r
);
2158 n_links
+= g_slist_length (styles
);
2159 style_list_free (styles
);
2161 format
= ngettext ("Remove %d Link", "Remove %d Links", n_links
);
2162 name
= g_strdup_printf (format
, n_links
);
2163 gnm_style_set_hlink (style
, NULL
);
2164 cmd_selection_format (wbc
, style
, NULL
, name
);
2168 case CONTEXT_DATA_SLICER_REFRESH
:
2169 cmd_slicer_refresh (wbc
);
2171 case CONTEXT_DATA_SLICER_EDIT
:
2172 dialog_data_slicer (wbcg
, FALSE
);
2181 scg_context_menu (SheetControlGUI
*scg
, GdkEvent
*event
,
2182 gboolean is_col
, gboolean is_row
)
2184 SheetView
*sv
= scg_view (scg
);
2185 Sheet
*sheet
= sv_sheet (sv
);
2188 CONTEXT_DISPLAY_FOR_CELLS
= 1 << 0,
2189 CONTEXT_DISPLAY_FOR_ROWS
= 1 << 1,
2190 CONTEXT_DISPLAY_FOR_COLS
= 1 << 2,
2191 CONTEXT_DISPLAY_WITH_HYPERLINK
= 1 << 3,
2192 CONTEXT_DISPLAY_WITHOUT_HYPERLINK
= 1 << 4,
2193 CONTEXT_DISPLAY_WITH_HYPERLINK_IN_RANGE
= 1 << 5,
2194 CONTEXT_DISPLAY_WITH_DATA_SLICER
= 1 << 6,
2195 CONTEXT_DISPLAY_WITH_DATA_SLICER_ROW
= 1 << 7,
2196 CONTEXT_DISPLAY_WITH_DATA_SLICER_COL
= 1 << 8,
2197 CONTEXT_DISPLAY_WITH_COMMENT
= 1 << 9,
2198 CONTEXT_DISPLAY_WITHOUT_COMMENT
= 1 << 10,
2199 CONTEXT_DISPLAY_WITH_COMMENT_IN_RANGE
= 1 << 11
2202 CONTEXT_DISABLE_PASTE_SPECIAL
= 1 << 0,
2203 CONTEXT_DISABLE_FOR_ROWS
= 1 << 1,
2204 CONTEXT_DISABLE_FOR_COLS
= 1 << 2,
2205 CONTEXT_DISABLE_FOR_CELLS
= 1 << 3,
2206 CONTEXT_DISABLE_FOR_DISCONTIGUOUS_SELECTION
= 1 << 4,
2207 CONTEXT_DISABLE_FOR_ALL_COLS
= 1 << 5,
2208 CONTEXT_DISABLE_FOR_ALL_ROWS
= 1 << 6,
2209 CONTEXT_DISABLE_FOR_NOMERGES
= 1 << 7,
2210 CONTEXT_DISABLE_FOR_ONLYMERGES
= 1 << 8
2213 /* Note: keep the following two in sync!*/
2218 POPUPITEM_PASTESPECIAL
,
2220 POPUPITEM_INSERT_CELL
,
2221 POPUPITEM_DELETE_CELL
,
2222 POPUPITEM_INSERT_COLUMN
,
2223 POPUPITEM_DELETE_COLUMN
,
2224 POPUPITEM_INSERT_ROW
,
2225 POPUPITEM_DELETE_ROW
,
2226 POPUPITEM_CLEAR_CONTENTS
,
2228 POPUPITEM_COMMENT_ADD
,
2229 POPUPITEM_COMMENT_EDIT
,
2230 POPUPITEM_COMMENT_REMOVE
,
2232 POPUPITEM_LINK_EDIT
,
2233 POPUPITEM_LINK_REMOVE
,
2235 POPUPITEM_DATASLICER_EDIT
,
2236 POPUPITEM_DATASLICER_REFRESH
,
2237 POPUPITEM_DATASLICER_FIELD_ORDER
,
2238 POPUPITEM_DATASLICER_LEFT
,
2239 POPUPITEM_DATASLICER_RIGHT
,
2240 POPUPITEM_DATASLICER_UP
,
2241 POPUPITEM_DATASLICER_DOWN
,
2242 POPUPITEM_DATASLICER_SUBMENU
,
2246 static GnmPopupMenuElement popup_elements
[] = {
2247 { N_("Cu_t"), "edit-cut",
2248 0, 0, CONTEXT_CUT
, NULL
},
2249 { N_("_Copy"), "edit-copy",
2250 0, 0, CONTEXT_COPY
, NULL
},
2251 { N_("_Paste"), "edit-paste",
2252 0, 0, CONTEXT_PASTE
, NULL
},
2253 { N_("Paste _Special"), "edit-paste",
2254 0, CONTEXT_DISABLE_PASTE_SPECIAL
, CONTEXT_PASTE_SPECIAL
, NULL
},
2256 { "", NULL
, 0, 0, 0, NULL
},
2258 { N_("_Insert Cells..."), NULL
,
2259 CONTEXT_DISPLAY_FOR_CELLS
,
2260 CONTEXT_DISABLE_FOR_DISCONTIGUOUS_SELECTION
, CONTEXT_INSERT
, NULL
},
2261 { N_("_Delete Cells..."), "edit-delete",
2262 CONTEXT_DISPLAY_FOR_CELLS
,
2263 CONTEXT_DISABLE_FOR_DISCONTIGUOUS_SELECTION
, CONTEXT_DELETE
, NULL
},
2264 { N_("_Insert Column(s)"), "gnumeric-column-add",
2265 CONTEXT_DISPLAY_FOR_COLS
,
2266 CONTEXT_DISABLE_FOR_DISCONTIGUOUS_SELECTION
,
2267 CONTEXT_INSERT
, NULL
},
2268 { N_("_Delete Column(s)"), "gnumeric-column-delete",
2269 CONTEXT_DISPLAY_FOR_COLS
,
2270 CONTEXT_DISABLE_FOR_DISCONTIGUOUS_SELECTION
,
2271 CONTEXT_DELETE
, NULL
},
2272 { N_("_Insert Row(s)"), "gnumeric-row-add",
2273 CONTEXT_DISPLAY_FOR_ROWS
,
2274 CONTEXT_DISABLE_FOR_DISCONTIGUOUS_SELECTION
,
2275 CONTEXT_INSERT
, NULL
},
2276 { N_("_Delete Row(s)"), "gnumeric-row-delete",
2277 CONTEXT_DISPLAY_FOR_ROWS
,
2278 CONTEXT_DISABLE_FOR_DISCONTIGUOUS_SELECTION
,
2279 CONTEXT_DELETE
, NULL
},
2281 { N_("Clear Co_ntents"), "edit-clear",
2282 0, 0, CONTEXT_CLEAR_CONTENT
, NULL
},
2284 { "", NULL
, CONTEXT_DISPLAY_FOR_CELLS
, 0, 0, NULL
},
2286 { N_("Add _Comment..."), "gnumeric-comment-add",
2287 CONTEXT_DISPLAY_WITHOUT_COMMENT
, 0, CONTEXT_COMMENT_ADD
, NULL
},
2288 { N_("Edit Co_mment..."),"gnumeric-comment-edit",
2289 CONTEXT_DISPLAY_WITH_COMMENT
, 0, CONTEXT_COMMENT_EDIT
, NULL
},
2290 { N_("_Remove Comments"), "gnumeric-comment-delete",
2291 CONTEXT_DISPLAY_WITH_COMMENT_IN_RANGE
, 0, CONTEXT_COMMENT_REMOVE
, NULL
},
2293 { N_("Add _Hyperlink..."), "gnumeric-link-add",
2294 CONTEXT_DISPLAY_WITHOUT_HYPERLINK
, 0,
2295 CONTEXT_HYPERLINK_ADD
, NULL
},
2296 { N_("Edit _Hyperlink..."), "gnumeric-link-edit",
2297 CONTEXT_DISPLAY_WITH_HYPERLINK
, 0,
2298 CONTEXT_HYPERLINK_EDIT
, NULL
},
2299 { N_("_Remove Hyperlink"), "gnumeric-link-delete",
2300 CONTEXT_DISPLAY_WITH_HYPERLINK_IN_RANGE
, 0,
2301 CONTEXT_HYPERLINK_REMOVE
, NULL
},
2303 { "", NULL
, 0, 0, 0, NULL
},
2305 { N_("_Edit DataSlicer"), NULL
,
2306 CONTEXT_DISPLAY_WITH_DATA_SLICER
, 0,
2307 CONTEXT_DATA_SLICER_EDIT
, NULL
},
2308 { N_("_Refresh DataSlicer"), NULL
,
2309 CONTEXT_DISPLAY_WITH_DATA_SLICER
, 0,
2310 CONTEXT_DATA_SLICER_REFRESH
, NULL
},
2312 { N_("DataSlicer Field _Order "), NULL
,
2313 CONTEXT_DISPLAY_WITH_DATA_SLICER_ROW
| CONTEXT_DISPLAY_WITH_DATA_SLICER_COL
, 0,
2314 -1, NULL
}, /* start sub menu */
2315 { N_("Left"), "go-previous",
2316 CONTEXT_DISPLAY_WITH_DATA_SLICER_ROW
, 0,
2317 CONTEXT_DATA_SLICER_REFRESH
, NULL
},
2318 { N_("Right"), "go-next",
2319 CONTEXT_DISPLAY_WITH_DATA_SLICER_ROW
, 0,
2320 CONTEXT_DATA_SLICER_REFRESH
, NULL
},
2321 { N_("Up"), "go-up",
2322 CONTEXT_DISPLAY_WITH_DATA_SLICER_COL
, 0,
2323 CONTEXT_DATA_SLICER_REFRESH
, NULL
},
2324 { N_("Down"), "go-down",
2325 CONTEXT_DISPLAY_WITH_DATA_SLICER_COL
, 0,
2326 CONTEXT_DATA_SLICER_REFRESH
, NULL
},
2328 CONTEXT_DISPLAY_WITH_DATA_SLICER_ROW
| CONTEXT_DISPLAY_WITH_DATA_SLICER_COL
, 0,
2329 -1, NULL
}, /* end sub menu */
2331 { N_("_Format All Cells..."), GTK_STOCK_PROPERTIES
,
2332 0, 0, CONTEXT_FORMAT_CELL
, NULL
},
2333 { N_("C_onditional Formatting..."), GTK_STOCK_PROPERTIES
,
2334 0, 0, CONTEXT_FORMAT_CELL_COND
, NULL
},
2335 { N_("Cell"), NULL
, 0, 0, -1, NULL
},/* start sub menu */
2336 { N_("_Merge"), "gnumeric-cells-merge", 0,
2337 CONTEXT_DISABLE_FOR_ONLYMERGES
, CONTEXT_CELL_MERGE
, NULL
},
2338 { N_("_Unmerge"), "gnumeric-cells-split", 0,
2339 CONTEXT_DISABLE_FOR_NOMERGES
, CONTEXT_CELL_UNMERGE
, NULL
},
2340 { N_("Auto Fit _Width"), "gnumeric-column-size", 0, 0, CONTEXT_CELL_AUTOFIT_WIDTH
, NULL
},
2341 { N_("Auto Fit _Height"), "gnumeric-row-size", 0, 0, CONTEXT_CELL_AUTOFIT_HEIGHT
, NULL
},
2342 { "", NULL
, 0, 0, -1, NULL
},/* end sub menu */
2345 /* Column specific (Note some labels duplicate row labels) */
2346 { N_("Column"), NULL
, 0, 0, -1, NULL
},/* start sub menu */
2347 { N_("_Width..."), "gnumeric-column-size", 0, 0, CONTEXT_COL_WIDTH
, NULL
},
2348 { N_("_Auto Fit Width"), "gnumeric-column-size", 0, 0, CONTEXT_COL_AUTOFIT
, NULL
},
2349 { N_("_Hide"), "gnumeric-column-hide", 0, CONTEXT_DISABLE_FOR_ALL_COLS
, CONTEXT_COL_HIDE
, NULL
},
2350 { N_("_Unhide"), "gnumeric-column-unhide", 0, 0, CONTEXT_COL_UNHIDE
, NULL
},
2351 { "", NULL
, 0, 0, -1, NULL
},/* end sub menu */
2353 /* Row specific (Note some labels duplicate col labels) */
2354 { N_("Row"), NULL
, 0, 0, -1, NULL
},/* start sub menu */
2355 { N_("Hei_ght..."), "gnumeric-row-size", 0, 0, CONTEXT_ROW_HEIGHT
, NULL
},
2356 { N_("_Auto Fit Height"), "gnumeric-row-size", 0, 0, CONTEXT_ROW_AUTOFIT
, NULL
},
2357 { N_("_Hide"), "gnumeric-row-hide", 0, CONTEXT_DISABLE_FOR_ALL_ROWS
, CONTEXT_ROW_HIDE
, NULL
},
2358 { N_("_Unhide"), "gnumeric-row-unhide", 0, 0, CONTEXT_ROW_UNHIDE
, NULL
},
2359 { "", NULL
, 0, 0, -1, NULL
},/* end sub menu */
2361 { NULL
, NULL
, 0, 0, 0, NULL
},
2364 /* row and column specific operations */
2365 int display_filter
=
2366 ((!is_col
&& !is_row
) ? CONTEXT_DISPLAY_FOR_CELLS
: 0) |
2367 (is_col
? CONTEXT_DISPLAY_FOR_COLS
: 0) |
2368 (is_row
? CONTEXT_DISPLAY_FOR_ROWS
: 0);
2370 /* Paste special only applies to local copies, not cuts, or remote
2373 int sensitivity_filter
=
2374 (!gnm_app_clipboard_is_empty () &&
2375 !gnm_app_clipboard_is_cut ())
2376 ? 0 : CONTEXT_DISABLE_PASTE_SPECIAL
;
2379 gboolean has_link
= FALSE
, has_comment
= FALSE
;
2380 int n_comments
= 0, n_links
= 0, n_cols
= 0, n_rows
= 0, n_cells
= 0;
2381 GnmSheetSlicer
*slicer
;
2384 gboolean full_sheet
= FALSE
, only_merges
= TRUE
, no_merges
= TRUE
;
2386 wbcg_edit_finish (scg
->wbcg
, WBC_EDIT_REJECT
, NULL
);
2388 /* Now see if there is some selection which selects a whole row or a
2389 * whole column and disable the insert/delete col/row menu items
2392 for (l
= scg_view (scg
)->selections
; l
!= NULL
; l
= l
->next
) {
2393 GnmRange
const *r
= l
->data
;
2394 GnmRange
const *merge
;
2395 GSList
*objs
, *merges
;
2396 GnmStyleList
*styles
;
2398 gboolean rfull_h
= range_is_full (r
, sheet
, TRUE
);
2399 gboolean rfull_v
= range_is_full (r
, sheet
, FALSE
);
2403 if (!range_is_singleton (r
)) {
2404 merge
= gnm_sheet_merge_is_corner (sheet
, &(r
->start
));
2405 if (NULL
== merge
|| !range_equal (merge
, r
))
2406 only_merges
= FALSE
;
2407 merges
= gnm_sheet_merge_get_overlap (sheet
, r
);
2408 if (merges
!= NULL
) {
2410 g_slist_free (merges
);
2415 display_filter
|= CONTEXT_DISPLAY_FOR_COLS
;
2416 display_filter
&= ~CONTEXT_DISPLAY_FOR_CELLS
;
2417 sensitivity_filter
|= CONTEXT_DISABLE_FOR_ALL_ROWS
;
2419 sensitivity_filter
|= CONTEXT_DISABLE_FOR_ROWS
;
2423 display_filter
|= CONTEXT_DISPLAY_FOR_ROWS
;
2424 display_filter
&= ~CONTEXT_DISPLAY_FOR_CELLS
;
2425 sensitivity_filter
|= CONTEXT_DISABLE_FOR_ALL_COLS
;
2427 sensitivity_filter
|= CONTEXT_DISABLE_FOR_COLS
;
2429 if (!(rfull_h
|| rfull_v
))
2430 sensitivity_filter
|= CONTEXT_DISABLE_FOR_CELLS
;
2432 full_sheet
= full_sheet
|| (rfull_h
&& rfull_v
);
2434 h
= range_height (r
);
2435 w
= range_width (r
);
2440 styles
= sheet_style_collect_hlinks (sheet
, r
);
2441 n_links
+= g_slist_length (styles
);
2442 style_list_free (styles
);
2444 objs
= sheet_objects_get (sheet
, r
, GNM_CELL_COMMENT_TYPE
);
2445 n_comments
+= g_slist_length (objs
);
2446 g_slist_free (objs
);
2450 sensitivity_filter
|= CONTEXT_DISABLE_FOR_ONLYMERGES
;
2452 sensitivity_filter
|= CONTEXT_DISABLE_FOR_NOMERGES
;
2455 if ((display_filter
& CONTEXT_DISPLAY_FOR_COLS
) &&
2456 (display_filter
& CONTEXT_DISPLAY_FOR_ROWS
))
2459 sensitivity_filter
|= CONTEXT_DISABLE_FOR_DISCONTIGUOUS_SELECTION
;
2461 has_comment
= (sheet_get_comment (sheet
, &sv
->edit_pos
) != NULL
);
2462 range_init_cellpos (&rge
, &sv
->edit_pos
);
2463 has_link
= (NULL
!= sheet_style_region_contains_link (sheet
, &rge
));
2465 slicer
= sv_editpos_in_slicer (scg_view (scg
));
2466 /* FIXME: disabled for now */
2468 GODataSlicerField
*dsf
= gnm_sheet_slicer_field_header_at_pos (slicer
, &sv
->edit_pos
);
2470 if (go_data_slicer_field_get_field_type_pos (dsf
, GDS_FIELD_TYPE_COL
) >= 0)
2471 display_filter
|= CONTEXT_DISPLAY_WITH_DATA_SLICER_COL
;
2472 if (go_data_slicer_field_get_field_type_pos (dsf
, GDS_FIELD_TYPE_ROW
) >= 0)
2473 display_filter
|= CONTEXT_DISPLAY_WITH_DATA_SLICER_ROW
;
2475 display_filter
|= CONTEXT_DISPLAY_WITH_DATA_SLICER
;
2476 display_filter
&= ~CONTEXT_DISPLAY_FOR_CELLS
;
2479 if (display_filter
& CONTEXT_DISPLAY_FOR_CELLS
) {
2481 display_filter
|= ((has_link
) ?
2482 CONTEXT_DISPLAY_WITH_HYPERLINK
: CONTEXT_DISPLAY_WITHOUT_HYPERLINK
);
2483 display_filter
|= ((n_links
> 0) ?
2484 CONTEXT_DISPLAY_WITH_HYPERLINK_IN_RANGE
: CONTEXT_DISPLAY_WITHOUT_HYPERLINK
);
2485 display_filter
|= ((has_comment
) ?
2486 CONTEXT_DISPLAY_WITH_COMMENT
: CONTEXT_DISPLAY_WITHOUT_COMMENT
);
2487 display_filter
|= ((n_comments
> 0) ?
2488 CONTEXT_DISPLAY_WITH_COMMENT_IN_RANGE
: CONTEXT_DISPLAY_WITHOUT_COMMENT
);
2490 /* xgettext : %d gives the number of links. This is input to ngettext. */
2491 format
= ngettext ("_Remove %d Link", "_Remove %d Links", n_links
);
2492 popup_elements
[POPUPITEM_LINK_REMOVE
].allocated_name
= g_strdup_printf (format
, n_links
);
2494 if (n_comments
> 0) {
2495 /* xgettext : %d gives the number of comments. This is input to ngettext. */
2496 format
= ngettext ("_Remove %d Comment", "_Remove %d Comments", n_comments
);
2497 popup_elements
[POPUPITEM_COMMENT_REMOVE
].allocated_name
= g_strdup_printf (format
, n_comments
);
2499 format
= ngettext ("_Insert %d Cell...", "_Insert %d Cells...", n_cells
);
2500 popup_elements
[POPUPITEM_INSERT_CELL
].allocated_name
= g_strdup_printf (format
, n_cells
);
2501 format
= ngettext ("_Delete %d Cell...", "_Delete %d Cells...", n_cells
);
2502 popup_elements
[POPUPITEM_DELETE_CELL
].allocated_name
= g_strdup_printf (format
, n_cells
);
2505 if (display_filter
& CONTEXT_DISPLAY_FOR_COLS
) {
2507 format
= ngettext ("_Insert %d Column", "_Insert %d Columns", n_cols
);
2508 popup_elements
[POPUPITEM_INSERT_COLUMN
].allocated_name
= g_strdup_printf (format
, n_cols
);
2509 format
= ngettext ("_Delete %d Column", "_Delete %d Columns", n_cols
);
2510 popup_elements
[POPUPITEM_DELETE_COLUMN
].allocated_name
= g_strdup_printf (format
, n_cols
);
2511 if (!(sensitivity_filter
& (CONTEXT_DISABLE_FOR_CELLS
| CONTEXT_DISABLE_FOR_ROWS
))) {
2512 format
= ngettext ("_Format %d Column", "_Format %d Columns", n_cols
);
2513 popup_elements
[POPUPITEM_FORMAT
].allocated_name
2514 = g_strdup_printf (format
, n_cols
);
2517 if (display_filter
& CONTEXT_DISPLAY_FOR_ROWS
) {
2519 format
= ngettext ("_Insert %d Row", "_Insert %d Rows", n_rows
);
2520 popup_elements
[POPUPITEM_INSERT_ROW
].allocated_name
= g_strdup_printf (format
, n_rows
);
2521 format
= ngettext ("_Delete %d Row", "_Delete %d Rows", n_rows
);
2522 popup_elements
[POPUPITEM_DELETE_ROW
].allocated_name
= g_strdup_printf (format
, n_rows
);
2524 if (!(sensitivity_filter
& (CONTEXT_DISABLE_FOR_CELLS
| CONTEXT_DISABLE_FOR_COLS
))) {
2525 format
= ngettext ("_Format %d Row", "_Format %d Rows", n_rows
);
2526 popup_elements
[POPUPITEM_FORMAT
].allocated_name
2527 = g_strdup_printf (format
, n_rows
);
2530 if (!popup_elements
[POPUPITEM_FORMAT
].allocated_name
&& !full_sheet
) {
2532 format
= ngettext ("_Format %d Cell...", "_Format %d Cells", n_cells
);
2533 popup_elements
[POPUPITEM_FORMAT
].allocated_name
= g_strdup_printf (format
, n_cells
);
2537 gnm_create_popup_menu (popup_elements
, &context_menu_handler
,
2538 scg
, display_filter
,
2539 sensitivity_filter
, event
);
2543 cb_redraw_sel (G_GNUC_UNUSED SheetView
*sv
, GnmRange
const *r
, gpointer user_data
)
2545 SheetControl
*sc
= user_data
;
2546 scg_redraw_range (sc
, r
);
2547 scg_redraw_headers (sc
, TRUE
, TRUE
, r
);
2552 scg_cursor_visible (SheetControlGUI
*scg
, gboolean is_visible
)
2554 SheetControl
*sc
= (SheetControl
*) scg
;
2556 /* there is always a grid 0 */
2557 if (NULL
== scg
->pane
[0])
2560 SCG_FOREACH_PANE (scg
, pane
,
2561 gnm_item_cursor_set_visibility (pane
->cursor
.std
, is_visible
););
2563 sv_selection_foreach (sc
->view
, cb_redraw_sel
, sc
);
2566 /***************************************************************************/
2570 * @scg: The sheet control
2572 * Put @sheet into the standard state 'edit mode'. This shuts down
2573 * any object editing and frees any objects that are created but not
2577 scg_mode_edit (SheetControlGUI
*scg
)
2580 g_return_if_fail (GNM_IS_SCG (scg
));
2584 if (wbcg
!= NULL
) /* Can be NULL during destruction */
2585 wbcg_insert_object_clear (wbcg
);
2587 scg_object_unselect (scg
, NULL
);
2589 /* During destruction we have already been disconnected
2590 * so don't bother changing the cursor */
2591 if (scg
->grid
!= NULL
&&
2592 scg_sheet (scg
) != NULL
&&
2593 scg_view (scg
) != NULL
) {
2594 scg_set_display_cursor (scg
);
2595 scg_cursor_visible (scg
, TRUE
);
2598 if (wbcg
!= NULL
&& wbc_gtk_get_guru (wbcg
) != NULL
&&
2599 scg
== wbcg_cur_scg (wbcg
))
2600 wbcg_edit_finish (wbcg
, WBC_EDIT_REJECT
, NULL
);
2603 wb_control_update_action_sensitivity (GNM_WBC (wbcg
));
2607 scg_mode_edit_virt (SheetControl
*sc
)
2609 scg_mode_edit ((SheetControlGUI
*)sc
);
2613 calc_obj_place (GnmPane
*pane
, gint64 canvas_coord
, gboolean is_col
,
2618 ColRowInfo
const *cri
;
2619 Sheet
const *sheet
= scg_sheet (pane
->simple
.scg
);
2622 colrow
= gnm_pane_find_col (pane
, canvas_coord
, &origin
);
2623 cri
= sheet_col_get_info (sheet
, colrow
);
2625 colrow
= gnm_pane_find_row (pane
, canvas_coord
, &origin
);
2626 cri
= sheet_row_get_info (sheet
, colrow
);
2629 /* TODO : handle other anchor types */
2630 *offset
= (canvas_coord
- origin
) / (double)cri
->size_pixels
;
2634 #define SO_CLASS(so) GNM_SO_CLASS (G_OBJECT_GET_CLASS(so))
2637 * scg_object_select:
2638 * @scg: The #SheetControl to edit in.
2639 * @so: The #SheetObject to select.
2641 * Adds @so to the set of selected objects and prepares it for user editing.
2642 * Adds a reference to @ref if it is selected.
2645 scg_object_select (SheetControlGUI
*scg
, SheetObject
*so
)
2649 if (scg
->selected_objects
== NULL
) {
2650 if (wb_view_is_protected (sv_wbv (scg_view (scg
)), TRUE
) ||
2651 !wbcg_edit_finish (scg
->wbcg
, WBC_EDIT_ACCEPT
, NULL
))
2655 wbcg_insert_object_clear (scg
->wbcg
);
2656 scg_cursor_visible (scg
, FALSE
);
2657 scg_set_display_cursor (scg
);
2658 scg_unant (GNM_SC (scg
));
2660 scg
->selected_objects
= g_hash_table_new_full (
2661 g_direct_hash
, g_direct_equal
,
2662 (GDestroyNotify
) g_object_unref
, (GDestroyNotify
) g_free
);
2663 wb_control_update_action_sensitivity (scg_wbc (scg
));
2665 g_return_if_fail (g_hash_table_lookup (scg
->selected_objects
, so
) == NULL
);
2669 coords
= g_new (double, 4);
2670 scg_object_anchor_to_coords (scg
, sheet_object_get_anchor (so
), coords
);
2671 g_hash_table_insert (scg
->selected_objects
, so
, coords
);
2672 g_signal_connect_object (so
, "unrealized",
2673 G_CALLBACK (scg_mode_edit
), scg
, G_CONNECT_SWAPPED
);
2675 SCG_FOREACH_PANE (scg
, pane
,
2676 gnm_pane_object_update_bbox (pane
, so
););
2680 cb_scg_object_unselect (SheetObject
*so
, G_GNUC_UNUSED
double *coords
, SheetControlGUI
*scg
)
2682 SCG_FOREACH_PANE (scg
, pane
, gnm_pane_object_unselect (pane
, so
););
2683 g_signal_handlers_disconnect_by_func (so
,
2684 scg_mode_edit
, scg
);
2688 * scg_object_unselect:
2689 * @scg: #SheetControlGUI
2690 * @so: #SheetObject (optionally NULL)
2692 * unselect the supplied object, and drop out of edit mode if this is the last
2693 * one. If @so == NULL unselect _all_ objects.
2696 scg_object_unselect (SheetControlGUI
*scg
, SheetObject
*so
)
2698 WorkbookControl
*wbc
= scg_wbc (scg
);
2700 /* cheesy cycle avoidance */
2701 if (scg
->selected_objects
== NULL
)
2705 double *pts
= g_hash_table_lookup (scg
->selected_objects
, so
);
2706 g_return_if_fail (pts
!= NULL
);
2707 cb_scg_object_unselect (so
, pts
, scg
);
2708 g_hash_table_remove (scg
->selected_objects
, so
);
2709 if (g_hash_table_size (scg
->selected_objects
) > 0)
2712 g_hash_table_foreach (scg
->selected_objects
,
2713 (GHFunc
) cb_scg_object_unselect
, scg
);
2715 g_hash_table_destroy (scg
->selected_objects
);
2716 scg
->selected_objects
= NULL
;
2717 scg_mode_edit (scg
);
2719 wb_control_update_action_sensitivity (wbc
);
2723 scg_object_select_next (SheetControlGUI
*scg
, gboolean reverse
)
2725 Sheet
*sheet
= scg_sheet (scg
);
2726 GSList
*ptr
= sheet
->sheet_objects
;
2728 g_return_if_fail (ptr
!= NULL
);
2730 if ((scg
->selected_objects
== NULL
) ||
2731 (g_hash_table_size (scg
->selected_objects
) == 0)) {
2732 scg_object_select (scg
, ptr
->data
);
2735 GSList
*prev
= NULL
;
2736 for (; ptr
!= NULL
; prev
= ptr
, ptr
= ptr
->next
)
2737 if (NULL
!= g_hash_table_lookup
2738 (scg
->selected_objects
, ptr
->data
)) {
2739 SheetObject
*target
;
2741 if (ptr
->next
== NULL
)
2742 target
= sheet
->sheet_objects
->data
;
2744 target
= ptr
->next
->data
;
2747 GSList
*last
= g_slist_last (ptr
);
2748 target
= last
->data
;
2750 target
= prev
->data
;
2752 if (ptr
->data
!= target
) {
2753 scg_object_unselect (scg
, NULL
);
2754 scg_object_select (scg
, target
);
2762 SheetControlGUI
*scg
;
2764 SheetObject
*primary_object
;
2768 gboolean snap_to_grid
;
2769 gboolean is_mouse_move
;
2773 snap_pos_to_grid (ObjDragInfo
const *info
, gboolean is_col
, double pos
,
2776 GnmPane
const *pane
= info
->pane
;
2777 Sheet
const *sheet
= scg_sheet (info
->scg
);
2778 int cell
= is_col
? pane
->first
.col
: pane
->first
.row
;
2779 gint64 pixel
= is_col
? pane
->first_offset
.x
: pane
->first_offset
.y
;
2780 gboolean snap
= FALSE
;
2782 ColRowInfo
const *cr_info
;
2783 int sheet_max
= colrow_max (is_col
, sheet
);
2786 while (cell
> 0 && pos
< pixel
) {
2787 cr_info
= sheet_colrow_get_info (sheet
, --cell
, is_col
);
2788 if (cr_info
->visible
) {
2789 length
= cr_info
->size_pixels
;
2797 cr_info
= sheet_colrow_get_info (sheet
, cell
, is_col
);
2798 if (cr_info
->visible
) {
2799 length
= cr_info
->size_pixels
;
2800 if (pixel
<= pos
&& pos
<= pixel
+ length
)
2804 } while (++cell
< sheet_max
&& !snap
);
2807 if (info
->is_mouse_move
)
2808 pos
= (fabs (pos
- pixel
) < fabs (pos
- pixel
- length
)) ? pixel
: pixel
+ length
;
2810 pos
= (pixel
== pos
) ? pixel
: (to_min
? pixel
: pixel
+ length
);
2813 return/* sign */ pos
;
2817 apply_move (SheetObject
*so
, int x_idx
, int y_idx
, double *coords
,
2818 ObjDragInfo
*info
, gboolean snap_to_grid
)
2820 gboolean move_x
= (x_idx
>= 0);
2821 gboolean move_y
= (y_idx
>= 0);
2824 x
= move_x
? coords
[x_idx
] + info
->dx
: 0;
2825 y
= move_y
? coords
[y_idx
] + info
->dy
: 0;
2828 g_return_if_fail (info
->pane
!= NULL
);
2831 x
= snap_pos_to_grid (info
, TRUE
, x
, info
->dx
< 0.);
2833 y
= snap_pos_to_grid (info
, FALSE
, y
, info
->dy
< 0.);
2834 if (info
->primary_object
== so
|| NULL
== info
->primary_object
) {
2835 if (move_x
) info
->dx
= x
- coords
[x_idx
];
2836 if (move_y
) info
->dy
= y
- coords
[y_idx
];
2840 if (move_x
) coords
[x_idx
] = x
;
2841 if (move_y
) coords
[y_idx
] = y
;
2843 if (info
->symmetric
&& !snap_to_grid
) {
2844 if (move_x
) coords
[x_idx
== 0 ? 2 : 0] -= info
->dx
;
2845 if (move_y
) coords
[y_idx
== 1 ? 3 : 1] -= info
->dy
;
2850 drag_object (SheetObject
*so
, double *coords
, ObjDragInfo
*info
)
2854 } const idx_info
[8] = {
2855 { 0, 1}, {-1, 1}, { 2, 1}, { 0,-1},
2856 { 2,-1}, { 0, 3}, {-1, 3}, { 2, 3}
2859 g_return_if_fail (info
->drag_type
<= 8);
2861 if (info
->drag_type
== 8) {
2862 apply_move (so
, 0, 1, coords
, info
, info
->snap_to_grid
);
2863 apply_move (so
, 2, 3, coords
, info
, FALSE
);
2866 idx_info
[info
->drag_type
].x_idx
,
2867 idx_info
[info
->drag_type
].y_idx
,
2868 coords
, info
, info
->snap_to_grid
);
2869 SCG_FOREACH_PANE (info
->scg
, pane
,
2870 gnm_pane_object_update_bbox (pane
, so
););
2874 cb_drag_selected_objects (SheetObject
*so
, double *coords
, ObjDragInfo
*info
)
2876 if (so
!= info
->primary_object
)
2877 drag_object (so
, coords
, info
);
2882 * @scg: #SheetControlGUI
2883 * @primary: #SheetObject (optionally NULL)
2889 * Move the control points and drag views of the currently selected objects to
2890 * a new position. This movement is only made in @scg not in the actual
2894 scg_objects_drag (SheetControlGUI
*scg
, GnmPane
*pane
,
2895 SheetObject
*primary
,
2896 gdouble
*dx
, gdouble
*dy
,
2897 int drag_type
, gboolean symmetric
,
2898 gboolean snap_to_grid
,
2899 gboolean is_mouse_move
)
2906 info
.primary_object
= primary
;
2909 info
.symmetric
= symmetric
;
2910 info
.drag_type
= drag_type
;
2911 info
.snap_to_grid
= snap_to_grid
;
2912 info
.is_mouse_move
= is_mouse_move
;
2914 if (primary
!= NULL
) {
2915 coords
= g_hash_table_lookup (scg
->selected_objects
, primary
);
2916 drag_object (primary
, coords
, &info
);
2919 g_hash_table_foreach (scg
->selected_objects
,
2920 (GHFunc
) cb_drag_selected_objects
, &info
);
2927 SheetControlGUI
*scg
;
2928 GSList
*objects
, *anchors
;
2929 } CollectObjectsData
;
2931 cb_collect_objects_to_commit (SheetObject
*so
, double *coords
, CollectObjectsData
*data
)
2933 SheetObjectAnchor
*anchor
= sheet_object_anchor_dup (
2934 sheet_object_get_anchor (so
));
2935 if (!sheet_object_can_resize (so
)) {
2936 /* FIXME: that code should be invalid */
2937 double scale
= goc_canvas_get_pixels_per_unit (GOC_CANVAS (data
->scg
->pane
[0])) / 72.;
2938 sheet_object_default_size (so
, coords
+ 2, coords
+ 3);
2939 coords
[2] *= gnm_app_display_dpi_get (TRUE
) * scale
;
2940 coords
[3] *= gnm_app_display_dpi_get (FALSE
) * scale
;
2941 coords
[2] += coords
[0];
2942 coords
[3] += coords
[1];
2944 scg_object_coords_to_anchor (data
->scg
, coords
, anchor
);
2945 data
->objects
= g_slist_prepend (data
->objects
, so
);
2946 data
->anchors
= g_slist_prepend (data
->anchors
, anchor
);
2948 if (!sheet_object_rubber_band_directly (so
)) {
2949 SCG_FOREACH_PANE (data
->scg
, pane
, {
2950 GocItem
**ctrl_pts
= g_hash_table_lookup (pane
->drag
.ctrl_pts
, so
);
2951 if (NULL
!= ctrl_pts
[9]) {
2952 double const *pts
= g_hash_table_lookup (
2953 pane
->simple
.scg
->selected_objects
, so
);
2954 SheetObjectView
*sov
= sheet_object_get_view (so
,
2955 (SheetObjectViewContainer
*)pane
);
2957 g_object_unref (ctrl_pts
[9]);
2961 sov
= sheet_object_new_view (so
, (SheetObjectViewContainer
*) pane
);
2963 sheet_object_view_set_bounds (sov
, pts
, TRUE
);
2970 scg_objects_drag_commit_get_undo_text (int drag_type
, int n
,
2971 gboolean created_objects
)
2975 if (created_objects
) {
2977 /* xgettext : %d gives the number of objects. This is input to ngettext. */
2978 format
= ngettext ("Duplicate %d Object", "Duplicate %d Objects", n
);
2980 /* xgettext : %d gives the number of objects. This is input to ngettext. */
2981 format
= ngettext ("Insert %d Object", "Insert %d Objects", n
);
2984 /* xgettext : %d gives the number of objects. This is input to ngettext. */
2985 format
= ngettext ("Move %d Object", "Move %d Objects", n
);
2987 /* xgettext : %d gives the number of objects. This is input to ngettext. */
2988 format
= ngettext ("Resize %d Object", "Resize %d Objects", n
);
2991 return g_strdup_printf (format
, n
);
2996 scg_objects_drag_commit (SheetControlGUI
*scg
, int drag_type
,
2997 gboolean created_objects
,
2998 GOUndo
**pundo
, GOUndo
**predo
, gchar
**undo_title
)
3000 CollectObjectsData data
;
3002 GOUndo
*undo
= NULL
;
3003 GOUndo
*redo
= NULL
;
3005 data
.objects
= data
.anchors
= NULL
;
3007 g_hash_table_foreach (scg
->selected_objects
,
3008 (GHFunc
) cb_collect_objects_to_commit
, &data
);
3010 undo
= sheet_object_move_undo (data
.objects
, created_objects
);
3011 redo
= sheet_object_move_do (data
.objects
, data
.anchors
, created_objects
);
3012 text
= scg_objects_drag_commit_get_undo_text
3013 (drag_type
, g_slist_length (data
.objects
), created_objects
);
3015 if (pundo
&& predo
) {
3021 cmd_generic (GNM_WBC (scg_wbcg (scg
)),
3025 g_slist_free (data
.objects
);
3026 g_slist_free_full (data
.anchors
, g_free
);
3030 scg_objects_nudge (SheetControlGUI
*scg
, GnmPane
*pane
,
3031 int drag_type
, double dx
, double dy
, gboolean symmetric
, gboolean snap_to_grid
)
3033 /* no nudging if we are creating an object */
3034 if (!scg
->wbcg
->new_object
) {
3035 scg_objects_drag (scg
, pane
, NULL
, &dx
, &dy
, drag_type
, symmetric
, snap_to_grid
, FALSE
);
3036 scg_objects_drag_commit (scg
, drag_type
, FALSE
, NULL
, NULL
, NULL
);
3041 scg_object_coords_to_anchor (SheetControlGUI
const *scg
,
3042 double const *coords
, SheetObjectAnchor
*in_out
)
3044 Sheet
*sheet
= scg_sheet (scg
);
3045 /* pane 0 always exists and the others are always use the same basis */
3046 GnmPane
*pane
= scg_pane ((SheetControlGUI
*)scg
, 0);
3048 g_return_if_fail (GNM_IS_SCG (scg
));
3049 g_return_if_fail (coords
!= NULL
);
3051 in_out
->base
.direction
= GOD_ANCHOR_DIR_NONE_MASK
;
3052 if (coords
[0] > coords
[2]) {
3058 in_out
->base
.direction
= GOD_ANCHOR_DIR_RIGHT
;
3060 if (coords
[1] > coords
[3]) {
3066 in_out
->base
.direction
|= GOD_ANCHOR_DIR_DOWN
;
3069 switch (in_out
->mode
) {
3070 case GNM_SO_ANCHOR_TWO_CELLS
:
3071 in_out
->cell_bound
.start
.col
= calc_obj_place (pane
, tmp
[0], TRUE
,
3072 in_out
->offset
+ 0);
3073 in_out
->cell_bound
.start
.row
= calc_obj_place (pane
, tmp
[1], FALSE
,
3074 in_out
->offset
+ 1);
3075 in_out
->cell_bound
.end
.col
= calc_obj_place (pane
, tmp
[2], TRUE
,
3076 in_out
->offset
+ 2);
3077 in_out
->cell_bound
.end
.row
= calc_obj_place (pane
, tmp
[3], FALSE
,
3078 in_out
->offset
+ 3);
3080 case GNM_SO_ANCHOR_ONE_CELL
:
3081 in_out
->cell_bound
.start
.col
= calc_obj_place (pane
, tmp
[0], TRUE
,
3082 in_out
->offset
+ 0);
3083 in_out
->cell_bound
.start
.row
= calc_obj_place (pane
, tmp
[1], FALSE
,
3084 in_out
->offset
+ 1);
3085 in_out
->cell_bound
.end
= in_out
->cell_bound
.start
;
3086 in_out
->offset
[2] = (tmp
[2] - tmp
[0]) / colrow_compute_pixel_scale (sheet
, TRUE
);
3087 in_out
->offset
[3] = (tmp
[3] - tmp
[1]) / colrow_compute_pixel_scale (sheet
, FALSE
);
3089 case GNM_SO_ANCHOR_ABSOLUTE
: {
3091 range_init (&in_out
->cell_bound
, 0, 0, 0, 0);
3092 h
= colrow_compute_pixel_scale (sheet
, TRUE
);
3093 v
= colrow_compute_pixel_scale (sheet
, FALSE
);
3094 in_out
->offset
[0] = tmp
[0] / h
;
3095 in_out
->offset
[1] = tmp
[1] / v
;
3096 in_out
->offset
[2] = (tmp
[2] - tmp
[0]) / h
;
3097 in_out
->offset
[3] = (tmp
[3] - tmp
[1]) / v
;
3104 cell_offset_calc_pixel (Sheet
const *sheet
, int i
, gboolean is_col
,
3107 ColRowInfo
const *cri
= sheet_colrow_get_info (sheet
, i
, is_col
);
3108 return offset
* cri
->size_pixels
;
3112 scg_object_anchor_to_coords (SheetControlGUI
const *scg
,
3113 SheetObjectAnchor
const *anchor
, double *coords
)
3115 Sheet
*sheet
= scg_sheet (scg
);
3116 GODrawingAnchorDir direction
;
3120 g_return_if_fail (GNM_IS_SCG (scg
));
3121 g_return_if_fail (anchor
!= NULL
);
3122 g_return_if_fail (coords
!= NULL
);
3124 r
= &anchor
->cell_bound
;
3125 if (anchor
->mode
!= GNM_SO_ANCHOR_ABSOLUTE
) {
3126 pixels
[0] = scg_colrow_distance_get (scg
, TRUE
, 0, r
->start
.col
);
3127 pixels
[1] = scg_colrow_distance_get (scg
, FALSE
, 0, r
->start
.row
);
3128 if (anchor
->mode
== GNM_SO_ANCHOR_TWO_CELLS
) {
3129 pixels
[2] = pixels
[0] + scg_colrow_distance_get (scg
, TRUE
,
3130 r
->start
.col
, r
->end
.col
);
3131 pixels
[3] = pixels
[1] + scg_colrow_distance_get (scg
, FALSE
,
3132 r
->start
.row
, r
->end
.row
);
3133 /* add .5 to offsets so that the rounding is optimal */
3134 pixels
[0] += cell_offset_calc_pixel (sheet
, r
->start
.col
,
3135 TRUE
, anchor
->offset
[0]) + .5;
3136 pixels
[1] += cell_offset_calc_pixel (sheet
, r
->start
.row
,
3137 FALSE
, anchor
->offset
[1]) + .5;
3138 pixels
[2] += cell_offset_calc_pixel (sheet
, r
->end
.col
,
3139 TRUE
, anchor
->offset
[2]) + .5;
3140 pixels
[3] += cell_offset_calc_pixel (sheet
, r
->end
.row
,
3141 FALSE
, anchor
->offset
[3]) + .5;
3143 /* add .5 to offsets so that the rounding is optimal */
3144 pixels
[0] += cell_offset_calc_pixel (sheet
, r
->start
.col
,
3145 TRUE
, anchor
->offset
[0]) + .5;
3146 pixels
[1] += cell_offset_calc_pixel (sheet
, r
->start
.row
,
3147 FALSE
, anchor
->offset
[1]) + .5;
3148 pixels
[2] = pixels
[0] + go_fake_floor (anchor
->offset
[2] * colrow_compute_pixel_scale (sheet
, TRUE
) + .5);
3149 pixels
[3] = pixels
[1] + go_fake_floor (anchor
->offset
[3] * colrow_compute_pixel_scale (sheet
, TRUE
) + .5);
3153 h
= colrow_compute_pixel_scale (sheet
, TRUE
);
3154 v
= colrow_compute_pixel_scale (sheet
, FALSE
);
3155 pixels
[0] = go_fake_floor (anchor
->offset
[0] * h
);
3156 pixels
[1] = go_fake_floor (anchor
->offset
[1] * v
);
3157 pixels
[2] = go_fake_floor ((anchor
->offset
[0] + anchor
->offset
[2]) * h
);
3158 pixels
[3] = go_fake_floor ((anchor
->offset
[1] + anchor
->offset
[3]) * v
);
3161 direction
= anchor
->base
.direction
;
3162 if (direction
== GOD_ANCHOR_DIR_UNKNOWN
)
3163 direction
= GOD_ANCHOR_DIR_DOWN_RIGHT
;
3165 coords
[0] = pixels
[direction
& GOD_ANCHOR_DIR_H_MASK
? 0 : 2];
3166 coords
[1] = pixels
[direction
& GOD_ANCHOR_DIR_V_MASK
? 1 : 3];
3167 coords
[2] = pixels
[direction
& GOD_ANCHOR_DIR_H_MASK
? 2 : 0];
3168 coords
[3] = pixels
[direction
& GOD_ANCHOR_DIR_V_MASK
? 3 : 1];
3171 /***************************************************************************/
3174 scg_comment_display_filter_cb (PangoAttribute
*attribute
, gboolean
*state
)
3176 if (attribute
->klass
->type
== PANGO_ATTR_FOREGROUND
&&
3177 attribute
->start_index
!= attribute
->end_index
)
3183 * scg_comment_display:
3184 * @scg: The SheetControl
3185 * @cc: A cell comment
3189 scg_comment_display (SheetControlGUI
*scg
, GnmComment
*cc
,
3192 g_return_if_fail (GNM_IS_SCG (scg
));
3194 scg_comment_timer_clear (scg
);
3196 /* If someone clicked and dragged the comment marker this may be NULL */
3197 if (scg
->comment
.selected
== NULL
)
3201 cc
= scg
->comment
.selected
;
3202 else if (scg
->comment
.selected
!= cc
)
3203 scg_comment_unselect (scg
, scg
->comment
.selected
);
3205 g_return_if_fail (GNM_IS_CELL_COMMENT (cc
));
3207 if (scg
->comment
.item
== NULL
) {
3208 GtkWidget
*label
, *box
;
3210 PangoAttrList
*comment_markup
;
3211 char const *comment_author
;
3213 g_object_get (G_OBJECT (cc
),
3214 "text", &comment_text
,
3215 "markup", &comment_markup
,
3217 comment_author
= cell_comment_author_get (cc
);
3219 box
= gtk_box_new (GTK_ORIENTATION_VERTICAL
, FALSE
);
3221 if (comment_author
!= NULL
) {
3223 PangoAttrList
*attrs
;
3224 PangoAttribute
*attr
;
3226 /* xgettext: this is a by-line for cell comments */
3227 text
= g_strdup_printf (_("By %s:"), comment_author
);
3228 label
= gtk_label_new (text
);
3231 attrs
= pango_attr_list_new ();
3232 attr
= pango_attr_weight_new (PANGO_WEIGHT_BOLD
);
3233 attr
->start_index
= 0;
3234 attr
->end_index
= G_MAXINT
;
3235 pango_attr_list_insert (attrs
, attr
);
3236 gtk_label_set_attributes (GTK_LABEL (label
), attrs
);
3237 pango_attr_list_unref (attrs
);
3239 gtk_widget_set_halign (label
, GTK_ALIGN_START
);
3240 gtk_box_pack_start (GTK_BOX (box
), label
, FALSE
, TRUE
, 0);
3241 gtk_box_set_spacing (GTK_BOX (box
), 10);
3244 label
= gtk_label_new (comment_text
);
3245 if (comment_markup
) {
3246 gboolean font_colour_set
= FALSE
;
3247 pango_attr_list_filter
3249 (PangoAttrFilterFunc
) scg_comment_display_filter_cb
,
3251 if (font_colour_set
) {
3252 /* Imported comments may have a font colour set. */
3253 /* If that is the case, we set a background colour. */
3254 guint length
= strlen (comment_text
);
3255 PangoAttribute
*attr
= pango_attr_foreground_new (0,0,0);
3256 attr
->start_index
= 0;
3257 attr
->end_index
= length
;
3258 pango_attr_list_insert_before (comment_markup
, attr
);
3259 attr
= pango_attr_background_new (255*255, 255*255, 224*255 );
3260 attr
->start_index
= 0;
3261 attr
->end_index
= length
;
3262 pango_attr_list_insert_before (comment_markup
, attr
);
3264 gtk_label_set_attributes (GTK_LABEL (label
), comment_markup
);
3266 g_free (comment_text
);
3267 gtk_widget_set_halign (label
, GTK_ALIGN_START
);
3268 gtk_box_pack_start (GTK_BOX (box
), label
, TRUE
, TRUE
, 0);
3270 gnm_convert_to_tooltip (GTK_WIDGET (scg
->grid
), box
);
3272 scg
->comment
.item
= gtk_widget_get_toplevel (box
);
3273 gtk_window_move (GTK_WINDOW (scg
->comment
.item
),
3276 gtk_widget_show_all (scg
->comment
.item
);
3281 cb_cell_comment_timer (SheetControlGUI
*scg
)
3283 g_return_val_if_fail (GNM_IS_SCG (scg
), FALSE
);
3284 g_return_val_if_fail (scg
->comment
.timer
!= 0, FALSE
);
3286 scg
->comment
.timer
= 0;
3287 scg_comment_display (scg
, scg
->comment
.selected
,
3288 scg
->comment
.x
, scg
->comment
.y
);
3293 * scg_comment_select:
3294 * @scg: The SheetControl
3295 * @cc: A cell comment
3297 * Prepare @cc for display.
3300 scg_comment_select (SheetControlGUI
*scg
, GnmComment
*cc
, int x
, int y
)
3302 g_return_if_fail (GNM_IS_SCG (scg
));
3304 if (scg
->comment
.selected
!= NULL
)
3305 scg_comment_unselect (scg
, scg
->comment
.selected
);
3307 g_return_if_fail (scg
->comment
.timer
== 0);
3309 scg
->comment
.selected
= cc
;
3310 scg
->comment
.timer
= g_timeout_add (1000,
3311 (GSourceFunc
)cb_cell_comment_timer
, scg
);
3317 * scg_comment_unselect:
3318 * @scg: The SheetControl
3319 * @cc: A cell comment
3321 * If @cc is the current cell comment being edited/displayed shutdown the
3322 * display mechanism.
3325 scg_comment_unselect (SheetControlGUI
*scg
, GnmComment
*cc
)
3327 g_return_if_fail (GNM_IS_SCG (scg
));
3329 if (cc
== scg
->comment
.selected
) {
3330 scg
->comment
.selected
= NULL
;
3331 scg_comment_timer_clear (scg
);
3333 if (scg
->comment
.item
!= NULL
) {
3334 gtk_widget_destroy (scg
->comment
.item
);
3335 scg
->comment
.item
= NULL
;
3340 /************************************************************************/
3341 /* Col/Row size support routines. */
3344 scg_colrow_distance_get (SheetControlGUI
const *scg
, gboolean is_cols
,
3347 Sheet
*sheet
= scg_sheet (scg
);
3348 ColRowCollection
const *collection
;
3354 g_return_val_if_fail (GNM_IS_SCG (scg
), 1);
3363 g_return_val_if_fail (from
>= 0, 1);
3366 g_return_val_if_fail (to
<= gnm_sheet_get_max_cols (sheet
), 1);
3367 collection
= &sheet
->cols
;
3369 g_return_val_if_fail (to
<= gnm_sheet_get_max_rows (sheet
), 1);
3370 collection
= &sheet
->rows
;
3373 /* Do not use col_row_foreach, it ignores empties.
3374 * Optimize this so that long jumps are not quite so horrific
3377 default_size
= collection
->default_style
.size_pixels
;
3378 for (i
= from
; i
< to
; ++i
) {
3379 ColRowSegment
const *segment
=
3380 COLROW_GET_SEGMENT(collection
, i
);
3382 if (segment
!= NULL
) {
3383 ColRowInfo
const *cri
= segment
->info
[COLROW_SUB_INDEX (i
)];
3385 pixels
+= default_size
;
3386 else if (cri
->visible
)
3387 pixels
+= cri
->size_pixels
;
3389 int segment_end
= COLROW_SEGMENT_END (i
)+1;
3390 if (segment_end
> to
)
3392 pixels
+= default_size
* (segment_end
- i
);
3393 i
= segment_end
- 1;
3400 /*************************************************************************/
3403 scg_cursor_bound (SheetControl
*sc
, GnmRange
const *r
)
3405 SheetControlGUI
*scg
= (SheetControlGUI
*) sc
;
3406 SCG_FOREACH_PANE (scg
, pane
, gnm_pane_cursor_bound_set (pane
, r
););
3410 scg_recompute_visible_region (SheetControl
*sc
, gboolean full_recompute
)
3412 SheetControlGUI
*scg
= (SheetControlGUI
*) sc
;
3414 SCG_FOREACH_PANE (scg
, pane
,
3415 gnm_pane_compute_visible_region (pane
, full_recompute
););
3419 scg_edit_start (SheetControlGUI
*scg
)
3421 g_return_if_fail (GNM_IS_SCG (scg
));
3423 SCG_FOREACH_PANE (scg
, pane
, gnm_pane_edit_start (pane
););
3427 scg_edit_stop (SheetControlGUI
*scg
)
3429 g_return_if_fail (GNM_IS_SCG (scg
));
3431 scg_rangesel_stop (scg
, FALSE
);
3432 SCG_FOREACH_PANE (scg
, pane
, gnm_pane_edit_stop (pane
););
3436 * scg_rangesel_changed:
3439 * Notify expr_entry that the expression range has changed.
3442 scg_rangesel_changed (SheetControlGUI
*scg
,
3443 int base_col
, int base_row
,
3444 int move_col
, int move_row
)
3446 GnmExprEntry
*expr_entry
;
3447 gboolean ic_changed
;
3448 GnmRange
*r
, last_r
;
3451 g_return_if_fail (GNM_IS_SCG (scg
));
3453 scg
->rangesel
.base_corner
.col
= base_col
;
3454 scg
->rangesel
.base_corner
.row
= base_row
;
3455 scg
->rangesel
.move_corner
.col
= move_col
;
3456 scg
->rangesel
.move_corner
.row
= move_row
;
3458 r
= &scg
->rangesel
.displayed
;
3459 if (base_col
< move_col
) {
3460 r
->start
.col
= base_col
;
3461 r
->end
.col
= move_col
;
3463 r
->end
.col
= base_col
;
3464 r
->start
.col
= move_col
;
3466 if (base_row
< move_row
) {
3467 r
->start
.row
= base_row
;
3468 r
->end
.row
= move_row
;
3470 r
->end
.row
= base_row
;
3471 r
->start
.row
= move_row
;
3474 sheet
= scg_sheet (scg
);
3475 expr_entry
= wbcg_get_entry_logical (scg
->wbcg
);
3477 gnm_expr_entry_freeze (expr_entry
);
3478 /* The order here is tricky.
3479 * 1) Assign the range to the expr entry.
3481 ic_changed
= gnm_expr_entry_load_from_range (
3482 expr_entry
, sheet
, r
);
3484 /* 2) if the expr entry changed the region get the new region */
3486 gnm_expr_entry_get_rangesel (expr_entry
, r
, NULL
);
3488 /* 3) now double check that all merged regions are fully contained */
3490 gnm_sheet_merge_find_bounding_box (sheet
, r
);
3491 if (!range_equal (&last_r
, r
))
3492 gnm_expr_entry_load_from_range (expr_entry
, sheet
, r
);
3494 gnm_expr_entry_thaw (expr_entry
);
3496 SCG_FOREACH_PANE (scg
, pane
, gnm_pane_rangesel_bound_set (pane
, r
););
3500 scg_rangesel_start (SheetControlGUI
*scg
,
3501 int base_col
, int base_row
,
3502 int move_col
, int move_row
)
3506 g_return_if_fail (GNM_IS_SCG (scg
));
3508 if (scg
->rangesel
.active
)
3511 if (scg
->wbcg
->rangesel
!= NULL
)
3512 g_warning ("misconfiged rangesel");
3514 scg
->wbcg
->rangesel
= scg
;
3515 scg
->rangesel
.active
= TRUE
;
3517 gnm_expr_entry_find_range (wbcg_get_entry_logical (scg
->wbcg
));
3519 range_init (&r
, base_col
, base_row
, move_col
, move_row
);
3520 SCG_FOREACH_PANE (scg
, pane
, gnm_pane_rangesel_start (pane
, &r
););
3521 scg_rangesel_changed (scg
, base_col
, base_row
, move_col
, move_row
);
3525 scg_rangesel_stop (SheetControlGUI
*scg
, gboolean clear_string
)
3527 g_return_if_fail (GNM_IS_SCG (scg
));
3529 if (!scg
->rangesel
.active
)
3531 if (scg
->wbcg
->rangesel
!= scg
)
3532 g_warning ("misconfiged rangesel");
3534 scg
->wbcg
->rangesel
= NULL
;
3535 scg
->rangesel
.active
= FALSE
;
3536 SCG_FOREACH_PANE (scg
, pane
, gnm_pane_rangesel_stop (pane
););
3538 gnm_expr_entry_rangesel_stop (wbcg_get_entry_logical (scg
->wbcg
),
3543 * scg_set_display_cursor:
3546 * Set the displayed cursor type.
3549 scg_set_display_cursor (SheetControlGUI
*scg
)
3551 GdkCursorType cursor
= GDK_CURSOR_IS_PIXMAP
;
3553 g_return_if_fail (GNM_IS_SCG (scg
));
3555 if (scg
->wbcg
->new_object
!= NULL
)
3556 cursor
= GDK_CROSSHAIR
;
3558 SCG_FOREACH_PANE (scg
, pane
, {
3559 GtkWidget
*w
= GTK_WIDGET (pane
);
3560 if (gtk_widget_get_window (w
)) {
3561 if (cursor
== GDK_CURSOR_IS_PIXMAP
)
3562 gnm_widget_set_cursor (w
, pane
->mouse_cursor
);
3564 gnm_widget_set_cursor_type (w
, cursor
);
3570 scg_rangesel_extend_to (SheetControlGUI
*scg
, int col
, int row
)
3572 int base_col
, base_row
;
3576 col
= gnm_sheet_get_last_col (scg_sheet (scg
));
3578 base_col
= scg
->rangesel
.base_corner
.col
;
3581 row
= gnm_sheet_get_last_row (scg_sheet (scg
));
3583 base_row
= scg
->rangesel
.base_corner
.row
;
3585 if (scg
->rangesel
.active
)
3586 scg_rangesel_changed (scg
, base_col
, base_row
, col
, row
);
3588 scg_rangesel_start (scg
, base_col
, base_row
, col
, row
);
3592 scg_rangesel_bound (SheetControlGUI
*scg
,
3593 int base_col
, int base_row
,
3594 int move_col
, int move_row
)
3596 if (scg
->rangesel
.active
)
3597 scg_rangesel_changed (scg
, base_col
, base_row
, move_col
, move_row
);
3599 scg_rangesel_start (scg
, base_col
, base_row
, move_col
, move_row
);
3603 scg_rangesel_move (SheetControlGUI
*scg
, int n
, gboolean jump_to_bound
,
3606 SheetView
*sv
= scg_view (scg
);
3609 if (!scg
->rangesel
.active
) {
3610 tmp
.col
= sv
->edit_pos_real
.col
;
3611 tmp
.row
= sv
->edit_pos_real
.row
;
3613 tmp
= scg
->rangesel
.base_corner
;
3616 tmp
.col
= sheet_find_boundary_horizontal (
3617 sv_sheet (sv
), tmp
.col
, tmp
.row
, tmp
.row
, n
, jump_to_bound
);
3619 tmp
.row
= sheet_find_boundary_vertical (
3620 sv_sheet (sv
), tmp
.col
, tmp
.row
, tmp
.col
, n
, jump_to_bound
);
3622 if (scg
->rangesel
.active
)
3623 scg_rangesel_changed (scg
, tmp
.col
, tmp
.row
, tmp
.col
, tmp
.row
);
3625 scg_rangesel_start (scg
, tmp
.col
, tmp
.row
, tmp
.col
, tmp
.row
);
3626 scg_make_cell_visible (scg
, tmp
.col
, tmp
.row
, FALSE
, FALSE
);
3627 gnm_expr_entry_signal_update (
3628 wbcg_get_entry_logical (scg
->wbcg
), FALSE
);
3632 scg_rangesel_extend (SheetControlGUI
*scg
, int n
,
3633 gboolean jump_to_bound
, gboolean horiz
)
3635 Sheet
*sheet
= scg_sheet (scg
);
3637 if (scg
->rangesel
.active
) {
3638 GnmCellPos tmp
= scg
->rangesel
.move_corner
;
3641 tmp
.col
= sheet_find_boundary_horizontal (sheet
,
3642 tmp
.col
, tmp
.row
, scg
->rangesel
.base_corner
.row
,
3645 tmp
.row
= sheet_find_boundary_vertical (sheet
,
3646 tmp
.col
, tmp
.row
, scg
->rangesel
.base_corner
.col
,
3649 scg_rangesel_changed (scg
,
3650 scg
->rangesel
.base_corner
.col
,
3651 scg
->rangesel
.base_corner
.row
, tmp
.col
, tmp
.row
);
3653 scg_make_cell_visible (scg
,
3654 scg
->rangesel
.move_corner
.col
,
3655 scg
->rangesel
.move_corner
.row
, FALSE
, TRUE
);
3656 gnm_expr_entry_signal_update (
3657 wbcg_get_entry_logical (scg
->wbcg
), FALSE
);
3659 scg_rangesel_move (scg
, n
, jump_to_bound
, horiz
);
3665 * @dir: Number of units to move the cursor
3666 * @jump_to_bound: skip from the start to the end of ranges
3667 * of filled or unfilled cells.
3668 * @horiz: is the movement horizontal or vertical
3670 * Moves the cursor count rows
3673 scg_cursor_move (SheetControlGUI
*scg
, int n
,
3674 gboolean jump_to_bound
, gboolean horiz
)
3676 SheetView
*sv
= scg_view (scg
);
3677 GnmCellPos tmp
= sv
->edit_pos_real
;
3678 int step
= (n
>0) ? 1 : -1;
3680 if (!wbcg_edit_finish (scg
->wbcg
, WBC_EDIT_ACCEPT
, NULL
))
3684 tmp
.col
= sheet_find_boundary_horizontal (sv
->sheet
,
3685 tmp
.col
+ n
- step
, tmp
.row
, tmp
.row
,
3686 step
, jump_to_bound
);
3688 tmp
.row
= sheet_find_boundary_vertical
3690 tmp
.col
, tmp
.row
+ n
- step
,
3692 step
, jump_to_bound
);
3694 sv_selection_reset (sv
);
3695 sv_cursor_set (sv
, &tmp
,
3696 tmp
.col
, tmp
.row
, tmp
.col
, tmp
.row
, NULL
);
3697 sv_make_cell_visible (sv
, tmp
.col
, tmp
.row
, FALSE
);
3698 sv_selection_add_pos (sv
, tmp
.col
, tmp
.row
, GNM_SELECTION_MODE_ADD
);
3702 * scg_cursor_extend:
3704 * @n: Units to extend the selection
3705 * @jump_to_bound: Move to transitions between cells and blanks,
3706 * or move in single steps.
3707 * @horiz: extend vertically or horizontally.
3710 scg_cursor_extend (SheetControlGUI
*scg
, int n
,
3711 gboolean jump_to_bound
, gboolean horiz
)
3713 SheetView
*sv
= scg_view (scg
);
3714 GnmCellPos move
= sv
->cursor
.move_corner
;
3715 GnmCellPos visible
= scg
->pane
[0]->first
;
3717 if (!wbcg_edit_finish (scg
->wbcg
, WBC_EDIT_ACCEPT
, NULL
))
3721 visible
.col
= move
.col
= sheet_find_boundary_horizontal (sv
->sheet
,
3722 move
.col
, move
.row
, sv
->cursor
.base_corner
.row
,
3725 visible
.row
= move
.row
= sheet_find_boundary_vertical (sv
->sheet
,
3726 move
.col
, move
.row
, sv
->cursor
.base_corner
.col
,
3729 sv_selection_extend_to (sv
, move
.col
, move
.row
);
3730 sv_make_cell_visible (sv
, visible
.col
, visible
.row
, FALSE
);
3734 scg_take_focus (SheetControlGUI
*scg
)
3736 g_return_if_fail (GNM_IS_SCG (scg
));
3738 /* FIXME: Slightly hackish. */
3739 if (wbcg_toplevel (scg
->wbcg
))
3740 gtk_window_set_focus (wbcg_toplevel (scg
->wbcg
),
3741 (scg_sheet (scg
)->sheet_type
== GNM_SHEET_OBJECT
)?
3742 GTK_WIDGET (scg
->vs
): GTK_WIDGET (scg_pane (scg
, 0)));
3745 /*********************************************************************************/
3747 scg_size_guide_start (SheetControlGUI
*scg
,
3748 gboolean vert
, int colrow
, gboolean is_colrow_resize
)
3750 g_return_if_fail (GNM_IS_SCG (scg
));
3751 SCG_FOREACH_PANE (scg
, pane
,
3752 gnm_pane_size_guide_start (pane
, vert
, colrow
, is_colrow_resize
););
3755 scg_size_guide_motion (SheetControlGUI
*scg
, gboolean vert
, gint64 guide_pos
)
3757 g_return_if_fail (GNM_IS_SCG (scg
));
3758 SCG_FOREACH_PANE (scg
, pane
,
3759 gnm_pane_size_guide_motion (pane
, vert
, guide_pos
););
3762 scg_size_guide_stop (SheetControlGUI
*scg
)
3764 g_return_if_fail (GNM_IS_SCG (scg
));
3765 SCG_FOREACH_PANE (scg
, pane
,
3766 gnm_pane_size_guide_stop (pane
););
3768 /*********************************************************************************/
3771 scg_special_cursor_start (SheetControlGUI
*scg
, int style
, int button
)
3773 g_return_if_fail (GNM_IS_SCG (scg
));
3775 SCG_FOREACH_PANE (scg
, pane
,
3776 gnm_pane_special_cursor_start (pane
, style
, button
););
3780 scg_special_cursor_stop (SheetControlGUI
*scg
)
3782 g_return_if_fail (GNM_IS_SCG (scg
));
3784 SCG_FOREACH_PANE (scg
, pane
,
3785 gnm_pane_special_cursor_stop (pane
););
3789 scg_special_cursor_bound_set (SheetControlGUI
*scg
, GnmRange
const *r
)
3791 gboolean changed
= FALSE
;
3793 g_return_val_if_fail (GNM_IS_SCG (scg
), FALSE
);
3795 SCG_FOREACH_PANE (scg
, pane
,
3796 changed
|= gnm_pane_special_cursor_bound_set (pane
, r
););
3801 scg_object_create_view (SheetControl
*sc
, SheetObject
*so
)
3803 SheetControlGUI
*scg
= GNM_SCG (sc
);
3804 if (scg
->active_panes
)
3805 SCG_FOREACH_PANE (scg
, pane
,
3806 sheet_object_new_view (so
, (SheetObjectViewContainer
*)pane
););
3808 sheet_object_new_view (so
, (SheetObjectViewContainer
*)scg
->vs
);
3812 scg_scale_changed (SheetControl
*sc
)
3814 SheetControlGUI
*scg
= (SheetControlGUI
*)sc
;
3815 Sheet
*sheet
= scg_sheet (scg
);
3819 g_return_if_fail (GNM_IS_SCG (scg
));
3821 z
= sheet
->last_zoom_factor_used
;
3823 SCG_FOREACH_PANE (scg
, pane
, {
3824 if (pane
->col
.canvas
!= NULL
)
3825 goc_canvas_set_pixels_per_unit (pane
->col
.canvas
, z
);
3826 if (pane
->row
.canvas
!= NULL
)
3827 goc_canvas_set_pixels_per_unit (pane
->row
.canvas
, z
);
3828 goc_canvas_set_pixels_per_unit (GOC_CANVAS (pane
), z
);
3831 scg_resize (scg
, TRUE
);
3832 set_resize_pane_pos (scg
, scg
->vpane
);
3833 set_resize_pane_pos (scg
, scg
->hpane
);
3834 /* now, update sheet objects positions and sizes */
3835 for (ptr
= sheet
->sheet_objects
; ptr
; ptr
= ptr
->next
)
3836 sheet_object_update_bounds (GNM_SO (ptr
->data
), NULL
);
3840 cb_cell_im_timer (SheetControlGUI
*scg
)
3842 g_return_val_if_fail (GNM_IS_SCG (scg
), FALSE
);
3843 g_return_val_if_fail (scg
->im
.timer
!= 0, FALSE
);
3846 scg_im_destroy (scg
);
3851 scg_find_pane (SheetControlGUI
*scg
, GnmCellPos
*pos
)
3855 for (i
= 0; i
< scg
->active_panes
; i
++) {
3856 GnmPane
*pane
= scg
->pane
[i
];
3859 pane
->first
.col
<= pos
->col
&&
3860 pane
->first
.row
<= pos
->row
&&
3861 pane
->last_visible
.col
>= pos
->col
&&
3862 pane
->last_visible
.row
>= pos
->row
)
3869 scg_show_im_tooltip (SheetControl
*sc
, GnmInputMsg
*im
, GnmCellPos
*pos
)
3871 SheetControlGUI
*scg
= (SheetControlGUI
*)sc
;
3874 g_return_if_fail (GNM_IS_SCG (scg
));
3876 scg_im_destroy (scg
);
3878 pane
= scg_find_pane (scg
, pos
);
3881 GtkWidget
*label
, *box
;
3882 char const *text
, *title
;
3883 int len_text
, len_title
;
3884 int x
, y
, x_origin
, y_origin
;
3885 GtkAllocation allocation
;
3886 Sheet
*sheet
= scg_sheet (scg
);
3887 gboolean rtl
= sheet
->text_is_rtl
;
3889 text
= gnm_input_msg_get_msg (im
);
3890 title
= gnm_input_msg_get_title (im
);
3891 len_text
= (text
== NULL
) ? 0 : strlen (text
);
3892 len_title
= (title
== NULL
) ? 0 : strlen (title
);
3894 if ((len_text
== 0) && (len_title
== 0))
3897 box
= gtk_box_new (GTK_ORIENTATION_VERTICAL
, FALSE
);
3899 if (len_title
> 0) {
3900 PangoAttrList
*attrs
;
3901 PangoAttribute
*attr
;
3903 label
= gtk_label_new (title
);
3905 attrs
= pango_attr_list_new ();
3906 attr
= pango_attr_weight_new (PANGO_WEIGHT_BOLD
);
3907 attr
->start_index
= 0;
3908 attr
->end_index
= G_MAXINT
;
3909 pango_attr_list_insert (attrs
, attr
);
3910 gtk_label_set_attributes (GTK_LABEL (label
), attrs
);
3911 pango_attr_list_unref (attrs
);
3913 gtk_widget_set_halign (label
, GTK_ALIGN_START
);
3914 gtk_box_pack_start (GTK_BOX (box
), label
, FALSE
, TRUE
, 0);
3917 label
= gtk_label_new (text
);
3919 gtk_widget_set_halign (label
, GTK_ALIGN_START
);
3920 gtk_box_pack_start (GTK_BOX (box
), label
, FALSE
, TRUE
, 0);
3922 gtk_box_set_spacing (GTK_BOX (box
), 10);
3924 gnm_convert_to_tooltip (GTK_WIDGET (scg
->grid
), box
);
3925 scg
->im
.item
= gtk_widget_get_toplevel (box
);
3927 x
= sheet_col_get_distance_pixels
3928 (sheet
, pane
->first
.col
, pos
->col
+ (rtl
? 1 : 0));
3930 y
= sheet_row_get_distance_pixels
3931 (sheet
, pane
->first
.row
, pos
->row
+ 1);
3933 gtk_widget_get_allocation (GTK_WIDGET (pane
), &allocation
);
3935 x
= allocation
.width
- x
;
3939 gdk_window_get_position
3940 (gtk_widget_get_parent_window (GTK_WIDGET (pane
)),
3941 &x_origin
, &y_origin
);
3945 gtk_window_move (GTK_WINDOW (scg
->im
.item
), x
+ 10, y
+ 10);
3946 gtk_widget_show_all (scg
->im
.item
);
3947 scg
->im
.timer
= g_timeout_add (1500, (GSourceFunc
)cb_cell_im_timer
, scg
);
3953 scg_class_init (GObjectClass
*object_class
)
3955 SheetControlClass
*sc_class
= SHEET_CONTROL_CLASS (object_class
);
3957 g_return_if_fail (sc_class
!= NULL
);
3959 scg_parent_class
= g_type_class_peek_parent (object_class
);
3961 object_class
->finalize
= scg_finalize
;
3963 sc_class
->resize
= scg_resize_virt
;
3964 sc_class
->redraw_all
= scg_redraw_all
;
3965 sc_class
->redraw_range
= scg_redraw_range
;
3966 sc_class
->redraw_headers
= scg_redraw_headers
;
3967 sc_class
->ant
= scg_ant
;
3968 sc_class
->unant
= scg_unant
;
3969 sc_class
->scrollbar_config
= scg_scrollbar_config
;
3970 sc_class
->mode_edit
= scg_mode_edit_virt
;
3971 sc_class
->set_top_left
= scg_set_top_left
;
3972 sc_class
->recompute_visible_region
= scg_recompute_visible_region
;
3973 sc_class
->make_cell_visible
= scg_make_cell_visible_virt
;
3974 sc_class
->cursor_bound
= scg_cursor_bound
;
3975 sc_class
->set_panes
= scg_set_panes
;
3976 sc_class
->object_create_view
= scg_object_create_view
;
3977 sc_class
->scale_changed
= scg_scale_changed
;
3978 sc_class
->show_im_tooltip
= scg_show_im_tooltip
;
3981 GSF_CLASS (SheetControlGUI
, sheet_control_gui
,
3982 scg_class_init
, scg_init
, GNM_SC_TYPE
)
3985 cb_scg_queued_movement (SheetControlGUI
*scg
)
3987 Sheet
const *sheet
= scg_sheet (scg
);
3988 scg
->delayedMovement
.timer
= 0;
3989 (*scg
->delayedMovement
.handler
) (scg
,
3990 scg
->delayedMovement
.n
, FALSE
,
3991 scg
->delayedMovement
.horiz
);
3992 if (wbcg_is_editing (scg
->wbcg
))
3993 sheet_update_only_grid (sheet
);
3995 sheet_update (sheet
);
4000 * scg_queue_movement:
4002 * @handler: (scope async): The movement handler
4004 * @jump: TRUE jump to bound
4005 * @horiz: TRUE move by cols
4007 * Do motion compression when possible to avoid redrawing an area that will
4008 * disappear when we scroll again.
4011 scg_queue_movement (SheetControlGUI
*scg
,
4012 SCGUIMoveFunc handler
,
4013 int n
, gboolean jump
, gboolean horiz
)
4015 g_return_if_fail (GNM_IS_SCG (scg
));
4017 /* do we need to flush a pending movement */
4018 if (scg
->delayedMovement
.timer
!= 0) {
4020 /* do not skip more than 3 requests at a time */
4021 scg
->delayedMovement
.counter
> 3 ||
4022 scg
->delayedMovement
.handler
!= handler
||
4023 scg
->delayedMovement
.horiz
!= horiz
) {
4024 g_source_remove (scg
->delayedMovement
.timer
);
4025 (*scg
->delayedMovement
.handler
) (scg
,
4026 scg
->delayedMovement
.n
, FALSE
,
4027 scg
->delayedMovement
.horiz
);
4028 scg
->delayedMovement
.handler
= NULL
;
4029 scg
->delayedMovement
.timer
= 0;
4031 scg
->delayedMovement
.counter
++;
4032 scg
->delayedMovement
.n
+= n
;
4037 /* jumps are always immediate */
4039 Sheet
const *sheet
= scg_sheet (scg
);
4040 (*handler
) (scg
, n
, TRUE
, horiz
);
4041 if (wbcg_is_editing (scg
->wbcg
))
4042 sheet_update_only_grid (sheet
);
4044 sheet_update (sheet
);
4048 scg
->delayedMovement
.counter
= 1;
4049 scg
->delayedMovement
.handler
= handler
;
4050 scg
->delayedMovement
.horiz
= horiz
;
4051 scg
->delayedMovement
.n
= n
;
4052 scg
->delayedMovement
.timer
= g_timeout_add (10,
4053 (GSourceFunc
)cb_scg_queued_movement
, scg
);
4057 scg_image_create (SheetControlGUI
*scg
, SheetObjectAnchor
*anchor
,
4058 guint8
const *data
, unsigned len
)
4060 SheetObjectImage
*soi
;
4064 /* ensure that we are not editing anything else */
4065 scg_mode_edit (scg
);
4067 soi
= g_object_new (GNM_SO_IMAGE_TYPE
, NULL
);
4068 sheet_object_image_set_image (soi
, "", data
, len
);
4071 sheet_object_set_anchor (so
, anchor
);
4072 sheet_object_set_sheet (so
, scg_sheet (scg
));
4073 scg_object_select (scg
, so
);
4074 sheet_object_default_size (so
, &w
, &h
);
4075 scg_objects_drag (scg
, NULL
, NULL
, &w
, &h
, 7, FALSE
, FALSE
, FALSE
);
4076 scg_objects_drag_commit (scg
, 7, TRUE
, NULL
, NULL
, NULL
);
4080 scg_paste_image (SheetControlGUI
*scg
, GnmRange
*where
,
4081 guint8
const *data
, unsigned len
)
4083 SheetObjectAnchor anchor
;
4085 sheet_object_anchor_init (&anchor
, where
, NULL
,
4086 GOD_ANCHOR_DIR_DOWN_RIGHT
, GNM_SO_ANCHOR_TWO_CELLS
);
4087 scg_image_create (scg
, &anchor
, data
, len
);
4091 scg_drag_receive_img_data (SheetControlGUI
*scg
, double x
, double y
,
4092 guint8
const *data
, unsigned len
)
4095 SheetObjectAnchor anchor
;
4097 sheet_object_anchor_init (&anchor
, NULL
, NULL
,
4098 GOD_ANCHOR_DIR_DOWN_RIGHT
, GNM_SO_ANCHOR_TWO_CELLS
);
4099 coords
[0] = coords
[2] = x
;
4100 coords
[1] = coords
[3] = y
;
4101 scg_object_coords_to_anchor (scg
, coords
, &anchor
);
4102 scg_image_create (scg
, &anchor
, data
, len
);
4106 scg_drag_receive_img_uri (SheetControlGUI
*scg
, double x
, double y
, const gchar
*uri
)
4109 GsfInput
*input
= go_file_open (uri
, &err
);
4110 GOIOContext
*ioc
= go_io_context_new (GO_CMD_CONTEXT (scg
->wbcg
));
4112 if (input
!= NULL
) {
4113 unsigned len
= gsf_input_size (input
);
4114 guint8
const *data
= gsf_input_read (input
, len
, NULL
);
4116 scg_drag_receive_img_data (scg
, x
, y
, data
, len
);
4117 g_object_unref (input
);
4119 go_cmd_context_error (GO_CMD_CONTEXT (ioc
), err
);
4121 if (go_io_error_occurred (ioc
) ||
4122 go_io_warning_occurred (ioc
)) {
4123 go_io_error_display (ioc
);
4124 go_io_error_clear (ioc
);
4126 g_object_unref (ioc
);
4130 scg_drag_receive_spreadsheet (SheetControlGUI
*scg
, const gchar
*uri
)
4133 GsfInput
*input
= go_file_open (uri
, &err
);
4134 GOIOContext
*ioc
= go_io_context_new (GO_CMD_CONTEXT (scg
->wbcg
));
4136 if (input
!= NULL
) {
4139 wbv
= workbook_view_new_from_input (input
, uri
, NULL
, ioc
, NULL
);
4141 gui_wb_view_show (scg
->wbcg
,
4145 go_cmd_context_error (GO_CMD_CONTEXT (ioc
), err
);
4147 if (go_io_error_occurred (ioc
) ||
4148 go_io_warning_occurred (ioc
)) {
4149 go_io_error_display (ioc
);
4150 go_io_error_clear (ioc
);
4152 g_object_unref (ioc
);
4156 scg_paste_cellregion (SheetControlGUI
*scg
, double x
, double y
,
4157 GnmCellRegion
*content
)
4159 WorkbookControl
*wbc
= scg_wbc (scg
);
4160 Sheet
*sheet
= scg_sheet (scg
) ;
4162 SheetObjectAnchor anchor
;
4165 sheet_object_anchor_init (&anchor
, NULL
, NULL
,
4166 GOD_ANCHOR_DIR_DOWN_RIGHT
, GNM_SO_ANCHOR_TWO_CELLS
);
4167 coords
[0] = coords
[2] = x
;
4168 coords
[1] = coords
[3] = y
;
4169 scg_object_coords_to_anchor (scg
, coords
, &anchor
);
4170 paste_target_init (&pt
, sheet
, &anchor
.cell_bound
, PASTE_ALL_SHEET
);
4171 if (content
&& ((content
->cols
> 0 && content
->rows
> 0) ||
4172 content
->objects
!= NULL
))
4173 cmd_paste_copy (wbc
, &pt
, content
);
4177 scg_drag_receive_cellregion (SheetControlGUI
*scg
, double x
, double y
,
4178 const char *data
, unsigned len
)
4180 GnmCellRegion
*content
;
4181 GOIOContext
*io_context
=
4182 go_io_context_new (GO_CMD_CONTEXT (scg
->wbcg
));
4184 content
= gnm_xml_cellregion_read (scg_wbc (scg
), io_context
,
4185 scg_sheet (scg
), data
, len
);
4186 g_object_unref (io_context
);
4187 if (content
!= NULL
) {
4188 scg_paste_cellregion (scg
, x
, y
, content
);
4189 cellregion_unref (content
);
4194 scg_drag_receive_uri_list (SheetControlGUI
*scg
, double x
, double y
,
4195 const char *data
, unsigned len
)
4197 char *cdata
= g_strndup (data
, len
);
4198 GSList
*urls
= go_file_split_urls (cdata
);
4202 for (l
= urls
; l
; l
= l
-> next
) {
4203 char const *uri_str
= l
->data
;
4204 gchar
*mime
= go_get_mime_type (uri_str
);
4205 /* Note that we have imperfect detection of mime-type with some
4206 * platforms, e.g. Win32. In the worst case if
4207 * go_get_mime_type() doesn't return "application/x-gnumeric"
4208 * (registry corruption?) it will give "text/plain" and a
4209 * spreadsheet file is assumed. */
4213 if (!strncmp (mime
, "image/", 6))
4214 scg_drag_receive_img_uri (scg
, x
, y
, uri_str
);
4215 else if (!strcmp (mime
, "application/x-gnumeric") ||
4216 !strcmp (mime
, "application/vnd.ms-excel") ||
4217 !strcmp (mime
, "application/vnd.sun.xml.calc") ||
4218 !strcmp (mime
, "application/vnd.oasis.opendocument.spreadsheet") ||
4219 !strcmp (mime
, "application/vnd.lotus-1-2-3") ||
4220 !strcmp (mime
, "application/x-applix-spreadsheet") ||
4221 !strcmp (mime
, "application/x-dbase") ||
4222 !strcmp (mime
, "application/x-oleo") ||
4223 !strcmp (mime
, "application/x-quattropro") ||
4224 !strcmp (mime
, "application/x-sc") ||
4225 /* !strcmp (mime, "application/xhtml+xml") || */
4226 !strcmp (mime
, "text/spreadsheet") ||
4227 !strcmp (mime
, "text/tab-separated-values") ||
4228 !strcmp (mime
, "text/x-comma-separated-values") ||
4229 !strcmp (mime
, "text/html") ||
4230 !strcmp (mime
, "text/plain")) {
4231 scg_drag_receive_spreadsheet (scg
, uri_str
);
4233 g_printerr ("Received URI %s with mime type %s.\n", uri_str
, mime
);
4234 g_printerr ("I have no idea what to do with that.\n");
4238 g_slist_free_full (urls
, (GDestroyNotify
) g_free
);
4242 scg_drag_receive_same_process (SheetControlGUI
*scg
, GtkWidget
*source_widget
,
4245 SheetControlGUI
*source_scg
= NULL
;
4248 g_return_if_fail (source_widget
!= NULL
);
4249 g_return_if_fail (GNM_IS_PANE (source_widget
));
4251 pane
= GNM_PANE (source_widget
);
4252 x
*= goc_canvas_get_pixels_per_unit (GOC_CANVAS (pane
));
4253 y
*= goc_canvas_get_pixels_per_unit (GOC_CANVAS (pane
));
4254 source_scg
= pane
->simple
.scg
;
4255 if (source_scg
== scg
) {
4257 GdkModifierType mask
;
4258 gint64 xx
= x
, yy
= y
;
4259 gint64 origin_x
= 0, origin_y
= 0;
4261 GOUndo
*undo
= NULL
;
4262 GOUndo
*redo
= NULL
;
4263 gchar
*title
= NULL
;
4265 window
= gtk_widget_get_parent_window (GTK_WIDGET (pane
));
4266 gdk_window_get_device_position (window
,
4267 gdk_device_manager_get_client_pointer (gdk_display_get_device_manager (gdk_window_get_display (window
))),
4270 make_dup
= ((mask
& GDK_CONTROL_MASK
) != 0);
4272 /* When copying objects, we have to create a copy of current selection.
4273 * Since new objects are on top of canvas, we have to move current selection
4274 * back to original position, create a copy of selected objects, make them
4275 * the current selection, then move these objects to drop location. */
4278 xx
= origin_x
= pane
->drag
.origin_x
;
4279 yy
= origin_y
= pane
->drag
.origin_y
;
4282 gnm_pane_objects_drag (pane
, NULL
, xx
, yy
, 8, FALSE
,
4283 (mask
& GDK_SHIFT_MASK
) != 0);
4284 pane
->drag
.origin_x
= pane
->drag
.last_x
;
4285 pane
->drag
.origin_y
= pane
->drag
.last_y
;
4288 GSList
*ptr
, *objs
= go_hash_keys (scg
->selected_objects
);
4289 GOUndo
*nudge_undo
= NULL
;
4290 GOUndo
*nudge_redo
= NULL
;
4293 for (ptr
= objs
; ptr
!= NULL
; ptr
= ptr
->next
) {
4294 SheetObject
*dup_obj
= sheet_object_dup (ptr
->data
);
4295 if (dup_obj
!= NULL
) {
4296 sheet_object_set_sheet (dup_obj
, scg_sheet (scg
));
4297 scg_object_select (scg
, dup_obj
);
4298 g_object_unref (dup_obj
);
4299 scg_object_unselect (scg
, ptr
->data
);
4302 g_slist_free (objs
);
4303 scg_objects_drag_commit (scg
, 8, TRUE
, &undo
, &redo
, &title
);
4306 scg_objects_drag (scg
, pane
, NULL
, &dx
, &dy
, 8, FALSE
, FALSE
, FALSE
);
4307 scg_objects_drag_commit (scg
, 8, FALSE
, &nudge_undo
, &nudge_redo
, NULL
);
4308 undo
= go_undo_combine (undo
, nudge_undo
);
4309 redo
= go_undo_combine (nudge_redo
, redo
);
4311 scg_objects_drag_commit (scg
, 8, FALSE
, &undo
, &redo
, &title
);
4312 cmd_generic (GNM_WBC (scg_wbcg (scg
)), title
, undo
, redo
);
4315 GnmCellRegion
*content
;
4318 g_return_if_fail (GNM_IS_SCG (source_scg
));
4320 objects
= go_hash_keys (source_scg
->selected_objects
);
4321 content
= clipboard_copy_obj (scg_sheet (source_scg
),
4323 if (content
!= NULL
) {
4324 scg_paste_cellregion (scg
, x
, y
, content
);
4325 cellregion_unref (content
);
4327 g_slist_free (objects
);
4331 /* Keep in sync with gtk_selection_data_targets_include_text() */
4333 is_text_target (gchar
*target_type
)
4335 const gchar
*charset
;
4336 gchar
*text_plain_locale
;
4339 g_get_charset (&charset
);
4340 text_plain_locale
= g_strdup_printf ("text/plain;charset=%s", charset
);
4341 ret
= !strcmp (target_type
, "UTF8_STRING") ||
4342 !strcmp (target_type
, "COMPOUND_TEXT") ||
4343 !strcmp (target_type
, "TEXT") ||
4344 !strcmp (target_type
, "STRING") ||
4345 !strcmp (target_type
, "text/plain;charset=utf-8") ||
4346 !strcmp (target_type
, text_plain_locale
) ||
4347 !strcmp (target_type
, "text/plain");
4348 g_free (text_plain_locale
);
4353 scg_drag_data_received (SheetControlGUI
*scg
, GtkWidget
*source_widget
,
4354 double x
, double y
, GtkSelectionData
*selection_data
)
4356 gchar
*target_type
= gdk_atom_name (gtk_selection_data_get_target (selection_data
));
4357 const char *sel_data
= (const char *)gtk_selection_data_get_data (selection_data
);
4358 gsize sel_len
= gtk_selection_data_get_length (selection_data
);
4360 if (!strcmp (target_type
, "text/uri-list")) {
4361 scg_drag_receive_uri_list (scg
, x
, y
, sel_data
, sel_len
);
4363 } else if (!strncmp (target_type
, "image/", 6)) {
4364 scg_drag_receive_img_data (scg
, x
, y
, sel_data
, sel_len
);
4365 } else if (!strcmp (target_type
, "GNUMERIC_SAME_PROC")) {
4366 scg_drag_receive_same_process (scg
, source_widget
, x
, y
);
4367 } else if (!strcmp (target_type
, "application/x-gnumeric")) {
4368 scg_drag_receive_cellregion (scg
, x
, y
, sel_data
, sel_len
);
4370 g_warning ("Unknown target type '%s'!", target_type
);
4372 if (gnm_debug_flag ("dnd")) {
4373 if (!strcmp (target_type
, "x-special/gnome-copied-files")) {
4374 char *cdata
= g_strndup (sel_data
, sel_len
);
4375 g_print ("data length: %d, data: %s\n",
4376 (int)sel_len
, cdata
);
4378 } else if (!strcmp (target_type
, "_NETSCAPE_URL")) {
4379 char *cdata
= g_strndup (sel_data
, sel_len
);
4380 g_print ("data length: %d, data: %s\n",
4381 (int)sel_len
, cdata
);
4383 } else if (is_text_target (target_type
)) {
4384 char *cdata
= g_strndup (sel_data
, sel_len
);
4385 g_print ("data length: %d, data: %s\n",
4386 (int)sel_len
, cdata
);
4388 } else if (!strcmp (target_type
, "text/html")) {
4389 char *cdata
= g_strndup (sel_data
, sel_len
);
4390 /* For mozilla, need to convert the encoding */
4391 g_print ("data length: %d, data: %s\n", (int)sel_len
, cdata
);
4396 g_free (target_type
);
4400 scg_drag_send_image (G_GNUC_UNUSED SheetControlGUI
*scg
,
4401 GtkSelectionData
*selection_data
,
4403 gchar
const *mime_type
)
4405 SheetObject
*so
= NULL
;
4407 GsfOutputMemory
*omem
;
4412 for (ptr
= objects
; ptr
!= NULL
; ptr
= ptr
->next
) {
4413 if (GNM_IS_SO_IMAGEABLE (GNM_SO (ptr
->data
))) {
4414 so
= GNM_SO (ptr
->data
);
4419 g_warning ("non imageable object requested as image\n");
4423 format
= go_mime_to_image_format (mime_type
);
4425 g_warning ("No image format for %s\n", mime_type
);
4430 output
= gsf_output_memory_new ();
4431 omem
= GSF_OUTPUT_MEMORY (output
);
4432 sheet_object_write_image (so
, format
, -1.0, output
, NULL
);
4433 osize
= gsf_output_size (output
);
4435 gtk_selection_data_set
4437 gtk_selection_data_get_target (selection_data
),
4438 8, gsf_output_memory_get_bytes (omem
), osize
);
4439 gsf_output_close (output
);
4440 g_object_unref (output
);
4445 scg_drag_send_graph (G_GNUC_UNUSED SheetControlGUI
*scg
,
4446 GtkSelectionData
*selection_data
,
4448 gchar
const *mime_type
)
4450 SheetObject
*so
= NULL
;
4452 GsfOutputMemory
*omem
;
4456 for (ptr
= objects
; ptr
!= NULL
; ptr
= ptr
->next
)
4457 if (GNM_IS_SO_EXPORTABLE (GNM_SO (ptr
->data
))) {
4458 so
= GNM_SO (ptr
->data
);
4463 g_warning ("non exportable object requested\n");
4467 output
= gsf_output_memory_new ();
4468 omem
= GSF_OUTPUT_MEMORY (output
);
4469 sheet_object_write_object (so
, mime_type
, output
, NULL
,
4470 gnm_conventions_default
);
4471 osize
= gsf_output_size (output
);
4473 gtk_selection_data_set
4475 gtk_selection_data_get_target (selection_data
),
4476 8, gsf_output_memory_get_bytes (omem
), osize
);
4477 gsf_output_close (output
);
4478 g_object_unref (output
);
4482 scg_drag_send_clipboard_objects (SheetControl
*sc
,
4483 GtkSelectionData
*selection_data
,
4486 GnmCellRegion
*content
= clipboard_copy_obj (sc_sheet (sc
), objects
);
4487 GsfOutputMemory
*output
;
4489 if (content
== NULL
)
4492 output
= gnm_cellregion_to_xml (content
);
4493 gtk_selection_data_set
4495 gtk_selection_data_get_target (selection_data
),
4497 gsf_output_memory_get_bytes (output
),
4498 gsf_output_size (GSF_OUTPUT (output
)));
4499 g_object_unref (output
);
4500 cellregion_unref (content
);
4504 scg_drag_send_text (SheetControlGUI
*scg
, GtkSelectionData
*sd
)
4506 Sheet
*sheet
= scg_sheet (scg
);
4507 Workbook
*wb
= sheet
->workbook
;
4508 GnmRange range
= sheet_get_extent (sheet
, TRUE
, TRUE
);
4509 GnmCellRegion
*reg
= clipboard_copy_range (sheet
, &range
);
4510 GString
*s
= cellregion_to_string (reg
, TRUE
, workbook_date_conv (wb
));
4512 cellregion_unref (reg
);
4515 gtk_selection_data_set (sd
, gtk_selection_data_get_target (sd
),
4517 g_string_free (s
, TRUE
);
4521 scg_drag_data_get (SheetControlGUI
*scg
, GtkSelectionData
*selection_data
)
4523 GdkAtom target
= gtk_selection_data_get_target (selection_data
);
4524 gchar
*target_name
= gdk_atom_name (target
);
4525 GSList
*objects
= scg
->selected_objects
4526 ? go_hash_keys (scg
->selected_objects
)
4529 if (strcmp (target_name
, "GNUMERIC_SAME_PROC") == 0)
4530 /* Set dummy selection for process internal dnd */
4531 gtk_selection_data_set (selection_data
, target
,
4532 8, (const guint8
*)"", 1);
4533 else if (strcmp (target_name
, "GNUMERIC_SHEET") == 0)
4534 gtk_selection_data_set (selection_data
, target
,
4535 8, (void *)scg
, sizeof (scg
));
4536 else if (strcmp (target_name
, "application/x-gnumeric") == 0)
4537 scg_drag_send_clipboard_objects (GNM_SC (scg
),
4538 selection_data
, objects
);
4539 else if (strcmp (target_name
, "application/x-goffice-graph") == 0)
4540 scg_drag_send_graph (scg
, selection_data
, objects
, target_name
);
4541 else if (strncmp (target_name
, "image/", 6) == 0)
4542 scg_drag_send_image (scg
, selection_data
, objects
, target_name
);
4543 else if (strcmp (target_name
, "UTF8_STRING") == 0)
4544 scg_drag_send_text (scg
, selection_data
);
4546 g_free (target_name
);
4547 g_slist_free (objects
);
4551 scg_delete_sheet_if_possible (SheetControlGUI
*scg
)
4553 SheetControl
*sc
= (SheetControl
*) scg
;
4554 Sheet
*sheet
= scg_sheet (scg
);
4555 Workbook
*wb
= sheet
->workbook
;
4557 /* If this is the last sheet left, ignore the request */
4558 if (workbook_sheet_count (wb
) != 1) {
4559 WorkbookSheetState
*old_state
= workbook_sheet_state_new (wb
);
4560 WorkbookControl
*wbc
= sc
->wbc
;
4561 workbook_sheet_delete (sheet
);
4562 /* Careful: sc just ceased to be valid. */
4563 cmd_reorganize_sheets (wbc
, old_state
, sheet
);
4568 scg_reload_item_edits (SheetControlGUI
*scg
)
4570 SCG_FOREACH_PANE (scg
, pane
, {
4571 if (pane
->editor
!= NULL
)
4572 goc_item_bounds_changed
4573 (GOC_ITEM (pane
->editor
));