3 * dialog-goto-cell.c: Implements the "goto cell/navigator" functionality
6 * Andreas J. Guelzow <aguelzow@pyrshep.ca>
8 * Copyright (C) Andreas J. Guelzow <aguelzow@pyrshep.ca>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, see <https://www.gnu.org/licenses/>.
24 #include <gnumeric-config.h>
25 #include <glib/gi18n-lib.h>
31 #include <workbook-control.h>
34 #include <expr-name.h>
38 #include <workbook-view.h>
39 #include <workbook-control.h>
40 #include <selection.h>
41 #include <parse-util.h>
42 #include <sheet-view.h>
48 #define GOTO_KEY "goto-dialog"
56 GtkWidget
*close_button
;
60 GtkSpinButton
*spin_rows
, *spin_cols
;
63 GtkTreeView
*treeview
;
64 GtkTreeSelection
*selection
;
66 gulong sheet_order_changed_listener
;
67 gulong sheet_added_listener
;
68 gulong sheet_deleted_listener
;
80 cb_dialog_goto_free (GotoState
*state
)
82 if (state
->sheet_order_changed_listener
)
83 g_signal_handler_disconnect (G_OBJECT (state
->wb
),
84 state
->sheet_order_changed_listener
);
85 if (state
->sheet_added_listener
)
86 g_signal_handler_disconnect (G_OBJECT (state
->wb
),
87 state
->sheet_added_listener
);
88 if (state
->sheet_deleted_listener
)
89 g_signal_handler_disconnect (G_OBJECT (state
->wb
),
90 state
->sheet_deleted_listener
);
92 if (state
->gui
!= NULL
)
93 g_object_unref (state
->gui
);
94 if (state
->model
!= NULL
)
95 g_object_unref (state
->model
);
100 cb_dialog_goto_close_clicked (G_GNUC_UNUSED GtkWidget
*button
,
103 gtk_widget_destroy (state
->dialog
);
107 dialog_goto_get_val (GotoState
*state
)
109 char const *text
= gtk_entry_get_text (state
->goto_text
);
110 Sheet
*sheet
= wb_control_cur_sheet (GNM_WBC (state
->wbcg
));
111 GnmValue
*val
= value_new_cellrange_str (sheet
, text
);
115 GnmNamedExpr
*nexpr
= expr_name_lookup
116 (parse_pos_init_sheet (&pp
, sheet
), text
);
117 if (nexpr
!= NULL
&& !expr_name_is_placeholder (nexpr
)) {
118 val
= gnm_expr_top_get_range (nexpr
->texpr
);
125 cb_dialog_goto_go_clicked (G_GNUC_UNUSED GtkWidget
*button
,
130 gint cols
= gtk_spin_button_get_value_as_int (state
->spin_cols
);
131 gint rows
= gtk_spin_button_get_value_as_int (state
->spin_rows
);
132 GnmValue
*val
= dialog_goto_get_val (state
);
133 Sheet
*sheet
= wb_control_cur_sheet (GNM_WBC (state
->wbcg
));
138 val
->v_range
.cell
.b
.row
= val
->v_range
.cell
.a
.row
+ (rows
- 1);
139 val
->v_range
.cell
.b
.col
= val
->v_range
.cell
.a
.col
+ (cols
- 1);
140 eval_pos_init_sheet (&ep
, sheet
);
141 gnm_cellref_make_abs (&range
.a
, &val
->v_range
.cell
.a
, &ep
);
142 gnm_cellref_make_abs (&range
.b
, &val
->v_range
.cell
.b
, &ep
);
145 wb_control_jump (GNM_WBC (state
->wbcg
), sheet
, &range
);
150 cb_dialog_goto_update_sensitivity (G_GNUC_UNUSED GtkWidget
*dummy
,
153 GnmValue
*val
= dialog_goto_get_val (state
);
156 Sheet
*sheet
= val
->v_range
.cell
.a
.sheet
;
157 GnmSheetSize
const *ssz
;
160 sheet
= wb_control_cur_sheet (GNM_WBC (state
->wbcg
));
161 ssz
= gnm_sheet_get_size (sheet
);
163 cols
= ssz
->max_cols
;
164 rows
= ssz
->max_rows
;
166 if (val
->v_range
.cell
.a
.sheet
!= NULL
&&
167 val
->v_range
.cell
.b
.sheet
!= NULL
&&
168 val
->v_range
.cell
.a
.sheet
!= val
->v_range
.cell
.b
.sheet
) {
169 ssz
= gnm_sheet_get_size (sheet
);
170 if (cols
> ssz
->max_cols
)
171 cols
= ssz
->max_cols
;
172 if (rows
> ssz
->max_rows
)
173 cols
= ssz
->max_rows
;
175 cols
-= val
->v_range
.cell
.a
.col
;
176 rows
-= val
->v_range
.cell
.a
.row
;
178 if (cols
< 1) cols
= 1;
179 if (rows
< 1) rows
= 1;
181 gtk_spin_button_set_range (state
->spin_cols
, 1, cols
);
182 gtk_spin_button_set_range (state
->spin_rows
, 1, rows
);
184 gtk_widget_set_sensitive (state
->go_button
, TRUE
);
188 gtk_widget_set_sensitive (state
->go_button
, FALSE
);
189 gtk_entry_set_activates_default (state
->goto_text
, (val
!= NULL
));
198 cb_load_names (G_GNUC_UNUSED gpointer key
,
199 GnmNamedExpr
*nexpr
, LoadNames
*user
)
202 gboolean is_address
= gnm_expr_top_is_rangeref (nexpr
->texpr
);
204 if (expr_name_is_placeholder (nexpr
))
208 gtk_tree_store_append (user
->state
->model
, &iter
, &user
->iter
);
209 gtk_tree_store_set (user
->state
->model
, &iter
,
210 ITEM_NAME
, expr_name_name (nexpr
),
211 SHEET_POINTER
, nexpr
->pos
.sheet
,
218 dialog_goto_load_names (GotoState
*state
)
224 gtk_tree_store_clear (state
->model
);
226 closure
.state
= state
;
227 gtk_tree_store_append (state
->model
, &closure
.iter
, NULL
);
228 gtk_tree_store_set (state
->model
, &closure
.iter
,
229 SHEET_NAME
, _("Workbook Level"),
234 workbook_foreach_name (state
->wb
, FALSE
,
235 (GHFunc
)cb_load_names
, &closure
);
237 l
= workbook_sheet_count (state
->wb
);
238 for (i
= 0; i
< l
; i
++) {
239 sheet
= workbook_sheet_by_index (state
->wb
, i
);
240 gtk_tree_store_append (state
->model
, &closure
.iter
, NULL
);
241 gtk_tree_store_set (state
->model
, &closure
.iter
,
242 SHEET_NAME
, sheet
->name_unquoted
,
244 SHEET_POINTER
, sheet
,
251 cb_dialog_goto_selection_changed (GtkTreeSelection
*the_selection
, GotoState
*state
)
258 if (gtk_tree_selection_get_selected (the_selection
, &model
, &iter
)) {
259 gtk_tree_model_get (model
, &iter
,
260 SHEET_POINTER
, &sheet
,
263 if (name
&& gnm_expr_top_is_rangeref (name
->texpr
)) {
268 sheet
= wb_control_cur_sheet ( GNM_WBC (state
->wbcg
));
270 parse_pos_init_sheet (&pp
, sheet
);
271 where_to
= expr_name_as_string (name
, &pp
, gnm_conventions_default
);
272 if (wb_control_parse_and_jump (GNM_WBC (state
->wbcg
), where_to
))
273 gtk_entry_set_text (state
->goto_text
,
279 wb_view_sheet_focus (
280 wb_control_view (GNM_WBC (state
->wbcg
)), sheet
);
286 cb_sheet_order_changed (G_GNUC_UNUSED Workbook
*wb
, GotoState
*state
)
288 dialog_goto_load_names (state
);
292 cb_sheet_deleted (G_GNUC_UNUSED Workbook
*wb
, GotoState
*state
)
294 dialog_goto_load_names (state
);
295 cb_dialog_goto_update_sensitivity (NULL
, state
);
299 cb_sheet_added (G_GNUC_UNUSED Workbook
*wb
, GotoState
*state
)
301 dialog_goto_load_names (state
);
302 cb_dialog_goto_update_sensitivity (NULL
, state
);
306 dialog_goto_load_selection (GotoState
*state
)
308 SheetView
*sv
= wb_control_cur_sheet_view
309 (GNM_WBC (state
->wbcg
));
310 GnmRange
const *first
= selection_first_range (sv
, NULL
, NULL
);
313 gint rows
= range_height (first
);
314 gint cols
= range_width (first
);
315 GnmConventionsOut out
;
316 GString
*str
= g_string_new (NULL
);
321 out
.pp
= parse_pos_init_sheet (&pp
, sv
->sheet
);
322 out
.convs
= sheet_get_conventions (sv
->sheet
);
323 gnm_cellref_init (&rr
.a
, NULL
, first
->start
.col
,
324 first
->start
.row
, TRUE
);
325 gnm_cellref_init (&rr
.b
, NULL
, first
->start
.col
,
326 first
->start
.row
, TRUE
);
327 rangeref_as_string (&out
, &rr
);
328 gtk_entry_set_text (state
->goto_text
, str
->str
);
329 gtk_editable_select_region (GTK_EDITABLE (state
->goto_text
),
331 g_string_free (str
, TRUE
);
332 cb_dialog_goto_update_sensitivity (NULL
, state
);
333 gtk_spin_button_set_value (state
->spin_rows
, rows
);
334 gtk_spin_button_set_value (state
->spin_cols
, cols
);
336 cb_dialog_goto_update_sensitivity (NULL
, state
);
344 * Create the dialog (guru).
347 dialog_goto_init (GotoState
*state
)
351 GtkTreeViewColumn
*column
;
353 grid
= GTK_GRID (go_gtk_builder_get_widget (state
->gui
, "names"));
354 state
->goto_text
= GTK_ENTRY (gtk_entry_new ());
355 gtk_widget_set_hexpand (GTK_WIDGET (state
->goto_text
), TRUE
);
356 gtk_grid_attach (grid
, GTK_WIDGET (state
->goto_text
), 0, 2, 1, 1);
357 g_signal_connect_after (G_OBJECT (state
->goto_text
),
359 G_CALLBACK (cb_dialog_goto_update_sensitivity
), state
);
361 state
->spin_rows
= GTK_SPIN_BUTTON
362 (go_gtk_builder_get_widget (state
->gui
, "spin-rows"));
363 state
->spin_cols
= GTK_SPIN_BUTTON
364 (go_gtk_builder_get_widget (state
->gui
, "spin-columns"));
366 /* Set-up treeview */
367 scrolled
= go_gtk_builder_get_widget (state
->gui
, "scrolled");
368 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled
),
369 GTK_SHADOW_ETCHED_IN
);
371 state
->model
= gtk_tree_store_new (NUM_COLUMNS
, G_TYPE_STRING
,
372 G_TYPE_STRING
, G_TYPE_POINTER
,
374 state
->treeview
= GTK_TREE_VIEW (
375 gtk_tree_view_new_with_model (GTK_TREE_MODEL (state
->model
)));
376 state
->selection
= gtk_tree_view_get_selection (state
->treeview
);
377 gtk_tree_selection_set_mode (state
->selection
, GTK_SELECTION_BROWSE
);
378 g_signal_connect (state
->selection
,
380 G_CALLBACK (cb_dialog_goto_selection_changed
), state
);
382 column
= gtk_tree_view_column_new_with_attributes (_("Sheet"),
383 gtk_cell_renderer_text_new (),
384 "text", SHEET_NAME
, NULL
);
385 gtk_tree_view_column_set_sort_column_id (column
, SHEET_NAME
);
386 gtk_tree_view_append_column (state
->treeview
, column
);
388 column
= gtk_tree_view_column_new_with_attributes (_("Cell"),
389 gtk_cell_renderer_text_new (),
390 "text", ITEM_NAME
, NULL
);
391 gtk_tree_view_column_set_sort_column_id (column
, ITEM_NAME
);
392 gtk_tree_view_append_column (state
->treeview
, column
);
393 gtk_tree_view_set_headers_visible (state
->treeview
, TRUE
);
394 gtk_container_add (GTK_CONTAINER (scrolled
), GTK_WIDGET (state
->treeview
));
395 dialog_goto_load_names (state
);
396 /* Finished set-up of treeview */
398 /* Listen for sheet changes */
399 state
->sheet_order_changed_listener
= g_signal_connect (G_OBJECT (state
->wb
),
400 "sheet_order_changed", G_CALLBACK (cb_sheet_order_changed
),
402 state
->sheet_added_listener
= g_signal_connect (G_OBJECT (state
->wb
),
403 "sheet_added", G_CALLBACK (cb_sheet_added
),
405 state
->sheet_deleted_listener
= g_signal_connect (G_OBJECT (state
->wb
),
406 "sheet_deleted", G_CALLBACK (cb_sheet_deleted
),
409 state
->close_button
= go_gtk_builder_get_widget (state
->gui
, "close_button");
410 g_signal_connect (G_OBJECT (state
->close_button
),
412 G_CALLBACK (cb_dialog_goto_close_clicked
), state
);
414 state
->go_button
= go_gtk_builder_get_widget (state
->gui
, "go_button");
415 g_signal_connect (G_OBJECT (state
->go_button
),
417 G_CALLBACK (cb_dialog_goto_go_clicked
), state
);
418 gtk_window_set_default (GTK_WINDOW (state
->dialog
), state
->go_button
);
420 gnm_init_help_button (
421 go_gtk_builder_get_widget (state
->gui
, "help_button"),
422 GNUMERIC_HELP_LINK_GOTO_CELL
);
424 dialog_goto_load_selection (state
);
426 wbc_gtk_attach_guru (state
->wbcg
, state
->dialog
);
427 g_object_set_data_full (G_OBJECT (state
->dialog
),
428 "state", state
, (GDestroyNotify
) cb_dialog_goto_free
);
435 dialog_goto_cell (WBCGtk
*wbcg
)
440 g_return_if_fail (wbcg
!= NULL
);
442 if (gnm_dialog_raise_if_exists (wbcg
, GOTO_KEY
))
444 gui
= gnm_gtk_builder_load ("res:ui/goto.ui", NULL
, GO_CMD_CONTEXT (wbcg
));
448 state
= g_new (GotoState
, 1);
450 state
->wb
= wb_control_get_workbook (GNM_WBC (wbcg
));
452 state
->dialog
= go_gtk_builder_get_widget (state
->gui
, "goto_dialog");
454 if (dialog_goto_init (state
)) {
455 go_gtk_notice_dialog (wbcg_toplevel (wbcg
), GTK_MESSAGE_ERROR
,
456 _("Could not create the goto dialog."));
461 gnm_keyed_dialog (state
->wbcg
, GTK_WINDOW (state
->dialog
),
464 gtk_widget_show_all (state
->dialog
);