SheetView: try not to crash every time we make one.
[gnumeric.git] / src / sheet-view.c
blob0ebdbea33427626129ed557520f27d2513fb74cc
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * sheet-view.c:
5 * Copyright (C) 2002-2006 Jody Goldberg (jody@gnome.org)
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) version 3.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
20 * USA
23 #include <gnumeric-config.h>
24 #include <glib/gi18n-lib.h>
25 #include <stdlib.h>
26 #include "gnumeric.h"
28 #include "sheet-view.h"
29 #include "sheet.h"
30 #include "sheet-merge.h"
31 #include "sheet-filter.h"
32 #include "gnm-sheet-slicer.h"
33 #include "sheet-private.h"
34 #include "sheet-control.h"
35 #include "sheet-control-priv.h"
36 #include "workbook-view.h"
37 #include "workbook-control.h"
38 #include "ranges.h"
39 #include "selection.h"
40 #include "application.h"
41 #include "value.h"
42 #include "parse-util.h"
43 #include "expr-name.h"
44 #include "command-context.h"
45 #include "gnumeric-conf.h"
46 #include "sheet-style.h"
47 #include "mstyle.h"
48 #include "gutils.h"
50 #include <gsf/gsf-impl-utils.h>
52 #define GNM_SHEET_VIEW_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GNM_SHEET_VIEW_TYPE, SheetViewClass))
53 static GObjectClass *parent_class;
55 /*************************************************************************/
57 static void
58 auto_expr_timer_clear (SheetView *sv)
60 if (sv->auto_expr_timer != 0) {
61 g_source_remove (sv->auto_expr_timer);
62 sv->auto_expr_timer = 0;
66 static gboolean
67 cb_update_auto_expr (gpointer data)
69 SheetView *sv = (SheetView *) data;
71 if (wb_view_cur_sheet_view (sv->sv_wbv) == sv)
72 wb_view_auto_expr_recalc (sv->sv_wbv);
74 sv->auto_expr_timer = 0;
75 return FALSE;
78 /*************************************************************************/
80 static void
81 sv_sheet_name_changed (G_GNUC_UNUSED Sheet *sheet,
82 G_GNUC_UNUSED GParamSpec *pspec,
83 SheetView *sv)
85 g_return_if_fail (GNM_IS_SHEET_VIEW (sv));
86 sv->edit_pos_changed.content = TRUE;
89 static void
90 sv_sheet_visibility_changed (Sheet *sheet,
91 G_GNUC_UNUSED GParamSpec *pspec,
92 SheetView *sv)
94 g_return_if_fail (GNM_IS_SHEET_VIEW (sv));
95 /* See bug 366477. */
96 if (sheet_is_visible (sheet) && !wb_view_cur_sheet (sv->sv_wbv))
97 wb_view_sheet_focus (sv->sv_wbv, sheet);
100 static void
101 sv_sheet_r1c1_changed (G_GNUC_UNUSED Sheet *sheet,
102 G_GNUC_UNUSED GParamSpec *pspec,
103 SheetView *sv)
105 g_return_if_fail (GNM_IS_SHEET_VIEW (sv));
106 sv->edit_pos_changed.location = TRUE;
110 * sv_sheet:
111 * @sv: #SheetView
113 * Returns: (transfer none): the sheet.
115 Sheet *
116 sv_sheet (SheetView const *sv)
118 g_return_val_if_fail (GNM_IS_SHEET_VIEW (sv), NULL);
119 return sv->sheet;
123 * sv_wbv:
124 * @sv: #SheetView
126 * Returns: (transfer none): the workbook view.
128 WorkbookView *
129 sv_wbv (SheetView const *sv)
131 g_return_val_if_fail (GNM_IS_SHEET_VIEW (sv), NULL);
132 return sv->sv_wbv;
135 static void
136 sv_init_sc (SheetView const *sv, SheetControl *sc)
138 GnmCellPos initial;
140 sc_scale_changed (sc);
142 /* set_panes will change the initial so cache it */
143 initial = sv->initial_top_left;
144 sc_set_panes (sc);
146 /* And this will restore it */
147 sc_set_top_left (sc, initial.col, initial.row);
148 sc_scrollbar_config (sc);
150 /* Set the visible bound, not the logical bound */
151 sc_cursor_bound (sc, selection_first_range (sv, NULL, NULL));
152 sc_ant (sc);
155 void
156 gnm_sheet_view_attach_control (SheetView *sv, SheetControl *sc)
158 g_return_if_fail (GNM_IS_SHEET_VIEW (sv));
159 g_return_if_fail (GNM_IS_SHEET_CONTROL (sc));
160 g_return_if_fail (sc->view == NULL);
162 g_ptr_array_add (sv->controls, sc);
163 sc->view = sv;
164 sv_init_sc (sv, sc);
167 void
168 gnm_sheet_view_detach_control (SheetView *sv, SheetControl *sc)
170 g_return_if_fail (GNM_IS_SHEET_VIEW (sv));
171 g_return_if_fail (GNM_IS_SHEET_CONTROL (sc));
172 g_return_if_fail (sv == sc->view);
174 g_ptr_array_remove (sv->controls, sc);
175 sc->view = NULL;
178 static void
179 sv_weakref_notify (SheetView **ptr, GObject *sv)
181 g_return_if_fail (ptr != NULL);
182 g_return_if_fail (*ptr == (SheetView *)sv); /* remember sv is dead */
183 *ptr = NULL;
186 void
187 gnm_sheet_view_weak_ref (SheetView *sv, SheetView **ptr)
189 g_return_if_fail (ptr != NULL);
191 *ptr = sv;
192 if (sv != NULL)
193 g_object_weak_ref (G_OBJECT (sv),
194 (GWeakNotify) sv_weakref_notify,
195 ptr);
198 void
199 gnm_sheet_view_weak_unref (SheetView **ptr)
201 g_return_if_fail (ptr != NULL);
203 if (*ptr != NULL) {
204 g_object_weak_unref (G_OBJECT (*ptr),
205 (GWeakNotify) sv_weakref_notify,
206 ptr);
207 *ptr = NULL;
211 static void
212 sv_finalize (GObject *object)
214 SheetView *sv = GNM_SHEET_VIEW (object);
215 g_ptr_array_free (sv->controls, TRUE);
216 parent_class->finalize (object);
219 static void
220 sv_real_dispose (GObject *object)
222 SheetView *sv = GNM_SHEET_VIEW (object);
224 while (sv->controls->len > 0) {
225 SheetControl *control =
226 g_ptr_array_index (sv->controls,
227 sv->controls->len - 10);
228 gnm_sheet_view_detach_control (sv, control);
229 g_object_unref (control);
232 if (sv->sheet) {
233 Sheet *sheet = sv->sheet;
234 sv->sheet = NULL;
235 g_ptr_array_remove (sheet->sheet_views, sv);
236 g_signal_handlers_disconnect_by_func (sheet, sv_sheet_name_changed, sv);
237 g_signal_handlers_disconnect_by_func (sheet, sv_sheet_visibility_changed, sv);
238 g_signal_handlers_disconnect_by_func (sheet, sv_sheet_r1c1_changed, sv);
239 g_object_unref (sv);
240 g_object_unref (sheet);
243 gnm_sheet_view_unant (sv);
244 sv_selection_free (sv);
245 sv_selection_simplified_free (sv);
246 auto_expr_timer_clear (sv);
248 parent_class->dispose (object);
251 static void
252 gnm_sheet_view_class_init (GObjectClass *klass)
254 SheetViewClass *wbc_class = GNM_SHEET_VIEW_CLASS (klass);
256 g_return_if_fail (wbc_class != NULL);
258 parent_class = g_type_class_peek_parent (klass);
259 klass->dispose = sv_real_dispose;
260 klass->finalize = sv_finalize;
263 static void
264 gnm_sheet_view_init (GObject *object)
266 SheetView *sv = GNM_SHEET_VIEW (object);
268 sv->controls = g_ptr_array_new ();
270 /* Init menu states */
271 sv->enable_insert_rows = TRUE;
272 sv->enable_insert_cols = TRUE;
273 sv->enable_insert_cells = TRUE;
275 sv->edit_pos_changed.location = TRUE;
276 sv->edit_pos_changed.content = TRUE;
277 sv->edit_pos_changed.style = TRUE;
278 sv->selection_content_changed = TRUE;
279 sv->reposition_selection = TRUE;
280 sv->auto_expr_timer = 0;
282 sv->frozen_top_left.col = sv->frozen_top_left.row =
283 sv->unfrozen_top_left.col = sv->unfrozen_top_left.row = -1;
284 sv->initial_top_left.col = sv->initial_top_left.row = 0;
286 sv->selections = NULL;
287 sv->selection_mode = GNM_SELECTION_MODE_ADD;
288 sv->selections_simplified = NULL;
289 sv_selection_add_pos (sv, 0, 0, GNM_SELECTION_MODE_ADD);
292 GSF_CLASS (SheetView, gnm_sheet_view,
293 gnm_sheet_view_class_init, gnm_sheet_view_init,
294 G_TYPE_OBJECT)
296 SheetView *
297 gnm_sheet_view_new (Sheet *sheet, WorkbookView *wbv)
299 SheetView *sv;
301 g_return_val_if_fail (IS_SHEET (sheet), NULL);
303 sv = g_object_new (GNM_SHEET_VIEW_TYPE, NULL);
304 sv->sheet = g_object_ref (sheet);
305 sv->sv_wbv = wbv;
306 g_ptr_array_add (sheet->sheet_views, sv);
307 g_object_ref (sv);
309 g_signal_connect (G_OBJECT (sheet),
310 "notify::name",
311 G_CALLBACK (sv_sheet_name_changed),
312 sv);
314 g_signal_connect (G_OBJECT (sheet),
315 "notify::visibility",
316 G_CALLBACK (sv_sheet_visibility_changed),
317 sv);
319 g_signal_connect (G_OBJECT (sheet),
320 "notify::use-r1c1",
321 G_CALLBACK (sv_sheet_r1c1_changed),
322 sv);
324 SHEET_VIEW_FOREACH_CONTROL (sv, control,
325 sv_init_sc (sv, control););
326 return sv;
329 void
330 gnm_sheet_view_dispose (SheetView *sv)
332 g_object_run_dispose (G_OBJECT (sv));
335 void
336 gnm_sheet_view_unant (SheetView *sv)
338 GList *ptr;
340 g_return_if_fail (GNM_IS_SHEET_VIEW (sv));
342 if (sv->ants == NULL)
343 return;
344 for (ptr = sv->ants; ptr != NULL; ptr = ptr->next)
345 g_free (ptr->data);
346 g_list_free (sv->ants);
347 sv->ants = NULL;
349 SHEET_VIEW_FOREACH_CONTROL (sv, control,
350 sc_unant (control););
354 * gnm_sheet_view_ant:
355 * @sv:
356 * @ranges: (element-type GnmRange) (transfer none): The ranges to ant.
358 void
359 gnm_sheet_view_ant (SheetView *sv, GList *ranges)
361 GList *ptr;
363 g_return_if_fail (GNM_IS_SHEET_VIEW (sv));
364 g_return_if_fail (ranges != NULL);
366 if (sv->ants != NULL)
367 gnm_sheet_view_unant (sv);
368 for (ptr = ranges; ptr != NULL; ptr = ptr->next)
369 sv->ants = g_list_prepend (sv->ants, gnm_range_dup (ptr->data));
370 sv->ants = g_list_reverse (sv->ants);
372 SHEET_VIEW_FOREACH_CONTROL (sv, control,
373 sc_ant (control););
376 void
377 gnm_sheet_view_make_cell_visible (SheetView *sv, int col, int row,
378 gboolean couple_panes)
380 g_return_if_fail (GNM_IS_SHEET_VIEW (sv));
381 SHEET_VIEW_FOREACH_CONTROL(sv, control,
382 sc_make_cell_visible (control, col, row, couple_panes););
385 void
386 gnm_sheet_view_redraw_range (SheetView *sv, GnmRange const *r)
388 g_return_if_fail (GNM_IS_SHEET_VIEW (sv));
390 SHEET_VIEW_FOREACH_CONTROL (sv, sc, sc_redraw_range (sc, r););
393 void
394 gnm_sheet_view_redraw_headers (SheetView const *sv,
395 gboolean col, gboolean row,
396 GnmRange const* r /* optional == NULL */)
398 g_return_if_fail (GNM_IS_SHEET_VIEW (sv));
400 SHEET_VIEW_FOREACH_CONTROL (sv, control,
401 sc_redraw_headers (control, col, row, r););
404 void
405 gnm_sheet_view_resize (SheetView *sv, gboolean force_scroll)
407 g_return_if_fail (GNM_IS_SHEET_VIEW (sv));
409 SHEET_VIEW_FOREACH_CONTROL (sv, control,
410 sc_resize (control, force_scroll););
414 gboolean
415 gnm_sheet_view_selection_copy (SheetView *sv, WorkbookControl *wbc)
417 GnmRange const *sel;
419 g_return_val_if_fail (GNM_IS_SHEET_VIEW (sv), FALSE);
420 if (!(sel = selection_first_range (sv, GO_CMD_CONTEXT (wbc), _("Copy"))))
421 return FALSE;
423 gnm_app_clipboard_cut_copy (wbc, FALSE, sv, sel, TRUE);
425 return TRUE;
428 gboolean
429 gnm_sheet_view_selection_cut (SheetView *sv, WorkbookControl *wbc)
431 GnmRange const *sel;
433 /* 'cut' is a poor description of what we're
434 * doing here. 'move' would be a better
435 * approximation. The key portion of this process is that
436 * the range being moved has all
437 * - references to it adjusted to the new site.
438 * - relative references from it adjusted.
440 * NOTE : This command DOES NOT MOVE ANYTHING !
441 * We only store the src, paste does the move.
443 g_return_val_if_fail (GNM_IS_SHEET_VIEW (sv), FALSE);
445 if (!(sel = selection_first_range (sv, GO_CMD_CONTEXT (wbc), _("Cut"))))
446 return FALSE;
448 if (sheet_range_splits_region (sv_sheet (sv), sel, NULL, GO_CMD_CONTEXT (wbc), _("Cut")))
449 return FALSE;
451 gnm_app_clipboard_cut_copy (wbc, TRUE, sv, sel, TRUE);
453 return TRUE;
457 * gnm_sheet_view_cursor_set:
458 * @sv: The sheet
459 * @edit:
460 * @base_col:
461 * @base_row:
462 * @move_col:
463 * @move_row:
464 * @bound: (nullable): A range that should contain all the supplied points
466 void
467 gnm_sheet_view_cursor_set (SheetView *sv,
468 GnmCellPos const *edit,
469 int base_col, int base_row,
470 int move_col, int move_row,
471 GnmRange const *bound)
473 GnmRange r;
475 g_return_if_fail (GNM_IS_SHEET_VIEW (sv));
477 /* Change the edit position */
478 gnm_sheet_view_set_edit_pos (sv, edit);
480 sv->cursor.base_corner.col = base_col;
481 sv->cursor.base_corner.row = base_row;
482 sv->cursor.move_corner.col = move_col;
483 sv->cursor.move_corner.row = move_row;
485 if (bound == NULL) {
486 if (base_col < move_col) {
487 r.start.col = base_col;
488 r.end.col = move_col;
489 } else {
490 r.end.col = base_col;
491 r.start.col = move_col;
493 if (base_row < move_row) {
494 r.start.row = base_row;
495 r.end.row = move_row;
496 } else {
497 r.end.row = base_row;
498 r.start.row = move_row;
500 bound = &r;
503 g_return_if_fail (range_is_sane (bound));
505 SHEET_VIEW_FOREACH_CONTROL(sv, control,
506 sc_cursor_bound (control, bound););
509 void
510 gnm_sheet_view_set_edit_pos (SheetView *sv, GnmCellPos const *pos)
512 GnmCellPos old;
513 GnmRange const *merged;
515 g_return_if_fail (GNM_IS_SHEET_VIEW (sv));
516 g_return_if_fail (pos != NULL);
518 old = sv->edit_pos;
519 sv->first_tab_col = -1; /* invalidate */
521 if (old.col == pos->col && old.row == pos->row)
522 return;
524 g_return_if_fail (IS_SHEET (sv->sheet));
525 g_return_if_fail (pos->col >= 0);
526 g_return_if_fail (pos->col < gnm_sheet_get_max_cols (sv->sheet));
527 g_return_if_fail (pos->row >= 0);
528 g_return_if_fail (pos->row < gnm_sheet_get_max_rows (sv->sheet));
531 merged = gnm_sheet_merge_is_corner (sv->sheet, &old);
533 sv->edit_pos_changed.location =
534 sv->edit_pos_changed.content =
535 sv->edit_pos_changed.style = TRUE;
537 /* Redraw before change */
538 if (merged == NULL) {
539 GnmRange tmp; tmp.start = tmp.end = old;
540 gnm_sheet_view_redraw_range (sv, &tmp);
541 } else
542 gnm_sheet_view_redraw_range (sv, merged);
544 sv->edit_pos_real = *pos;
546 /* Redraw after change (handling merged cells) */
547 merged = gnm_sheet_merge_contains_pos (sv->sheet, &sv->edit_pos_real);
548 if (merged == NULL) {
549 GnmRange tmp; tmp.start = tmp.end = *pos;
550 gnm_sheet_view_redraw_range (sv, &tmp);
551 sv->edit_pos = sv->edit_pos_real;
552 } else {
553 gnm_sheet_view_redraw_range (sv, merged);
554 sv->edit_pos = merged->start;
559 * gnm_sheet_view_flag_status_update_pos:
560 * @sv:
561 * @pos:
563 * flag the view as requiring an update to the status display
564 * if the supplied cell location is the edit cursor, or part of the
565 * selected region.
567 * Will cause the format toolbar, the edit area, and the auto expressions to be
568 * updated if appropriate.
570 void
571 gnm_sheet_view_flag_status_update_pos (SheetView *sv, GnmCellPos const *pos)
573 g_return_if_fail (GNM_IS_SHEET_VIEW (sv));
574 g_return_if_fail (pos != NULL);
576 /* if a part of the selected region changed value update
577 * the auto expressions
579 if (sv_is_pos_selected (sv, pos->col, pos->row))
580 sv->selection_content_changed = TRUE;
582 /* If the edit cell changes value update the edit area
583 * and the format toolbar
585 if (pos->col == sv->edit_pos.col && pos->row == sv->edit_pos.row)
586 sv->edit_pos_changed.content =
587 sv->edit_pos_changed.style = TRUE;
591 * gnm_sheet_view_flag_status_update_range:
592 * @sv:
593 * @range: (nullable): If %NULL then force an update.
595 * flag the sheet as requiring an update to the status display if the supplied
596 * cell location contains the edit cursor, or intersects of the selected region.
598 * Will cause the format toolbar, the edit area, and the auto expressions to be
599 * updated if appropriate.
601 void
602 gnm_sheet_view_flag_status_update_range (SheetView *sv, GnmRange const *range)
604 g_return_if_fail (GNM_IS_SHEET_VIEW (sv));
606 /* Force an update */
607 if (range == NULL) {
608 sv->selection_content_changed = TRUE;
609 sv->edit_pos_changed.location =
610 sv->edit_pos_changed.content =
611 sv->edit_pos_changed.style = TRUE;
612 return;
615 /* if a part of the selected region changed value update
616 * the auto expressions
618 if (sv_is_range_selected (sv, range))
619 sv->selection_content_changed = TRUE;
621 /* If the edit cell changes value update the edit area
622 * and the format toolbar
624 if (range_contains (range, sv->edit_pos.col, sv->edit_pos.row))
625 sv->edit_pos_changed.content = sv->edit_pos_changed.style = TRUE;
629 * gnm_sheet_view_flag_style_update_range:
630 * @sv: The sheet being changed
631 * @range: the range that is changing.
633 * Flag style changes that will require updating the style indicators.
635 void
636 gnm_sheet_view_flag_style_update_range (SheetView *sv, GnmRange const *range)
638 g_return_if_fail (GNM_IS_SHEET_VIEW (sv));
639 g_return_if_fail (range != NULL);
640 if (range_contains (range, sv->edit_pos.col, sv->edit_pos.row))
641 sv->edit_pos_changed.style = TRUE;
645 * gnm_sheet_view_flag_selection_change:
646 * @sv:
648 * flag the sheet as requiring an update to the status display
650 * Will cause auto expressions to be updated
652 void
653 gnm_sheet_view_flag_selection_change (SheetView *sv)
655 g_return_if_fail (GNM_IS_SHEET_VIEW (sv));
656 sv->selection_content_changed = TRUE;
659 static void
660 sheet_view_edit_pos_tool_tips (SheetView *sv)
662 GnmStyle const *style;
663 GnmInputMsg *im = NULL;
665 style = sheet_style_get (sv->sheet,
666 sv->edit_pos.col,
667 sv->edit_pos.row);
668 if (style != NULL && gnm_style_is_element_set (style, MSTYLE_INPUT_MSG))
669 im = gnm_style_get_input_msg (style);
671 /* We need to call these even with im == NULL to remove the old tooltip.*/
672 SHEET_VIEW_FOREACH_CONTROL (sv, control,
673 sc_show_im_tooltip (control, im, &sv->edit_pos););
676 void
677 gnm_sheet_view_update (SheetView *sv)
679 g_return_if_fail (GNM_IS_SHEET_VIEW (sv));
681 if (sv->edit_pos_changed.content) {
682 sv->edit_pos_changed.content = FALSE;
683 if (wb_view_cur_sheet_view (sv->sv_wbv) == sv)
684 wb_view_edit_line_set (sv->sv_wbv, NULL);
687 if (sv->edit_pos_changed.style ) {
688 sv->edit_pos_changed.style = FALSE;
689 if (wb_view_cur_sheet_view (sv->sv_wbv) == sv)
690 wb_view_style_feedback (sv->sv_wbv);
693 if (sv->edit_pos_changed.location) {
694 sv->edit_pos_changed.location = FALSE;
695 if (wb_view_cur_sheet_view (sv->sv_wbv) == sv) {
696 wb_view_selection_desc (sv->sv_wbv, TRUE, NULL);
697 SHEET_VIEW_FOREACH_CONTROL
698 (sv, sc, wb_control_menu_state_update
699 (sc_wbc (sc),
700 MS_COMMENT_LINKS | MS_PAGE_BREAKS););
701 sheet_view_edit_pos_tool_tips (sv);
705 if (sv->selection_content_changed) {
706 int const lag = gnm_conf_get_core_gui_editing_recalclag ();
707 sv->selection_content_changed = FALSE;
708 if (sv->auto_expr_timer == 0 || lag < 0) {
709 auto_expr_timer_clear (sv);
710 sv->auto_expr_timer = g_timeout_add_full (0, abs (lag), /* seems ok */
711 cb_update_auto_expr, (gpointer) sv, NULL);
713 SHEET_VIEW_FOREACH_CONTROL (sv, sc,
714 wb_control_menu_state_update (sc_wbc (sc), MS_ADD_VS_REMOVE_FILTER |
715 MS_COMMENT_LINKS_RANGE););
718 SHEET_VIEW_FOREACH_CONTROL (sv, sc,
719 wb_control_menu_state_update
720 (sc_wbc (sc), MS_SELECT_OBJECT););
725 * gnm_sheet_view_editpos_in_filter:
726 * @sv: #SheetView
728 * Returns: (nullable): #GnmFilter that overlaps the sv::edit_pos
730 GnmFilter *
731 gnm_sheet_view_editpos_in_filter (SheetView const *sv)
733 g_return_val_if_fail (GNM_IS_SHEET_VIEW (sv), NULL);
734 return gnm_sheet_filter_at_pos (sv->sheet, &sv->edit_pos);
738 * gnm_sheet_view_selection_intersects_filter_rows:
739 * @sv: #SheetView
741 * Returns: (nullable): #GnmFilter whose rows intersect the rows
742 * of the current selection.
744 GnmFilter *
745 gnm_sheet_view_selection_intersects_filter_rows (SheetView const *sv)
747 GnmRange const *r;
748 g_return_val_if_fail (GNM_IS_SHEET_VIEW (sv), NULL);
749 r = selection_first_range (sv, NULL, NULL);
751 return r ? gnm_sheet_filter_intersect_rows
752 (sv->sheet, r->start.row, r->end.row) : NULL;
756 * gnm_sheet_view_selection_extends_filter:
757 * @sv: #SheetView
759 * Returns: (nullable): #GnmFilter whose rows intersect the rows
760 * of the current selection range to which the filter can be
761 * extended.
763 GnmRange *
764 gnm_sheet_view_selection_extends_filter (SheetView const *sv,
765 GnmFilter const *f)
767 GnmRange const *r;
768 g_return_val_if_fail (GNM_IS_SHEET_VIEW (sv), NULL);
769 r = selection_first_range (sv, NULL, NULL);
771 return gnm_sheet_filter_can_be_extended (sv->sheet, f, r);
778 * gnm_sheet_view_editpos_in_slicer:
779 * @sv: #SheetView
781 * Returns: (transfer none) (nullable): #GnmSheetSlicer that overlaps the
782 * sv::edit_pos
784 GnmSheetSlicer *
785 gnm_sheet_view_editpos_in_slicer (SheetView const *sv)
787 g_return_val_if_fail (GNM_IS_SHEET_VIEW (sv), NULL);
788 return gnm_sheet_slicers_at_pos (sv->sheet, &sv->edit_pos);
792 * gnm_sheet_view_freeze_panes:
793 * @sv: the sheet
794 * @frozen_top_left: (nullable): top left corner of the frozen region
795 * @unfrozen_top_left: (nullable): top left corner of the unfrozen region
797 * By definition the unfrozen region must be below the frozen.
798 * If @frozen_top_left == @unfrozen_top_left or @frozen_top_left == NULL unfreeze
800 void
801 gnm_sheet_view_freeze_panes (SheetView *sv,
802 GnmCellPos const *frozen,
803 GnmCellPos const *unfrozen)
805 g_return_if_fail (GNM_IS_SHEET_VIEW (sv));
807 if (gnm_debug_flag ("frozen-panes")) {
808 g_printerr ("Frozen: %-10s",
809 frozen ? cellpos_as_string (frozen) : "-");
810 g_printerr ("Unfrozen: %s\n",
811 unfrozen ? cellpos_as_string (unfrozen) : "-");
814 if (frozen != NULL) {
815 g_return_if_fail (unfrozen != NULL);
816 g_return_if_fail (unfrozen->col >= frozen->col);
817 g_return_if_fail (unfrozen->row >= frozen->row);
819 /* Just in case */
820 if (unfrozen->col != gnm_sheet_get_last_col (sv->sheet) &&
821 unfrozen->row != gnm_sheet_get_last_row (sv->sheet) &&
822 !gnm_cellpos_equal (frozen, unfrozen)) {
823 sv->frozen_top_left = *frozen;
824 sv->unfrozen_top_left = *unfrozen;
825 if (sv->frozen_top_left.col == sv->unfrozen_top_left.col)
826 sv->frozen_top_left.col = sv->unfrozen_top_left.col = 0;
827 if (sv->frozen_top_left.row == sv->unfrozen_top_left.row)
828 sv->frozen_top_left.row = sv->unfrozen_top_left.row = 0;
829 } else
830 frozen = unfrozen = NULL;
833 if (frozen == NULL) {
834 g_return_if_fail (unfrozen == NULL);
836 /* no change */
837 if (sv->frozen_top_left.col < 0 &&
838 sv->frozen_top_left.row < 0 &&
839 sv->unfrozen_top_left.col < 0 &&
840 sv->unfrozen_top_left.row < 0)
841 return;
843 sv->initial_top_left = sv->frozen_top_left;
844 sv->frozen_top_left.col = sv->frozen_top_left.row =
845 sv->unfrozen_top_left.col = sv->unfrozen_top_left.row = -1;
848 SHEET_VIEW_FOREACH_CONTROL (sv, control,
849 sv_init_sc (sv, control););
851 WORKBOOK_VIEW_FOREACH_CONTROL(sv->sv_wbv, wbc,
852 wb_control_menu_state_update (wbc, MS_FREEZE_VS_THAW););
856 * gnm_sheet_view_panes_insdel_colrow:
857 * @sv:
858 * @is_cols:
859 * @is_insert:
860 * @start:
861 * @count:
863 * Adjust the positions of frozen panes as necessary to handle col/row
864 * insertions and deletions. note this assumes that the ins/del operations
865 * have already set the flags that will force a resize.
867 void
868 gnm_sheet_view_panes_insdel_colrow (SheetView *sv, gboolean is_cols,
869 gboolean is_insert, int start, int count)
871 GnmCellPos tl;
872 GnmCellPos br;
874 g_return_if_fail (GNM_IS_SHEET_VIEW (sv));
876 tl = sv->frozen_top_left; /* _copy_ them */
877 br = sv->unfrozen_top_left;
879 if (is_cols) {
880 /* ignore if not frozen, or acting in unfrozen region */
881 if (br.col <= tl.col || br.col <= start)
882 return;
883 if (is_insert) {
884 br.col += count;
885 if (tl.col > start)
886 tl.col += count;
887 if (br.col < tl.col || br.col >= gnm_sheet_get_max_cols (sv->sheet))
888 return;
889 } else {
890 if (tl.col >= start)
891 tl.col -= MIN (count, tl.col - start);
892 br.col -= count;
893 if (br.col <= tl.col)
894 br.col = tl.col + 1;
896 } else {
897 /* ignore if not frozen, or acting in unfrozen region */
898 if (br.row <= tl.row || br.row <= start)
899 return;
900 if (is_insert) {
901 br.row += count;
902 if (tl.row > start)
903 tl.row += count;
904 if (br.row < tl.row || br.row >= gnm_sheet_get_max_rows (sv->sheet))
905 return;
906 } else {
907 if (tl.row >= start)
908 tl.row -= MIN (count, tl.row - start);
909 br.row -= count;
910 if (br.row <= tl.row)
911 br.row = tl.row + 1;
914 gnm_sheet_view_freeze_panes (sv, &tl, &br);
917 gboolean
918 gnm_sheet_view_is_frozen (SheetView const *sv)
920 g_return_val_if_fail (GNM_IS_SHEET_VIEW (sv), FALSE);
922 /* be flexible, in the future we will support 2 way splits too */
923 return sv->unfrozen_top_left.col >= 0 ||
924 sv->unfrozen_top_left.row >= 0;
928 gnm_sheet_view_set_initial_top_left:
929 * @sv: the sheet view.
930 * @col:
931 * @row:
933 * Sets the top left cell that a newly created sheet control should display.
934 * This corresponds to the top left cell visible in pane 0 (frozen or not).
935 * NOTE : the unfrozen_top_left != initial_top_left. Unfrozen is the first
936 * unfrozen cell, and corresponds to the _minimum_ cell in pane 0. However,
937 * the pane can scroll and may have something else currently visible as the top
938 * left.
940 void
941 gnm_sheet_view_set_initial_top_left (SheetView *sv, int col, int row)
943 g_return_if_fail (GNM_IS_SHEET_VIEW (sv));
944 g_return_if_fail (0 <= col && col < gnm_sheet_get_max_cols (sv->sheet));
945 g_return_if_fail (0 <= row && row < gnm_sheet_get_max_rows (sv->sheet));
946 g_return_if_fail (!gnm_sheet_view_is_frozen (sv) ||
947 (sv->unfrozen_top_left.col <= col &&
948 sv->unfrozen_top_left.row <= row));
950 sv->initial_top_left.col = col;
951 sv->initial_top_left.row = row;