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>
27 #include <dialogs/dialogs.h>
28 #include <dialogs/help.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>
47 #define GOTO_KEY "goto-dialog"
55 GtkWidget
*close_button
;
59 GtkSpinButton
*spin_rows
, *spin_cols
;
62 GtkTreeView
*treeview
;
63 GtkTreeSelection
*selection
;
65 gulong sheet_order_changed_listener
;
66 gulong sheet_added_listener
;
67 gulong sheet_deleted_listener
;
79 cb_dialog_goto_free (GotoState
*state
)
81 if (state
->sheet_order_changed_listener
)
82 g_signal_handler_disconnect (G_OBJECT (state
->wb
),
83 state
->sheet_order_changed_listener
);
84 if (state
->sheet_added_listener
)
85 g_signal_handler_disconnect (G_OBJECT (state
->wb
),
86 state
->sheet_added_listener
);
87 if (state
->sheet_deleted_listener
)
88 g_signal_handler_disconnect (G_OBJECT (state
->wb
),
89 state
->sheet_deleted_listener
);
91 if (state
->gui
!= NULL
)
92 g_object_unref (state
->gui
);
93 if (state
->model
!= NULL
)
94 g_object_unref (state
->model
);
99 cb_dialog_goto_close_clicked (G_GNUC_UNUSED GtkWidget
*button
,
102 gtk_widget_destroy (state
->dialog
);
106 dialog_goto_get_val (GotoState
*state
)
108 char const *text
= gtk_entry_get_text (state
->goto_text
);
109 Sheet
*sheet
= wb_control_cur_sheet (GNM_WBC (state
->wbcg
));
110 GnmValue
*val
= value_new_cellrange_str (sheet
, text
);
114 GnmNamedExpr
*nexpr
= expr_name_lookup
115 (parse_pos_init_sheet (&pp
, sheet
), text
);
116 if (nexpr
!= NULL
&& !expr_name_is_placeholder (nexpr
)) {
117 val
= gnm_expr_top_get_range (nexpr
->texpr
);
124 cb_dialog_goto_go_clicked (G_GNUC_UNUSED GtkWidget
*button
,
129 gint cols
= gtk_spin_button_get_value_as_int (state
->spin_cols
);
130 gint rows
= gtk_spin_button_get_value_as_int (state
->spin_rows
);
131 GnmValue
*val
= dialog_goto_get_val (state
);
132 Sheet
*sheet
= wb_control_cur_sheet (GNM_WBC (state
->wbcg
));
137 val
->v_range
.cell
.b
.row
= val
->v_range
.cell
.a
.row
+ (rows
- 1);
138 val
->v_range
.cell
.b
.col
= val
->v_range
.cell
.a
.col
+ (cols
- 1);
139 eval_pos_init_sheet (&ep
, sheet
);
140 gnm_cellref_make_abs (&range
.a
, &val
->v_range
.cell
.a
, &ep
);
141 gnm_cellref_make_abs (&range
.b
, &val
->v_range
.cell
.b
, &ep
);
144 wb_control_jump (GNM_WBC (state
->wbcg
), sheet
, &range
);
149 cb_dialog_goto_update_sensitivity (G_GNUC_UNUSED GtkWidget
*dummy
,
152 GnmValue
*val
= dialog_goto_get_val (state
);
155 Sheet
*sheet
= val
->v_range
.cell
.a
.sheet
;
156 GnmSheetSize
const *ssz
;
159 sheet
= wb_control_cur_sheet (GNM_WBC (state
->wbcg
));
160 ssz
= gnm_sheet_get_size (sheet
);
162 cols
= ssz
->max_cols
;
163 rows
= ssz
->max_rows
;
165 if (val
->v_range
.cell
.a
.sheet
!= NULL
&&
166 val
->v_range
.cell
.b
.sheet
!= NULL
&&
167 val
->v_range
.cell
.a
.sheet
!= val
->v_range
.cell
.b
.sheet
) {
168 ssz
= gnm_sheet_get_size (sheet
);
169 if (cols
> ssz
->max_cols
)
170 cols
= ssz
->max_cols
;
171 if (rows
> ssz
->max_rows
)
172 cols
= ssz
->max_rows
;
174 cols
-= val
->v_range
.cell
.a
.col
;
175 rows
-= val
->v_range
.cell
.a
.row
;
177 if (cols
< 1) cols
= 1;
178 if (rows
< 1) rows
= 1;
180 gtk_spin_button_set_range (state
->spin_cols
, 1, cols
);
181 gtk_spin_button_set_range (state
->spin_rows
, 1, rows
);
183 gtk_widget_set_sensitive (state
->go_button
, TRUE
);
187 gtk_widget_set_sensitive (state
->go_button
, FALSE
);
188 gtk_entry_set_activates_default (state
->goto_text
, (val
!= NULL
));
197 cb_load_names (G_GNUC_UNUSED gpointer key
,
198 GnmNamedExpr
*nexpr
, LoadNames
*user
)
201 gboolean is_address
= gnm_expr_top_is_rangeref (nexpr
->texpr
);
203 if (expr_name_is_placeholder (nexpr
))
207 gtk_tree_store_append (user
->state
->model
, &iter
, &user
->iter
);
208 gtk_tree_store_set (user
->state
->model
, &iter
,
209 ITEM_NAME
, expr_name_name (nexpr
),
210 SHEET_POINTER
, nexpr
->pos
.sheet
,
217 dialog_goto_load_names (GotoState
*state
)
223 gtk_tree_store_clear (state
->model
);
225 closure
.state
= state
;
226 gtk_tree_store_append (state
->model
, &closure
.iter
, NULL
);
227 gtk_tree_store_set (state
->model
, &closure
.iter
,
228 SHEET_NAME
, _("Workbook Level"),
233 workbook_foreach_name (state
->wb
, FALSE
,
234 (GHFunc
)cb_load_names
, &closure
);
236 l
= workbook_sheet_count (state
->wb
);
237 for (i
= 0; i
< l
; i
++) {
238 sheet
= workbook_sheet_by_index (state
->wb
, i
);
239 gtk_tree_store_append (state
->model
, &closure
.iter
, NULL
);
240 gtk_tree_store_set (state
->model
, &closure
.iter
,
241 SHEET_NAME
, sheet
->name_unquoted
,
243 SHEET_POINTER
, sheet
,
250 cb_dialog_goto_selection_changed (GtkTreeSelection
*the_selection
, GotoState
*state
)
257 if (gtk_tree_selection_get_selected (the_selection
, &model
, &iter
)) {
258 gtk_tree_model_get (model
, &iter
,
259 SHEET_POINTER
, &sheet
,
262 if (name
&& gnm_expr_top_is_rangeref (name
->texpr
)) {
267 sheet
= wb_control_cur_sheet ( GNM_WBC (state
->wbcg
));
269 parse_pos_init_sheet (&pp
, sheet
);
270 where_to
= expr_name_as_string (name
, &pp
, gnm_conventions_default
);
271 if (wb_control_parse_and_jump (GNM_WBC (state
->wbcg
), where_to
))
272 gtk_entry_set_text (state
->goto_text
,
278 wb_view_sheet_focus (
279 wb_control_view (GNM_WBC (state
->wbcg
)), sheet
);
285 cb_sheet_order_changed (G_GNUC_UNUSED Workbook
*wb
, GotoState
*state
)
287 dialog_goto_load_names (state
);
291 cb_sheet_deleted (G_GNUC_UNUSED Workbook
*wb
, GotoState
*state
)
293 dialog_goto_load_names (state
);
294 cb_dialog_goto_update_sensitivity (NULL
, state
);
298 cb_sheet_added (G_GNUC_UNUSED Workbook
*wb
, GotoState
*state
)
300 dialog_goto_load_names (state
);
301 cb_dialog_goto_update_sensitivity (NULL
, state
);
305 dialog_goto_load_selection (GotoState
*state
)
307 SheetView
*sv
= wb_control_cur_sheet_view
308 (GNM_WBC (state
->wbcg
));
309 GnmRange
const *first
= selection_first_range (sv
, NULL
, NULL
);
312 gint rows
= range_height (first
);
313 gint cols
= range_width (first
);
314 GnmConventionsOut out
;
315 GString
*str
= g_string_new (NULL
);
320 out
.pp
= parse_pos_init_sheet (&pp
, sv
->sheet
);
321 out
.convs
= sheet_get_conventions (sv
->sheet
);
322 gnm_cellref_init (&rr
.a
, NULL
, first
->start
.col
,
323 first
->start
.row
, TRUE
);
324 gnm_cellref_init (&rr
.b
, NULL
, first
->start
.col
,
325 first
->start
.row
, TRUE
);
326 rangeref_as_string (&out
, &rr
);
327 gtk_entry_set_text (state
->goto_text
, str
->str
);
328 gtk_editable_select_region (GTK_EDITABLE (state
->goto_text
),
330 g_string_free (str
, TRUE
);
331 cb_dialog_goto_update_sensitivity (NULL
, state
);
332 gtk_spin_button_set_value (state
->spin_rows
, rows
);
333 gtk_spin_button_set_value (state
->spin_cols
, cols
);
335 cb_dialog_goto_update_sensitivity (NULL
, state
);
343 * Create the dialog (guru).
346 dialog_goto_init (GotoState
*state
)
350 GtkTreeViewColumn
*column
;
352 grid
= GTK_GRID (go_gtk_builder_get_widget (state
->gui
, "names"));
353 state
->goto_text
= GTK_ENTRY (gtk_entry_new ());
354 gtk_widget_set_hexpand (GTK_WIDGET (state
->goto_text
), TRUE
);
355 gtk_grid_attach (grid
, GTK_WIDGET (state
->goto_text
), 0, 2, 1, 1);
356 g_signal_connect_after (G_OBJECT (state
->goto_text
),
358 G_CALLBACK (cb_dialog_goto_update_sensitivity
), state
);
360 state
->spin_rows
= GTK_SPIN_BUTTON
361 (go_gtk_builder_get_widget (state
->gui
, "spin-rows"));
362 state
->spin_cols
= GTK_SPIN_BUTTON
363 (go_gtk_builder_get_widget (state
->gui
, "spin-columns"));
365 /* Set-up treeview */
366 scrolled
= go_gtk_builder_get_widget (state
->gui
, "scrolled");
367 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled
),
368 GTK_SHADOW_ETCHED_IN
);
370 state
->model
= gtk_tree_store_new (NUM_COLUMNS
, G_TYPE_STRING
,
371 G_TYPE_STRING
, G_TYPE_POINTER
,
373 state
->treeview
= GTK_TREE_VIEW (
374 gtk_tree_view_new_with_model (GTK_TREE_MODEL (state
->model
)));
375 state
->selection
= gtk_tree_view_get_selection (state
->treeview
);
376 gtk_tree_selection_set_mode (state
->selection
, GTK_SELECTION_BROWSE
);
377 g_signal_connect (state
->selection
,
379 G_CALLBACK (cb_dialog_goto_selection_changed
), state
);
381 column
= gtk_tree_view_column_new_with_attributes (_("Sheet"),
382 gtk_cell_renderer_text_new (),
383 "text", SHEET_NAME
, NULL
);
384 gtk_tree_view_column_set_sort_column_id (column
, SHEET_NAME
);
385 gtk_tree_view_append_column (state
->treeview
, column
);
387 column
= gtk_tree_view_column_new_with_attributes (_("Cell"),
388 gtk_cell_renderer_text_new (),
389 "text", ITEM_NAME
, NULL
);
390 gtk_tree_view_column_set_sort_column_id (column
, ITEM_NAME
);
391 gtk_tree_view_append_column (state
->treeview
, column
);
392 gtk_tree_view_set_headers_visible (state
->treeview
, TRUE
);
393 gtk_container_add (GTK_CONTAINER (scrolled
), GTK_WIDGET (state
->treeview
));
394 dialog_goto_load_names (state
);
395 /* Finished set-up of treeview */
397 /* Listen for sheet changes */
398 state
->sheet_order_changed_listener
= g_signal_connect (G_OBJECT (state
->wb
),
399 "sheet_order_changed", G_CALLBACK (cb_sheet_order_changed
),
401 state
->sheet_added_listener
= g_signal_connect (G_OBJECT (state
->wb
),
402 "sheet_added", G_CALLBACK (cb_sheet_added
),
404 state
->sheet_deleted_listener
= g_signal_connect (G_OBJECT (state
->wb
),
405 "sheet_deleted", G_CALLBACK (cb_sheet_deleted
),
408 state
->close_button
= go_gtk_builder_get_widget (state
->gui
, "close_button");
409 g_signal_connect (G_OBJECT (state
->close_button
),
411 G_CALLBACK (cb_dialog_goto_close_clicked
), state
);
413 state
->go_button
= go_gtk_builder_get_widget (state
->gui
, "go_button");
414 g_signal_connect (G_OBJECT (state
->go_button
),
416 G_CALLBACK (cb_dialog_goto_go_clicked
), state
);
417 gtk_window_set_default (GTK_WINDOW (state
->dialog
), state
->go_button
);
419 gnm_init_help_button (
420 go_gtk_builder_get_widget (state
->gui
, "help_button"),
421 GNUMERIC_HELP_LINK_GOTO_CELL
);
423 dialog_goto_load_selection (state
);
425 wbc_gtk_attach_guru (state
->wbcg
, state
->dialog
);
426 g_object_set_data_full (G_OBJECT (state
->dialog
),
427 "state", state
, (GDestroyNotify
) cb_dialog_goto_free
);
434 dialog_goto_cell (WBCGtk
*wbcg
)
439 g_return_if_fail (wbcg
!= NULL
);
441 if (gnm_dialog_raise_if_exists (wbcg
, GOTO_KEY
))
443 gui
= gnm_gtk_builder_load ("res:ui/goto.ui", NULL
, GO_CMD_CONTEXT (wbcg
));
447 state
= g_new (GotoState
, 1);
449 state
->wb
= wb_control_get_workbook (GNM_WBC (wbcg
));
451 state
->dialog
= go_gtk_builder_get_widget (state
->gui
, "goto_dialog");
453 if (dialog_goto_init (state
)) {
454 go_gtk_notice_dialog (wbcg_toplevel (wbcg
), GTK_MESSAGE_ERROR
,
455 _("Could not create the goto dialog."));
460 gnm_keyed_dialog (state
->wbcg
, GTK_WINDOW (state
->dialog
),
463 gtk_widget_show_all (state
->dialog
);