GUI: Move .ui files from goffice resources to glib resources
[gnumeric.git] / src / dialogs / dialog-define-names.c
blobbc5f470e23a0b3d03eb2f1cf7489cb75a73f14a8
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* vim: set sw=8: */
3 /*
4 * dialog-define-name.c: Edit named regions.
6 * Author:
7 * Jody Goldberg <jody@gnome.org>
8 * Michael Meeks <michael@ximian.com>
9 * Chema Celorio <chema@celorio.com>
10 * Andreas J. Guelzow <aguelzow@pyrshep.ca>
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses/>.
25 #include <gnumeric-config.h>
26 #include <glib/gi18n-lib.h>
27 #include <gnumeric.h>
28 #include "dialogs.h"
29 #include "help.h"
31 #include <expr.h>
32 #include <expr-name.h>
33 #include <selection.h>
34 #include <sheet.h>
35 #include <sheet-view.h>
36 #include <workbook.h>
37 #include <workbook-control.h>
38 #include <wbc-gtk.h>
39 #include <workbook-view.h>
40 #include <gui-util.h>
41 #include <parse-util.h>
42 #include <commands.h>
43 #include <widgets/gnumeric-expr-entry.h>
44 #include <widgets/gnumeric-cell-renderer-expr-entry.h>
45 #include <widgets/gnumeric-cell-renderer-toggle.h>
47 #include <gtk/gtk.h>
48 #include <string.h>
50 #define DEFINE_NAMES_KEY "define-names-dialog"
51 #define PASTE_NAMES_KEY "paste-names-dialog"
53 typedef struct {
54 GtkBuilder *gui;
55 GtkWidget *dialog;
56 GtkWidget *treeview;
57 GtkTreeStore *model;
58 GtkTreeModel *model_f;
60 GtkWidget *close_button;
61 GtkWidget *paste_button;
62 GtkWidget *search_entry;
64 Sheet *sheet;
65 SheetView *sv;
66 Workbook *wb;
67 WBCGtk *wbcg;
68 GnmParsePos pp;
70 GdkPixbuf *image_add;
71 GdkPixbuf *image_delete;
72 GdkPixbuf *image_lock;
73 GdkPixbuf *image_up;
74 GdkPixbuf *image_down;
75 GdkPixbuf *image_paste;
77 gboolean is_paste_dialog;
78 gboolean has_pasted;
79 } NameGuruState;
81 enum {
82 ITEM_NAME,
83 ITEM_NAME_POINTER,
84 ITEM_CONTENT,
85 ITEM_TYPE,
86 ITEM_CONTENT_IS_EDITABLE,
87 ITEM_NAME_IS_EDITABLE,
88 ITEM_UPDOWN_IMAGE,
89 ITEM_ADDDELETE_IMAGE,
90 ITEM_UPDOWN_ACTIVE,
91 ITEM_ADDDELETE_ACTIVE,
92 ITEM_PASTABLE,
93 ITEM_PASTE_IMAGE,
94 ITEM_VISIBLE,
95 NUM_COLUMNS
98 typedef enum {
99 item_type_workbook = 0,
100 item_type_main_sheet,
101 item_type_other_sheet,
102 item_type_locked_name,
103 item_type_available_wb_name,
104 item_type_available_sheet_name,
105 item_type_foreign_name,
106 item_type_new_unsaved_wb_name,
107 item_type_new_unsaved_sheet_name,
108 } item_type_t;
112 * name_guru_translate_pathstring_to_iter:
113 * @state:
114 * @path_string: in the filter_model
115 * @iter: in the base model
119 static gboolean
120 name_guru_translate_pathstring_to_iter (NameGuruState *state,
121 GtkTreeIter *iter,
122 gchar const *path_string)
124 GtkTreeIter iter_f;
126 if (!gtk_tree_model_get_iter_from_string
127 (state->model_f, &iter_f, path_string))
128 return FALSE;
130 gtk_tree_model_filter_convert_iter_to_child_iter
131 (GTK_TREE_MODEL_FILTER (state->model_f), iter, &iter_f);
132 return TRUE;
139 * name_guru_expand_at_iter:
140 * @state:
141 * @iter:
143 * expand the treeview at the given iter.
146 static void
147 name_guru_expand_at_iter (NameGuruState *state, GtkTreeIter *iter)
149 GtkTreePath *path;
151 path = gtk_tree_model_get_path
152 (GTK_TREE_MODEL (state->model), iter);
153 gtk_tree_view_expand_to_path
154 (GTK_TREE_VIEW (state->treeview), path);
155 gtk_tree_path_free (path);
160 * name_guru_warned_if_used:
161 * @state:
162 * @nexpr: expression to be deleted
164 * If the expresion that is about to be deleted is being used,
165 * warn the user about it. Ask if we should proceed or not
167 * Return Value: TRUE if users confirms deletion, FALSE otherwise
170 static gboolean
171 name_guru_warn (NameGuruState *state,
172 GnmNamedExpr *nexpr)
174 return (!expr_name_in_use (nexpr) ||
175 go_gtk_query_yes_no
176 (GTK_WINDOW (state->dialog), FALSE,
177 "The defined name '%s' is in use. "
178 "Do you really want to delete it?",
179 expr_name_name (nexpr)));
182 static gboolean
183 cb_name_guru_show_all (G_GNUC_UNUSED GtkTreeModel *model,
184 G_GNUC_UNUSED GtkTreePath *path,
185 GtkTreeIter *iter, gpointer data)
187 NameGuruState *state = data;
188 gtk_tree_store_set (state->model, iter,
189 ITEM_VISIBLE, TRUE,
190 -1);
191 return FALSE;
194 static void
195 name_guru_erase_search_entry (GtkEntry *entry,
196 G_GNUC_UNUSED GtkEntryIconPosition icon_pos,
197 G_GNUC_UNUSED GdkEvent *event,
198 gpointer data)
200 NameGuruState *state = data;
201 gtk_entry_set_text (entry, "");
202 gtk_tree_model_foreach (GTK_TREE_MODEL (state->model),
203 cb_name_guru_show_all, state);
206 static gboolean
207 cb_name_guru_search (GtkTreeModel *model,
208 G_GNUC_UNUSED GtkTreePath *path,
209 GtkTreeIter *iter, gpointer data)
211 char const *text = data;
212 gchar *name;
213 gboolean visible = TRUE, was_visible;
214 item_type_t type;
216 gtk_tree_model_get (model, iter,
217 ITEM_TYPE, &type,
218 ITEM_NAME, &name,
219 ITEM_VISIBLE, &was_visible,
220 -1);
222 if (type != item_type_workbook &&
223 type != item_type_main_sheet &&
224 type != item_type_other_sheet) {
225 gchar *name_n, *name_cf, *text_n, *text_cf;
227 text_n = g_utf8_normalize (text, -1, G_NORMALIZE_ALL);
228 text_cf = g_utf8_casefold(text_n, -1);
229 name_n = g_utf8_normalize (name, -1, G_NORMALIZE_ALL);
230 name_cf = g_utf8_casefold(name_n, -1);
231 visible = (NULL != g_strstr_len (name_cf, -1, text_cf));
232 g_free (text_n);
233 g_free (text_cf);
234 g_free (name_n);
235 g_free (name_cf);
238 if (visible != was_visible)
239 gtk_tree_store_set (GTK_TREE_STORE (model), iter,
240 ITEM_VISIBLE, visible,
241 -1);
243 g_free (name);
244 return FALSE;
247 static void
248 name_guru_search (GtkEntry *entry, gpointer data)
250 gchar const *text;
251 NameGuruState *state = data;
253 if (0 == gtk_entry_get_text_length (entry)){
254 name_guru_erase_search_entry
255 (entry,
256 GTK_ENTRY_ICON_SECONDARY, NULL,
257 data);
258 return;
260 text = gtk_entry_get_text (entry);
261 gtk_tree_model_foreach (GTK_TREE_MODEL (state->model),
262 cb_name_guru_search, (gpointer) text);
265 static void
266 cb_get_names (G_GNUC_UNUSED gpointer key, GnmNamedExpr *nexpr,
267 GList **accum)
269 if (!nexpr->is_hidden)
270 *accum = g_list_prepend (*accum, nexpr);
273 static GList *
274 name_guru_get_available_sheet_names (Sheet const *sheet)
276 GList *res = NULL;
278 gnm_sheet_foreach_name (sheet, (GHFunc) cb_get_names, &res);
279 return g_list_sort (res, (GCompareFunc)expr_name_cmp_by_name);
282 static GList *
283 name_guru_get_available_wb_names (Workbook const *wb)
285 GList *res = NULL;
287 workbook_foreach_name (wb, TRUE,
288 (GHFunc) cb_get_names,
289 &res);
290 return g_list_sort (res, (GCompareFunc)expr_name_cmp_by_name);
293 static void
294 name_guru_set_images (NameGuruState *state, GtkTreeIter *name_iter,
295 item_type_t type, gboolean pastable)
297 GdkPixbuf *button1 = NULL, *button2 = NULL;
299 switch (type) {
300 case item_type_workbook:
301 case item_type_main_sheet:
302 button2 = state->image_add;
303 break;
304 case item_type_locked_name:
305 button2 = state->image_lock;
306 break;
307 case item_type_available_wb_name:
308 case item_type_new_unsaved_wb_name:
309 button1 = state->image_down;
310 button2 = state->image_delete;
311 break;
312 case item_type_available_sheet_name:
313 case item_type_new_unsaved_sheet_name:
314 button1 = state->image_up;
315 button2 = state->image_delete;
316 break;
317 case item_type_other_sheet:
318 case item_type_foreign_name:
319 default:
320 break;
323 gtk_tree_store_set (state->model, name_iter,
324 ITEM_UPDOWN_IMAGE, button1,
325 ITEM_ADDDELETE_IMAGE, button2,
326 ITEM_PASTE_IMAGE,
327 pastable ? state->image_paste : NULL,
328 ITEM_UPDOWN_ACTIVE, button1 != NULL,
329 ITEM_ADDDELETE_ACTIVE, button2 != NULL,
330 -1);
333 static void
334 name_guru_store_names (GList *list,
335 GtkTreeIter *iter,
336 NameGuruState *state,
337 item_type_t type)
339 GtkTreeIter name_iter;
340 char *content;
341 item_type_t adj_type;
342 GList *l;
344 for (l = list; l != NULL; l = l->next) {
345 GnmNamedExpr *nexpr = l->data;
346 gboolean ciseditable, ispastable;
348 if (nexpr->is_hidden || expr_name_is_placeholder (nexpr))
349 continue;
351 ispastable = ciseditable =
352 type == item_type_available_wb_name
353 || type == item_type_available_sheet_name;
355 if (nexpr->is_permanent) {
356 adj_type = item_type_locked_name;
357 ciseditable = FALSE;
358 } else
359 adj_type = type;
361 content = expr_name_as_string (nexpr, &state->pp,
362 sheet_get_conventions (state->sheet));
365 gtk_tree_store_append (state->model, &name_iter,
366 iter);
367 gtk_tree_store_set (state->model, &name_iter,
368 ITEM_NAME, expr_name_name (nexpr),
369 ITEM_NAME_POINTER, nexpr,
370 ITEM_CONTENT, content,
371 ITEM_TYPE, adj_type,
372 ITEM_CONTENT_IS_EDITABLE, ciseditable,
373 ITEM_NAME_IS_EDITABLE, FALSE,
374 ITEM_PASTABLE, ispastable,
375 ITEM_VISIBLE, TRUE,
376 -1);
377 g_free (content);
379 name_guru_set_images (state, &name_iter, adj_type, ispastable);
381 g_list_free (list);
384 static void
385 name_guru_populate_list (NameGuruState *state)
387 GtkTreeIter iter;
389 g_return_if_fail (state != NULL);
390 g_return_if_fail (state->treeview != NULL);
392 gtk_tree_store_clear (state->model);
394 gtk_tree_store_append (state->model, &iter, NULL);
395 gtk_tree_store_set (state->model, &iter,
396 ITEM_NAME, _("Workbook"),
397 ITEM_NAME_POINTER, NULL,
398 ITEM_TYPE, item_type_workbook,
399 ITEM_CONTENT_IS_EDITABLE, FALSE,
400 ITEM_NAME_IS_EDITABLE, FALSE,
401 ITEM_PASTABLE, FALSE,
402 ITEM_VISIBLE, TRUE,
403 -1);
404 name_guru_set_images (state, &iter, item_type_workbook, FALSE);
405 name_guru_store_names (name_guru_get_available_wb_names (state->wb),
406 &iter,
407 state,
408 item_type_available_wb_name);
409 name_guru_expand_at_iter (state, &iter);
411 gtk_tree_store_append (state->model, &iter, NULL);
412 gtk_tree_store_set (state->model, &iter,
413 ITEM_NAME, state->sheet->name_unquoted,
414 ITEM_NAME_POINTER, state->sheet,
415 ITEM_TYPE, item_type_main_sheet,
416 ITEM_CONTENT_IS_EDITABLE, FALSE,
417 ITEM_NAME_IS_EDITABLE, FALSE,
418 ITEM_PASTABLE, FALSE,
419 ITEM_VISIBLE, TRUE,
420 -1);
421 name_guru_set_images (state, &iter, item_type_main_sheet, FALSE);
423 name_guru_store_names (name_guru_get_available_sheet_names
424 (state->sheet),
425 &iter,
426 state,
427 item_type_available_sheet_name);
428 name_guru_expand_at_iter (state, &iter);
430 WORKBOOK_FOREACH_SHEET
431 (state->wb, sheet,
433 if (sheet == state->sheet)
434 continue;
436 gtk_tree_store_append (state->model, &iter, NULL);
437 gtk_tree_store_set (state->model, &iter,
438 ITEM_NAME, sheet->name_unquoted,
439 ITEM_NAME_POINTER, sheet,
440 ITEM_TYPE, item_type_other_sheet,
441 ITEM_CONTENT_IS_EDITABLE, FALSE,
442 ITEM_NAME_IS_EDITABLE, FALSE,
443 ITEM_VISIBLE, TRUE,
444 ITEM_PASTABLE, FALSE,
445 -1);
447 name_guru_store_names
448 (name_guru_get_available_sheet_names (sheet),
449 &iter, state, item_type_foreign_name);
453 static gboolean
454 name_guru_paste (NameGuruState *state, GtkTreeIter *iter)
456 char *name;
457 gboolean is_pastable;
459 gtk_tree_model_get (GTK_TREE_MODEL (state->model),
460 iter,
461 ITEM_PASTABLE, &is_pastable,
462 ITEM_NAME, &name,
463 -1);
465 if (!is_pastable)
466 return FALSE;
468 if (wbcg_edit_start (state->wbcg, FALSE, FALSE)) {
469 GtkEntry *entry;
470 gint position;
471 entry = wbcg_get_entry (state->wbcg);
473 position = gtk_entry_get_text_length (entry);
474 if (position == 0)
475 gtk_editable_insert_text (GTK_EDITABLE (entry), "=",
476 -1, &position);
477 else {
478 gtk_editable_delete_selection (GTK_EDITABLE (entry));
479 position = gtk_editable_get_position
480 (GTK_EDITABLE (entry));
482 if (state->has_pasted) {
483 char sep = go_locale_get_arg_sep ();
484 gtk_editable_insert_text (GTK_EDITABLE (entry), &sep,
485 1, &position);
487 gtk_editable_insert_text (GTK_EDITABLE (entry), name,
488 -1, &position);
489 gtk_editable_set_position (GTK_EDITABLE (entry), position);
492 g_free (name);
494 state->has_pasted = TRUE;
495 return TRUE;
501 static void
502 cb_name_guru_clicked (GtkWidget *button, NameGuruState *state)
504 if (state->dialog == NULL)
505 return;
507 wbcg_set_entry (state->wbcg, NULL);
509 if (button == state->close_button) {
510 gtk_widget_destroy (state->dialog);
511 return;
513 if (button == state->paste_button) {
514 GtkTreeIter iter_f;
515 GtkTreeIter iter;
516 if (gtk_tree_selection_get_selected
517 (gtk_tree_view_get_selection
518 (GTK_TREE_VIEW (state->treeview)), NULL, &iter_f)) {
519 gtk_tree_model_filter_convert_iter_to_child_iter
520 (GTK_TREE_MODEL_FILTER (state->model_f),
521 &iter, &iter_f);
522 if (name_guru_paste (state, &iter))
523 gtk_widget_destroy (state->dialog);
525 return;
529 static GtkWidget *
530 name_guru_init_button (NameGuruState *state, char const *name)
532 GtkWidget *tmp = go_gtk_builder_get_widget (state->gui, name);
534 g_return_val_if_fail (tmp != NULL, NULL);
536 g_signal_connect (G_OBJECT (tmp),
537 "clicked",
538 G_CALLBACK (cb_name_guru_clicked), state);
539 return tmp;
542 static void
543 cb_name_guru_destroy (NameGuruState *state)
545 WorkbookControl *wbc = GNM_WBC (state->wbcg);
547 wb_view_selection_desc (wb_control_view (wbc), TRUE, wbc);
548 g_clear_object (&state->gui);
549 g_clear_object (&state->model);
551 if (!state->is_paste_dialog)
552 wbcg_edit_finish (state->wbcg, WBC_EDIT_REJECT, NULL);
554 g_clear_object (&state->image_paste);
555 g_clear_object (&state->image_add);
556 g_clear_object (&state->image_delete);
557 g_clear_object (&state->image_lock);
558 g_clear_object (&state->image_up);
559 g_clear_object (&state->image_down);
561 state->dialog = NULL;
562 g_free (state);
566 static void
567 cb_name_guru_paste (G_GNUC_UNUSED GtkCellRendererToggle *cell,
568 gchar *path_string,
569 gpointer data)
571 NameGuruState *state = data;
572 GtkTreeIter iter;
574 if (name_guru_translate_pathstring_to_iter
575 (state, &iter, path_string))
576 name_guru_paste (state, &iter);
579 static void
580 name_guru_add (NameGuruState *state, GtkTreeIter *iter, gchar const *path_string)
582 GtkTreeIter name_iter;
583 char *content;
584 GtkTreePath *path;
585 item_type_t type;
587 path = gtk_tree_path_new_from_string (path_string);
589 type = ((gtk_tree_path_get_indices (path))[0] == 0) ?
590 item_type_new_unsaved_wb_name :
591 item_type_new_unsaved_sheet_name;
592 content = selection_to_string (state->sv, FALSE);
594 gtk_tree_store_insert (state->model, &name_iter,
595 iter, 0);
596 gtk_tree_store_set (state->model, &name_iter,
597 ITEM_NAME, _("<new name>"),
598 ITEM_NAME_POINTER, NULL,
599 ITEM_CONTENT,
600 ((content == NULL) ? "#REF!" : content),
601 ITEM_TYPE, type,
602 ITEM_CONTENT_IS_EDITABLE, TRUE,
603 ITEM_NAME_IS_EDITABLE, TRUE,
604 ITEM_PASTABLE, FALSE,
605 ITEM_VISIBLE, TRUE,
606 -1);
607 name_guru_set_images (state, &name_iter, type, FALSE);
608 name_guru_expand_at_iter (state, iter);
609 g_free (content);
612 static void
613 name_guru_delete (NameGuruState *state, GtkTreeIter *iter, item_type_t type)
615 GnmNamedExpr *nexpr;
617 if (type != item_type_new_unsaved_wb_name &&
618 type != item_type_new_unsaved_sheet_name) {
619 gtk_tree_model_get (GTK_TREE_MODEL (state->model),
620 iter,
621 ITEM_NAME_POINTER, &nexpr,
622 -1);
624 if (!name_guru_warn (state, nexpr))
625 return;
627 cmd_remove_name (GNM_WBC (state->wbcg), nexpr);
629 gtk_tree_store_remove (state->model, iter);
633 static void
634 cb_name_guru_add_delete (G_GNUC_UNUSED GtkCellRendererToggle *cell,
635 gchar *path_string,
636 gpointer data)
638 NameGuruState *state = data;
639 GtkTreeIter iter;
641 if (name_guru_translate_pathstring_to_iter
642 (state, &iter, path_string)) {
643 item_type_t type;
645 gtk_tree_model_get (GTK_TREE_MODEL (state->model),
646 &iter,
647 ITEM_TYPE, &type,
648 -1);
650 switch (type) {
651 case item_type_workbook:
652 case item_type_main_sheet:
653 name_guru_add (state, &iter, path_string);
654 break;
655 case item_type_available_wb_name:
656 case item_type_available_sheet_name:
657 case item_type_new_unsaved_wb_name:
658 case item_type_new_unsaved_sheet_name:
659 name_guru_delete (state, &iter, type);
660 break;
661 case item_type_other_sheet:
662 case item_type_locked_name:
663 case item_type_foreign_name:
664 default:
665 break;
670 static void
671 name_guru_find_place (NameGuruState *state, GtkTreeIter *iter,
672 GtkTreeIter *parent_iter, GnmNamedExpr *nexpr)
674 GtkTreeIter next_iter;
675 GnmNamedExpr *next_nexpr;
676 if (nexpr != NULL &&
677 gtk_tree_model_iter_children (GTK_TREE_MODEL (state->model),
678 &next_iter,
679 parent_iter)) {
680 do {
681 gtk_tree_model_get (GTK_TREE_MODEL (state->model),
682 &next_iter,
683 ITEM_NAME_POINTER, &next_nexpr,
684 -1);
685 if (next_nexpr != NULL &&
686 expr_name_cmp_by_name (nexpr, next_nexpr) < 0) {
687 gtk_tree_store_insert_before
688 (state->model,
689 iter,
690 parent_iter,
691 &next_iter);
692 return;
694 } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (state->model),
695 &next_iter));
697 gtk_tree_store_append (state->model, iter,
698 parent_iter);
699 } else {
700 gtk_tree_store_prepend (state->model, iter,
701 parent_iter);
706 static void
707 name_guru_move_record (NameGuruState *state, GtkTreeIter *from_iter,
708 GtkTreeIter *new_parent_iter, item_type_t new_type)
710 GnmNamedExpr *nexpr;
711 gchar *name, *content;
712 gboolean ceditable, neditable, pastable, visible;
713 GtkTreeIter new_iter;
715 gtk_tree_model_get (GTK_TREE_MODEL (state->model),
716 from_iter,
717 ITEM_NAME, &name,
718 ITEM_NAME_POINTER, &nexpr,
719 ITEM_CONTENT, &content,
720 ITEM_CONTENT_IS_EDITABLE, &ceditable,
721 ITEM_NAME_IS_EDITABLE, &neditable,
722 ITEM_PASTABLE, &pastable,
723 ITEM_VISIBLE, &visible,
724 -1);
726 gtk_tree_store_remove (state->model, from_iter);
728 name_guru_find_place (state, &new_iter, new_parent_iter, nexpr);
730 gtk_tree_store_set (state->model, &new_iter,
731 ITEM_NAME, name,
732 ITEM_NAME_POINTER, nexpr,
733 ITEM_CONTENT, content,
734 ITEM_TYPE, new_type,
735 ITEM_CONTENT_IS_EDITABLE, ceditable,
736 ITEM_NAME_IS_EDITABLE, neditable,
737 ITEM_PASTABLE, pastable,
738 ITEM_VISIBLE, visible,
739 -1);
740 name_guru_set_images (state, &new_iter, new_type, pastable);
741 name_guru_expand_at_iter (state, &new_iter);
742 g_free (name);
743 g_free (content);
746 static void
747 cb_name_guru_switch_scope (G_GNUC_UNUSED GtkCellRendererToggle *cell,
748 gchar *path_string,
749 gpointer data)
751 NameGuruState *state = data;
752 GtkTreeIter iter;
754 if (name_guru_translate_pathstring_to_iter
755 (state, &iter, path_string)) {
756 item_type_t type, new_type;
757 gchar const *new_path;
758 GnmNamedExpr *nexpr;
759 GtkTreeIter new_parent_iter;
761 gtk_tree_model_get (GTK_TREE_MODEL (state->model),
762 &iter,
763 ITEM_TYPE, &type,
764 ITEM_NAME_POINTER, &nexpr,
765 -1);
767 switch (type) {
768 case item_type_available_wb_name:
769 if (cmd_rescope_name
770 (GNM_WBC (state->wbcg),
771 nexpr, state->sheet))
772 return;
773 new_path = "1";
774 new_type = item_type_available_sheet_name;
775 break;
776 case item_type_new_unsaved_wb_name:
777 new_path = "1";
778 new_type = item_type_new_unsaved_sheet_name;
779 break;
780 case item_type_available_sheet_name:
781 if (cmd_rescope_name
782 (GNM_WBC (state->wbcg),
783 nexpr, NULL))
784 return;
785 new_path = "0";
786 new_type = item_type_available_wb_name;
787 break;
788 case item_type_new_unsaved_sheet_name:
789 new_path = "0";
790 new_type = item_type_new_unsaved_wb_name;
791 break;
792 case item_type_workbook:
793 case item_type_main_sheet:
794 case item_type_other_sheet:
795 case item_type_locked_name:
796 case item_type_foreign_name:
797 default:
798 return;
801 if (gtk_tree_model_get_iter_from_string
802 (GTK_TREE_MODEL (state->model),
803 &new_parent_iter, new_path)) {
804 name_guru_move_record
805 (state, &iter, &new_parent_iter, new_type);
810 static gboolean
811 name_guru_parse_pos_init (NameGuruState *state,
812 GnmParsePos *pp, item_type_t type)
814 switch (type) {
815 case item_type_available_wb_name:
816 case item_type_new_unsaved_wb_name:
817 parse_pos_init (pp, state->wb, NULL,
818 state->pp.eval.col, state->pp.eval.row);
819 return TRUE;
820 case item_type_available_sheet_name:
821 case item_type_new_unsaved_sheet_name:
822 parse_pos_init (pp, state->wb, state->sheet,
823 state->pp.eval.col, state->pp.eval.row);
824 return TRUE;
825 case item_type_workbook:
826 case item_type_main_sheet:
827 case item_type_other_sheet:
828 case item_type_locked_name:
829 case item_type_foreign_name:
830 default:
831 return FALSE;
836 * Return the expression if it is acceptable.
837 * The parse position will be initialized then.
840 static GnmExprTop const*
841 name_guru_check_expression (NameGuruState *state, gchar *text,
842 GnmParsePos *pp, item_type_t type)
844 GnmExprTop const *texpr;
845 GnmParseError perr;
847 if (!name_guru_parse_pos_init (state, pp, type))
848 return NULL; /* We should have never gotten here. */
850 if (text == NULL || text[0] == '\0') {
851 go_gtk_notice_dialog (GTK_WINDOW (state->dialog),
852 GTK_MESSAGE_ERROR,
853 _("Why would you want to define a "
854 "name for the empty string?"));
855 return NULL;
858 texpr = gnm_expr_parse_str (text, pp,
859 GNM_EXPR_PARSE_DEFAULT |
860 GNM_EXPR_PARSE_UNKNOWN_NAMES_ARE_INVALID |
861 GNM_EXPR_PARSE_PERMIT_MULTIPLE_EXPRESSIONS,
862 NULL,
863 parse_error_init (&perr));
864 if (texpr == NULL) {
865 if (perr.err == NULL)
866 return NULL;
868 go_gtk_notice_dialog (GTK_WINDOW (state->dialog),
869 GTK_MESSAGE_ERROR,
870 "%s", perr.err->message);
871 parse_error_free (&perr);
872 return NULL;
874 /* don't allow user to define a nexpr that looks like a placeholder *
875 * because it will be would disappear from the lists. */
876 if (gnm_expr_top_is_err (texpr, GNM_ERROR_NAME)) {
877 go_gtk_notice_dialog (GTK_WINDOW (state->dialog), GTK_MESSAGE_ERROR,
878 _("Why would you want to define a name to be #NAME?"));
879 parse_error_free (&perr);
880 gnm_expr_top_unref (texpr);
881 return NULL;
884 return texpr;
888 static void
889 cb_name_guru_content_edited
890 (G_GNUC_UNUSED GnumericCellRendererExprEntry *cell,
891 gchar *path_string,
892 gchar *new_text,
893 NameGuruState *state)
895 GtkTreeIter iter;
896 item_type_t type;
897 GnmParsePos pp;
898 GnmExprTop const *texpr;
899 GnmNamedExpr *nexpr;
901 if (!name_guru_translate_pathstring_to_iter
902 (state, &iter, path_string))
903 return;
905 gtk_tree_model_get (GTK_TREE_MODEL (state->model), &iter,
906 ITEM_TYPE, &type,
907 ITEM_NAME_POINTER, &nexpr,
908 -1);
910 /* check whether the content is valid */
913 texpr = name_guru_check_expression (state, new_text, &pp , type);
914 if (texpr == NULL)
915 return;
917 /* content is valid */
919 if (type != item_type_new_unsaved_wb_name
920 && type != item_type_new_unsaved_sheet_name) {
921 /* save the changes (if the name is already saved) */
922 cmd_define_name (GNM_WBC (state->wbcg),
923 expr_name_name (nexpr),
924 &pp, texpr, NULL);
925 } else
926 gnm_expr_top_unref (texpr);
928 /* set the model */
929 gtk_tree_store_set (state->model, &iter, ITEM_CONTENT, new_text, -1);
932 static void
933 cb_name_guru_name_edited (G_GNUC_UNUSED GtkCellRendererText *cell,
934 gchar *path_string,
935 gchar *new_text,
936 NameGuruState *state)
938 GtkTreeIter iter;
939 GtkTreeIter parent_iter;
940 item_type_t type;
941 GnmParsePos pp;
942 GnmExprTop const *texpr;
943 gchar *content;
944 GnmNamedExpr *nexpr;
946 g_return_if_fail (new_text != NULL);
948 if (!name_guru_translate_pathstring_to_iter
949 (state, &iter, path_string))
950 return;
952 gtk_tree_model_get (GTK_TREE_MODEL (state->model), &iter,
953 ITEM_TYPE, &type,
954 ITEM_CONTENT, &content,
955 -1);
957 if (type != item_type_new_unsaved_wb_name &&
958 type != item_type_new_unsaved_sheet_name)
959 return;
961 name_guru_parse_pos_init (state, &pp, type);
962 nexpr = expr_name_lookup (&pp, new_text);
964 if (nexpr != NULL && !nexpr->is_placeholder) {
965 Sheet *scope = nexpr->pos.sheet;
966 if ((type == item_type_new_unsaved_wb_name &&
967 scope == NULL) ||
968 (type == item_type_new_unsaved_sheet_name)) {
969 go_gtk_notice_dialog
970 (GTK_WINDOW (state->dialog),
971 GTK_MESSAGE_ERROR,
972 _("This name is already in use!"));
973 return;
977 texpr = name_guru_check_expression (state, content, &pp , type);
978 if (texpr == NULL)
979 return;
981 if (!cmd_define_name (GNM_WBC (state->wbcg),
982 new_text, &pp,
983 texpr, NULL)) {
984 nexpr = expr_name_lookup (&pp, new_text);
986 type = (type == item_type_new_unsaved_wb_name) ?
987 item_type_available_wb_name :
988 item_type_available_sheet_name;
990 gtk_tree_store_set
991 (state->model, &iter,
992 ITEM_NAME, new_text,
993 ITEM_NAME_POINTER, nexpr,
994 ITEM_TYPE, type,
995 ITEM_PASTABLE, TRUE,
996 ITEM_NAME_IS_EDITABLE, FALSE,
997 -1);
998 name_guru_set_images (state, &iter, type, TRUE);
1000 if (gtk_tree_model_iter_parent (GTK_TREE_MODEL (state->model),
1001 &parent_iter, &iter))
1002 name_guru_move_record (state, &iter, &parent_iter, type);
1006 static void
1007 name_guru_update_sensitivity (GtkTreeSelection *treeselection,
1008 gpointer user_data)
1010 NameGuruState *state = user_data;
1011 gboolean is_pastable = FALSE;
1012 GtkTreeIter iter;
1014 if (gtk_tree_selection_get_selected
1015 (treeselection, NULL, &iter))
1016 gtk_tree_model_get (state->model_f, &iter,
1017 ITEM_PASTABLE, &is_pastable,
1018 -1);
1019 gtk_widget_set_sensitive (GTK_WIDGET (state->paste_button),
1020 is_pastable);
1024 static gboolean
1025 cb_name_guru_selection_function (G_GNUC_UNUSED GtkTreeSelection *selection,
1026 GtkTreeModel *model,
1027 GtkTreePath *path,
1028 gboolean path_currently_selected,
1029 G_GNUC_UNUSED gpointer data)
1031 GtkTreeIter iter;
1033 if (path_currently_selected)
1034 return TRUE;
1035 if (gtk_tree_model_get_iter (model, &iter, path)) {
1036 gboolean is_pastable, is_editable;
1037 gtk_tree_model_get (model,
1038 &iter,
1039 ITEM_PASTABLE, &is_pastable,
1040 ITEM_CONTENT_IS_EDITABLE, &is_editable,
1041 -1);
1042 return (is_pastable || is_editable);
1044 return FALSE;
1047 static gboolean
1048 name_guru_init (NameGuruState *state, WBCGtk *wbcg, gboolean is_paste_dialog)
1050 Workbook *wb = wb_control_get_workbook (GNM_WBC (wbcg));
1051 GtkTreeViewColumn *column;
1052 GtkCellRenderer *renderer;
1053 GtkTreeSelection *selection;
1054 GtkWidget *widget = GTK_WIDGET (wbcg_toplevel (wbcg));
1056 state->is_paste_dialog = is_paste_dialog;
1057 state->has_pasted = FALSE;
1059 state->gui = gnm_gtk_builder_load ("res:ui/define-name.ui", NULL,
1060 GO_CMD_CONTEXT (wbcg));
1061 if (state->gui == NULL)
1062 return TRUE;
1064 state->wbcg = wbcg;
1065 state->wb = wb;
1066 state->sv = wb_control_cur_sheet_view (GNM_WBC (wbcg));
1067 state->sheet = sv_sheet (state->sv);
1068 parse_pos_init_editpos (&state->pp, state->sv);
1070 state->dialog = go_gtk_builder_get_widget (state->gui, "NameGuru");
1072 gnm_dialog_setup_destroy_handlers (GTK_DIALOG (state->dialog),
1073 state->wbcg,
1074 GNM_DIALOG_DESTROY_SHEET_REMOVED);
1075 state->model = gtk_tree_store_new
1076 (NUM_COLUMNS,
1077 G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_STRING,
1078 G_TYPE_INT, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
1079 GDK_TYPE_PIXBUF, GDK_TYPE_PIXBUF,
1080 G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
1081 GDK_TYPE_PIXBUF, G_TYPE_BOOLEAN);
1083 state->treeview = go_gtk_builder_get_widget (state->gui, "name_list");
1085 state->model_f = gtk_tree_model_filter_new
1086 (GTK_TREE_MODEL (state->model), NULL);
1087 gtk_tree_model_filter_set_visible_column
1088 (GTK_TREE_MODEL_FILTER (state->model_f), ITEM_VISIBLE);
1090 gtk_tree_view_set_model (GTK_TREE_VIEW (state->treeview),
1091 state->model_f);
1092 g_object_unref (state->model_f);
1093 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (state->treeview),
1094 FALSE);
1095 gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (state->treeview),
1096 GTK_TREE_VIEW_GRID_LINES_NONE);
1097 gtk_tree_view_set_reorderable (GTK_TREE_VIEW (state->treeview),
1098 FALSE);
1100 renderer = gtk_cell_renderer_text_new ();
1101 g_signal_connect (G_OBJECT (renderer), "edited",
1102 G_CALLBACK (cb_name_guru_name_edited), state);
1103 column = gtk_tree_view_column_new_with_attributes
1104 ("name",
1105 renderer,
1106 "text", ITEM_NAME,
1107 "editable", ITEM_NAME_IS_EDITABLE,
1108 NULL);
1109 gtk_tree_view_append_column (GTK_TREE_VIEW (state->treeview), column);
1111 if (is_paste_dialog) {
1112 renderer = gnumeric_cell_renderer_toggle_new ();
1113 g_signal_connect (G_OBJECT (renderer),
1114 "toggled",
1115 G_CALLBACK (cb_name_guru_paste), state);
1116 column = gtk_tree_view_column_new_with_attributes
1117 ("Paste",
1118 renderer,
1119 "active", ITEM_PASTABLE,
1120 "pixbuf", ITEM_PASTE_IMAGE,
1121 NULL);
1122 gtk_tree_view_append_column (GTK_TREE_VIEW (state->treeview),
1123 column);
1124 } else {
1125 renderer = gnumeric_cell_renderer_toggle_new ();
1126 g_signal_connect (G_OBJECT (renderer),
1127 "toggled",
1128 G_CALLBACK (cb_name_guru_add_delete), state);
1129 column = gtk_tree_view_column_new_with_attributes
1130 ("Lock",
1131 renderer,
1132 "active", ITEM_ADDDELETE_ACTIVE,
1133 "pixbuf", ITEM_ADDDELETE_IMAGE,
1134 NULL);
1135 gtk_tree_view_append_column (GTK_TREE_VIEW (state->treeview),
1136 column);
1138 renderer = gnumeric_cell_renderer_toggle_new ();
1139 g_signal_connect (G_OBJECT (renderer),
1140 "toggled",
1141 G_CALLBACK (cb_name_guru_switch_scope),
1142 state);
1143 column = gtk_tree_view_column_new_with_attributes
1144 ("Scope",
1145 renderer,
1146 "active", ITEM_UPDOWN_ACTIVE,
1147 "pixbuf", ITEM_UPDOWN_IMAGE,
1148 NULL);
1149 gtk_tree_view_append_column (GTK_TREE_VIEW (state->treeview),
1150 column);
1153 renderer = gnumeric_cell_renderer_expr_entry_new (state->wbcg);
1154 g_signal_connect (G_OBJECT (renderer), "edited",
1155 G_CALLBACK (cb_name_guru_content_edited), state);
1156 column = gtk_tree_view_column_new_with_attributes
1157 (_("content"),
1158 renderer,
1159 "text", ITEM_CONTENT,
1160 "editable", ITEM_CONTENT_IS_EDITABLE,
1161 NULL);
1162 gtk_tree_view_append_column (GTK_TREE_VIEW (state->treeview), column);
1165 selection = gtk_tree_view_get_selection
1166 (GTK_TREE_VIEW (state->treeview));
1167 gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
1168 gtk_tree_selection_set_select_function
1169 (selection, cb_name_guru_selection_function,
1170 state, NULL);
1172 state->close_button = name_guru_init_button (state, "close_button");
1173 state->paste_button = name_guru_init_button (state, "paste_button");
1175 if (is_paste_dialog) {
1176 g_signal_connect (G_OBJECT (selection),
1177 "changed",
1178 G_CALLBACK (name_guru_update_sensitivity),
1179 state);
1180 state->image_paste = go_gtk_widget_render_icon_pixbuf (widget, "edit-paste", GTK_ICON_SIZE_MENU);
1181 state->image_add = NULL;
1182 state->image_delete = NULL;
1183 state->image_lock = NULL;
1184 state->image_up = NULL;
1185 state->image_down = NULL;
1186 } else {
1187 state->image_paste = NULL;
1188 state->image_add = go_gtk_widget_render_icon_pixbuf (widget, "list-add", GTK_ICON_SIZE_MENU);
1189 state->image_delete = go_gtk_widget_render_icon_pixbuf (widget, "list-remove", GTK_ICON_SIZE_MENU);
1190 state->image_lock = go_gtk_widget_render_icon_pixbuf (widget, "gnumeric-protection-yes", GTK_ICON_SIZE_MENU);
1191 state->image_up = go_gtk_widget_render_icon_pixbuf (widget, "go-up", GTK_ICON_SIZE_MENU);
1192 state->image_down = go_gtk_widget_render_icon_pixbuf (widget, "go-down", GTK_ICON_SIZE_MENU);
1195 state->search_entry = go_gtk_builder_get_widget (state->gui, "search_entry");
1197 g_signal_connect (G_OBJECT (state->search_entry),
1198 "icon-press",
1199 G_CALLBACK (name_guru_erase_search_entry),
1200 state);
1202 g_signal_connect (G_OBJECT (state->search_entry),
1203 "activate",
1204 G_CALLBACK (name_guru_search),
1205 state);
1207 name_guru_populate_list (state);
1208 name_guru_update_sensitivity (selection, state);
1210 gnm_init_help_button (
1211 go_gtk_builder_get_widget (state->gui, "help_button"),
1212 is_paste_dialog ? GNUMERIC_HELP_LINK_PASTE_NAMES
1213 : GNUMERIC_HELP_LINK_DEFINE_NAMES);
1215 /* a candidate for merging into attach guru */
1216 gnm_keyed_dialog (state->wbcg, GTK_WINDOW (state->dialog),
1217 is_paste_dialog ? PASTE_NAMES_KEY
1218 : DEFINE_NAMES_KEY);
1219 go_gtk_nonmodal_dialog (wbcg_toplevel (state->wbcg),
1220 GTK_WINDOW (state->dialog));
1222 g_object_set_data_full (G_OBJECT (state->dialog),
1223 "state", state, (GDestroyNotify)cb_name_guru_destroy);
1225 if (is_paste_dialog) {
1226 gtk_window_set_title (GTK_WINDOW (state->dialog),
1227 _("Paste Defined Names"));
1228 gtk_widget_show_all (GTK_WIDGET (state->dialog));
1229 } else {
1230 wbc_gtk_attach_guru (state->wbcg, state->dialog);
1231 gtk_widget_show (GTK_WIDGET (state->dialog));
1234 return FALSE;
1238 * dialog_define_names:
1239 * @wbcg:
1241 * Create and show the define names dialog.
1243 void
1244 dialog_define_names (WBCGtk *wbcg)
1246 NameGuruState *state;
1248 g_return_if_fail (wbcg != NULL);
1250 /* Only one guru per workbook. */
1251 if (wbc_gtk_get_guru (wbcg))
1252 return;
1254 /* Only pop up one copy per workbook */
1255 if (gnm_dialog_raise_if_exists (wbcg, DEFINE_NAMES_KEY))
1256 return;
1258 state = g_new0 (NameGuruState, 1);
1259 if (name_guru_init (state, wbcg, FALSE)) {
1260 go_gtk_notice_dialog (wbcg_toplevel (wbcg), GTK_MESSAGE_ERROR,
1261 _("Could not create the Name Guru."));
1262 g_free (state);
1263 return;
1268 * dialog_define_names:
1269 * @wbcg:
1271 * Create and show the define names dialog.
1273 void
1274 dialog_paste_names (WBCGtk *wbcg)
1276 NameGuruState *state;
1278 g_return_if_fail (wbcg != NULL);
1280 /* Only one guru per workbook. */
1281 if (wbc_gtk_get_guru (wbcg))
1282 return;
1284 /* Only pop up one copy per workbook */
1285 if (gnm_dialog_raise_if_exists (wbcg, PASTE_NAMES_KEY))
1286 return;
1288 state = g_new0 (NameGuruState, 1);
1289 if (name_guru_init (state, wbcg, TRUE)) {
1290 go_gtk_notice_dialog (wbcg_toplevel (wbcg), GTK_MESSAGE_ERROR,
1291 _("Could not create the Name Guru."));
1292 g_free (state);
1293 return;