Compilation: don't compile dialogs separately.
[gnumeric.git] / src / dialogs / dialog-goto-cell.c
blob9fec0731be7a0c2cd396b5b8e42f5a022ebaee0f
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.h"
28 #include "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>
46 #include <gtk/gtk.h>
48 #define GOTO_KEY "goto-dialog"
50 typedef struct {
51 WBCGtk *wbcg;
52 Workbook *wb;
54 GtkBuilder *gui;
55 GtkWidget *dialog;
56 GtkWidget *close_button;
57 GtkWidget *go_button;
58 GtkEntry *goto_text;
60 GtkSpinButton *spin_rows, *spin_cols;
62 GtkTreeStore *model;
63 GtkTreeView *treeview;
64 GtkTreeSelection *selection;
66 gulong sheet_order_changed_listener;
67 gulong sheet_added_listener;
68 gulong sheet_deleted_listener;
69 } GotoState;
71 enum {
72 ITEM_NAME,
73 SHEET_NAME,
74 SHEET_POINTER,
75 EXPRESSION,
76 NUM_COLUMNS
79 static void
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);
96 g_free (state);
99 static void
100 cb_dialog_goto_close_clicked (G_GNUC_UNUSED GtkWidget *button,
101 GotoState *state)
103 gtk_widget_destroy (state->dialog);
106 static GnmValue *
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);
113 if (val == NULL) {
114 GnmParsePos pp;
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);
121 return val;
124 static void
125 cb_dialog_goto_go_clicked (G_GNUC_UNUSED GtkWidget *button,
126 GotoState *state)
128 GnmEvalPos ep;
129 GnmRangeRef range;
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));
135 if (val == NULL)
136 return;
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);
143 value_release (val);
145 wb_control_jump (GNM_WBC (state->wbcg), sheet, &range);
146 return;
149 static void
150 cb_dialog_goto_update_sensitivity (G_GNUC_UNUSED GtkWidget *dummy,
151 GotoState *state)
153 GnmValue *val = dialog_goto_get_val (state);
154 if (val != NULL) {
155 gint cols, rows;
156 Sheet *sheet = val->v_range.cell.a.sheet;
157 GnmSheetSize const *ssz;
159 if (sheet == NULL)
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);
186 value_release (val);
187 } else
188 gtk_widget_set_sensitive (state->go_button, FALSE);
189 gtk_entry_set_activates_default (state->goto_text, (val != NULL));
192 typedef struct {
193 GtkTreeIter iter;
194 GotoState *state;
195 } LoadNames;
197 static void
198 cb_load_names (G_GNUC_UNUSED gpointer key,
199 GnmNamedExpr *nexpr, LoadNames *user)
201 GtkTreeIter iter;
202 gboolean is_address = gnm_expr_top_is_rangeref (nexpr->texpr);
204 if (expr_name_is_placeholder (nexpr))
205 return;
207 if (is_address) {
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,
212 EXPRESSION, nexpr,
213 -1);
217 static void
218 dialog_goto_load_names (GotoState *state)
220 Sheet *sheet;
221 LoadNames closure;
222 int i, l;
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"),
230 ITEM_NAME, NULL,
231 SHEET_POINTER, NULL,
232 EXPRESSION, NULL,
233 -1);
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,
243 ITEM_NAME, NULL,
244 SHEET_POINTER, sheet,
245 EXPRESSION, NULL,
246 -1);
250 static void
251 cb_dialog_goto_selection_changed (GtkTreeSelection *the_selection, GotoState *state)
253 GtkTreeIter iter;
254 GtkTreeModel *model;
255 Sheet *sheet;
256 GnmNamedExpr *name;
258 if (gtk_tree_selection_get_selected (the_selection, &model, &iter)) {
259 gtk_tree_model_get (model, &iter,
260 SHEET_POINTER, &sheet,
261 EXPRESSION, &name,
262 -1);
263 if (name && gnm_expr_top_is_rangeref (name->texpr)) {
264 GnmParsePos pp;
265 char *where_to;
267 if (NULL == sheet)
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,
274 where_to);
275 g_free (where_to);
276 return;
278 if (sheet)
279 wb_view_sheet_focus (
280 wb_control_view (GNM_WBC (state->wbcg)), sheet);
285 static void
286 cb_sheet_order_changed (G_GNUC_UNUSED Workbook *wb, GotoState *state)
288 dialog_goto_load_names (state);
291 static void
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);
298 static void
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);
305 static void
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);
312 if (first != NULL) {
313 gint rows = range_height (first);
314 gint cols = range_width (first);
315 GnmConventionsOut out;
316 GString *str = g_string_new (NULL);
317 GnmParsePos pp;
318 GnmRangeRef rr;
320 out.accum = str;
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),
330 0, -1);
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);
335 } else
336 cb_dialog_goto_update_sensitivity (NULL, state);
341 * dialog_goto_init:
342 * @state:
344 * Create the dialog (guru).
346 static gboolean
347 dialog_goto_init (GotoState *state)
349 GtkGrid *grid;
350 GtkWidget *scrolled;
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),
358 "changed",
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,
373 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,
379 "changed",
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),
401 state);
402 state->sheet_added_listener = g_signal_connect (G_OBJECT (state->wb),
403 "sheet_added", G_CALLBACK (cb_sheet_added),
404 state);
405 state->sheet_deleted_listener = g_signal_connect (G_OBJECT (state->wb),
406 "sheet_deleted", G_CALLBACK (cb_sheet_deleted),
407 state);
409 state->close_button = go_gtk_builder_get_widget (state->gui, "close_button");
410 g_signal_connect (G_OBJECT (state->close_button),
411 "clicked",
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),
416 "clicked",
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);
430 return FALSE;
434 void
435 dialog_goto_cell (WBCGtk *wbcg)
437 GotoState* state;
438 GtkBuilder *gui;
440 g_return_if_fail (wbcg != NULL);
442 if (gnm_dialog_raise_if_exists (wbcg, GOTO_KEY))
443 return;
444 gui = gnm_gtk_builder_load ("res:ui/goto.ui", NULL, GO_CMD_CONTEXT (wbcg));
445 if (gui == NULL)
446 return;
448 state = g_new (GotoState, 1);
449 state->wbcg = wbcg;
450 state->wb = wb_control_get_workbook (GNM_WBC (wbcg));
451 state->gui = gui;
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."));
457 g_free (state);
458 return;
461 gnm_keyed_dialog (state->wbcg, GTK_WINDOW (state->dialog),
462 GOTO_KEY);
464 gtk_widget_show_all (state->dialog);