Update Spanish translation
[gnumeric.git] / src / dialogs / dialog-goto-cell.c
blobff567934fc78665ee86643683b900d3c184ac3b7
2 /*
3 * dialog-goto-cell.c: Implements the "goto cell/navigator" functionality
5 * Author:
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>
26 #include <gnumeric.h>
27 #include <dialogs/dialogs.h>
28 #include <dialogs/help.h>
30 #include <gui-util.h>
31 #include <workbook-control.h>
32 #include <ranges.h>
33 #include <value.h>
34 #include <expr-name.h>
35 #include <expr.h>
36 #include <sheet.h>
37 #include <workbook.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>
44 #include <wbc-gtk.h>
47 #define GOTO_KEY "goto-dialog"
49 typedef struct {
50 WBCGtk *wbcg;
51 Workbook *wb;
53 GtkBuilder *gui;
54 GtkWidget *dialog;
55 GtkWidget *close_button;
56 GtkWidget *go_button;
57 GtkEntry *goto_text;
59 GtkSpinButton *spin_rows, *spin_cols;
61 GtkTreeStore *model;
62 GtkTreeView *treeview;
63 GtkTreeSelection *selection;
65 gulong sheet_order_changed_listener;
66 gulong sheet_added_listener;
67 gulong sheet_deleted_listener;
68 } GotoState;
70 enum {
71 ITEM_NAME,
72 SHEET_NAME,
73 SHEET_POINTER,
74 EXPRESSION,
75 NUM_COLUMNS
78 static void
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);
95 g_free (state);
98 static void
99 cb_dialog_goto_close_clicked (G_GNUC_UNUSED GtkWidget *button,
100 GotoState *state)
102 gtk_widget_destroy (state->dialog);
105 static GnmValue *
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);
112 if (val == NULL) {
113 GnmParsePos pp;
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);
120 return val;
123 static void
124 cb_dialog_goto_go_clicked (G_GNUC_UNUSED GtkWidget *button,
125 GotoState *state)
127 GnmEvalPos ep;
128 GnmRangeRef range;
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));
134 if (val == NULL)
135 return;
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);
142 value_release (val);
144 wb_control_jump (GNM_WBC (state->wbcg), sheet, &range);
145 return;
148 static void
149 cb_dialog_goto_update_sensitivity (G_GNUC_UNUSED GtkWidget *dummy,
150 GotoState *state)
152 GnmValue *val = dialog_goto_get_val (state);
153 if (val != NULL) {
154 gint cols, rows;
155 Sheet *sheet = val->v_range.cell.a.sheet;
156 GnmSheetSize const *ssz;
158 if (sheet == NULL)
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);
185 value_release (val);
186 } else
187 gtk_widget_set_sensitive (state->go_button, FALSE);
188 gtk_entry_set_activates_default (state->goto_text, (val != NULL));
191 typedef struct {
192 GtkTreeIter iter;
193 GotoState *state;
194 } LoadNames;
196 static void
197 cb_load_names (G_GNUC_UNUSED gpointer key,
198 GnmNamedExpr *nexpr, LoadNames *user)
200 GtkTreeIter iter;
201 gboolean is_address = gnm_expr_top_is_rangeref (nexpr->texpr);
203 if (expr_name_is_placeholder (nexpr))
204 return;
206 if (is_address) {
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,
211 EXPRESSION, nexpr,
212 -1);
216 static void
217 dialog_goto_load_names (GotoState *state)
219 Sheet *sheet;
220 LoadNames closure;
221 int i, l;
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"),
229 ITEM_NAME, NULL,
230 SHEET_POINTER, NULL,
231 EXPRESSION, NULL,
232 -1);
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,
242 ITEM_NAME, NULL,
243 SHEET_POINTER, sheet,
244 EXPRESSION, NULL,
245 -1);
249 static void
250 cb_dialog_goto_selection_changed (GtkTreeSelection *the_selection, GotoState *state)
252 GtkTreeIter iter;
253 GtkTreeModel *model;
254 Sheet *sheet;
255 GnmNamedExpr *name;
257 if (gtk_tree_selection_get_selected (the_selection, &model, &iter)) {
258 gtk_tree_model_get (model, &iter,
259 SHEET_POINTER, &sheet,
260 EXPRESSION, &name,
261 -1);
262 if (name && gnm_expr_top_is_rangeref (name->texpr)) {
263 GnmParsePos pp;
264 char *where_to;
266 if (NULL == sheet)
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,
273 where_to);
274 g_free (where_to);
275 return;
277 if (sheet)
278 wb_view_sheet_focus (
279 wb_control_view (GNM_WBC (state->wbcg)), sheet);
284 static void
285 cb_sheet_order_changed (G_GNUC_UNUSED Workbook *wb, GotoState *state)
287 dialog_goto_load_names (state);
290 static void
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);
297 static void
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);
304 static void
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);
311 if (first != NULL) {
312 gint rows = range_height (first);
313 gint cols = range_width (first);
314 GnmConventionsOut out;
315 GString *str = g_string_new (NULL);
316 GnmParsePos pp;
317 GnmRangeRef rr;
319 out.accum = str;
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),
329 0, -1);
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);
334 } else
335 cb_dialog_goto_update_sensitivity (NULL, state);
340 * dialog_goto_init:
341 * @state:
343 * Create the dialog (guru).
345 static gboolean
346 dialog_goto_init (GotoState *state)
348 GtkGrid *grid;
349 GtkWidget *scrolled;
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),
357 "changed",
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,
372 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,
378 "changed",
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),
400 state);
401 state->sheet_added_listener = g_signal_connect (G_OBJECT (state->wb),
402 "sheet_added", G_CALLBACK (cb_sheet_added),
403 state);
404 state->sheet_deleted_listener = g_signal_connect (G_OBJECT (state->wb),
405 "sheet_deleted", G_CALLBACK (cb_sheet_deleted),
406 state);
408 state->close_button = go_gtk_builder_get_widget (state->gui, "close_button");
409 g_signal_connect (G_OBJECT (state->close_button),
410 "clicked",
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),
415 "clicked",
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);
429 return FALSE;
433 void
434 dialog_goto_cell (WBCGtk *wbcg)
436 GotoState* state;
437 GtkBuilder *gui;
439 g_return_if_fail (wbcg != NULL);
441 if (gnm_dialog_raise_if_exists (wbcg, GOTO_KEY))
442 return;
443 gui = gnm_gtk_builder_load ("res:ui/goto.ui", NULL, GO_CMD_CONTEXT (wbcg));
444 if (gui == NULL)
445 return;
447 state = g_new (GotoState, 1);
448 state->wbcg = wbcg;
449 state->wb = wb_control_get_workbook (GNM_WBC (wbcg));
450 state->gui = gui;
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."));
456 g_free (state);
457 return;
460 gnm_keyed_dialog (state->wbcg, GTK_WINDOW (state->dialog),
461 GOTO_KEY);
463 gtk_widget_show_all (state->dialog);