GitLab repository storage has been migrated to hashed layout. Please contact Infrastructure team if you notice any issues with repositories or hooks.

dialog-define-names.c 15.1 KB
Newer Older
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
Jody Goldberg's avatar
Jody Goldberg committed
2 3 4
/* vim: set sw=8: */
/*
 * dialog-define-name.c: Edit named regions.
Miguel de Icaza's avatar
Miguel de Icaza committed
5 6
 *
 * Author:
Jody Goldberg's avatar
Jody Goldberg committed
7
 *	Jody Goldberg <jody@gnome.org>
Jody Goldberg's avatar
Jody Goldberg committed
8
 *	Michael Meeks <michael@imaginator.com>
9
 *	Chema Celorio <chema@celorio.com>
Jody Goldberg's avatar
Jody Goldberg committed
10
 */
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
#include <gnumeric-config.h>
#include <gnumeric.h>
#include "dialogs.h"

#include <expr.h>
#include <str.h>
#include <expr-name.h>
#include <sheet.h>
#include <workbook.h>
#include <workbook-control.h>
#include <workbook-edit.h>
#include <gui-util.h>
#include <parse-util.h>
#include <widgets/gnumeric-expr-entry.h>

26
#include <libgnome/gnome-i18n.h>
Michael Meeks's avatar
Michael Meeks committed
27
#include <glade/glade.h>
Miguel de Icaza's avatar
Miguel de Icaza committed
28

29
#define LIST_KEY "name_list_data"
30
#define DEFINE_NAMES_KEY "define-names-dialog"
31

Michael Meeks's avatar
Michael Meeks committed
32 33
typedef struct {
	GladeXML  *gui;
34
	GtkWidget *dialog;
35 36
	GtkList   *list;
	GtkEntry  *name;
37
	GnumericExprEntry *expr_text;
38
	GtkToggleButton *sheet_scope;
39
	GtkToggleButton *wb_scope;
Michael Meeks's avatar
Michael Meeks committed
40
	GList     *expr_names;
41
	NamedExpression *cur_name;
Jody Goldberg's avatar
Jody Goldberg committed
42

43 44 45 46
	GtkWidget *ok_button;
	GtkWidget *add_button;
	GtkWidget *close_button;
	GtkWidget *delete_button;
47
	GtkWidget *update_button;
48

49
	Sheet	  *sheet;
50 51
	Workbook  *wb;
	WorkbookControlGUI  *wbcg;
52
	ParsePos   pp;
53 54

	gboolean updating;
55
} NameGuruState;
Michael Meeks's avatar
Michael Meeks committed
56

57 58 59 60 61 62
static gboolean
name_guru_scope_is_sheet (NameGuruState *state)
{
	return gtk_toggle_button_get_active (state->sheet_scope);
}

63 64
/**
 * name_guru_warned_if_used:
65 66
 * @state:
 *
67 68
 * If the expresion that is about to be deleted is beeing used,
 * warn the user about it. Ask if we should procede or not
69
 *
70 71 72 73 74 75 76
 * Return Value: TRUE if users confirms deletion, FALSE otherwise
 **/
static gboolean
name_guru_warn (NameGuruState *state)
{
	return TRUE;
}
77

78
static void
79
cb_scope_changed (GtkToggleButton *button, NameGuruState *state)
80
{
81
	if (state->updating || state->cur_name == NULL)
82
		return;
83 84
	expr_name_set_scope (state->cur_name,
		name_guru_scope_is_sheet (state) ? state->sheet : NULL);
85 86 87
}

static void
88
name_guru_display_scope (NameGuruState *state)
89
{
90
	NamedExpression const *nexpr = state->cur_name;
91

92
	state->updating = TRUE;
93 94 95 96
	if (nexpr == NULL || nexpr->pos.sheet == NULL)
		gtk_toggle_button_set_active (state->wb_scope, TRUE);
	else
		gtk_toggle_button_set_active (state->sheet_scope, TRUE);
97
	state->updating = FALSE;
98 99
}

100 101 102 103
static void cb_name_guru_select_name (GtkWidget *list, NameGuruState *state);

/**
 * name_guru_set_expr:
104
 * @state:
105
 * @expr_name: Expression to set in the entries, NULL to clear entries
106
 *
107 108 109 110 111 112 113
 * Set the entries in the dialog from an NamedExpression
 **/
static void
name_guru_set_expr (NameGuruState *state, NamedExpression *expr_name)
{
	state->updating = TRUE;
	if (expr_name) {
114 115
		gchar *txt;

116 117 118
		/* Display the name */
		gtk_entry_set_text (state->name, expr_name->name->str);

Jody Goldberg's avatar
Jody Goldberg committed
119
		/* Display the expr_text */
120
		txt = expr_name_as_string (expr_name, &state->pp);
121
		gtk_entry_set_text (GTK_ENTRY (state->expr_text), txt);
122 123 124
		g_free (txt);
	} else {
		gtk_entry_set_text (state->name, "");
125
		gtk_entry_set_text (GTK_ENTRY (state->expr_text), "");
126 127 128
	}
	state->updating = FALSE;

129
	name_guru_display_scope (state);
130 131 132 133
}

/**
 * name_guru_clear_selection:
134 135
 * @state:
 *
136 137 138 139 140 141 142 143 144 145 146 147 148 149
 * Clear the selection of the gtklist
 **/
static void
name_guru_clear_selection (NameGuruState *state)
{
	g_return_if_fail (state != NULL);

	state->updating = TRUE;
	gtk_list_unselect_all (state->list);
	state->updating = FALSE;
}

/**
 * name_guru_in_list:
150
 * @name:
151
 * @state:
152
 *
153
 * Given a name, it searches for it inside the list of Names
154
 *
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
 * Return Value: TRUE if name is already defined, FALSE otherwise
 **/
static gboolean
name_guru_in_list (const gchar *name, NameGuruState *state)
{
	NamedExpression *expression;
	GList *list;

	g_return_val_if_fail (name != NULL, FALSE);
	g_return_val_if_fail (state != NULL, FALSE);

	for (list = state->expr_names; list; list = list->next) {
		expression = (NamedExpression *) list->data;
		g_return_val_if_fail (expression != NULL, FALSE);
		g_return_val_if_fail (expression->name != NULL, FALSE);
		g_return_val_if_fail (expression->name->str != NULL, FALSE);
		if (strcasecmp (name, expression->name->str) == 0) {
			return TRUE;
		}
	}

	return FALSE;
}



/**
 * name_guru_update_sensitivity:
183 184 185
 * @state:
 * @update_entries:
 *
186 187 188 189 190 191 192 193 194
 * Update the dialog widgets sensitivity
 **/
static void
name_guru_update_sensitivity (NameGuruState *state, gboolean update_entries)
{
	gboolean selection;
	gboolean update;
	gboolean add;
	gboolean in_list = FALSE;
Jody Goldberg's avatar
Jody Goldberg committed
195
	char const *name;
196
	char const *expr_text;
197 198 199 200 201

	g_return_if_fail (state->list != NULL);

	if (state->updating)
		return;
202

203
	name  = gtk_entry_get_text (state->name);
204
	expr_text = gtk_entry_get_text (GTK_ENTRY (state->expr_text));
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228

	/** Add is active if :
	 *  - We have a name in the entry to add
	 *  - Either we don't have a current Name or if we have a current
	 *     name, the name is different than what we are going to add
	 **/
	add = (name != NULL &&
	       name[0] != '\0' &&
	       !(in_list = name_guru_in_list (name, state)));
	selection = (g_list_length (state->list->selection) > 0);
	update = (name && *name && !add);

	gtk_widget_set_sensitive (state->delete_button, selection && in_list);
	gtk_widget_set_sensitive (state->add_button,    add);
	gtk_widget_set_sensitive (state->update_button, update);

	if (!selection && update_entries)
		name_guru_set_expr (state, NULL);

	if (selection && !in_list)
		name_guru_clear_selection (state);
}

/**
229
 * cb_name_guru_update_sensitivity:
230 231 232
 * @dummy:
 * @state:
 *
233 234
 **/
static void
235
cb_name_guru_update_sensitivity (GtkWidget *dummy, NameGuruState *state)
236 237 238 239 240 241
{
	name_guru_update_sensitivity (state, FALSE);
}

/**
 * cb_name_guru_select_name:
242 243 244
 * @list:
 * @state:
 *
245 246
 * Set the expression from the selected row in the gtklist
 **/
247
static void
Jody Goldberg's avatar
Jody Goldberg committed
248
cb_name_guru_select_name (GtkWidget *list, NameGuruState *state)
249
{
Jody Goldberg's avatar
Jody Goldberg committed
250
	NamedExpression *expr_name;
Morten Welinder's avatar
Morten Welinder committed
251
	GList *sel = GTK_LIST (list)->selection;
252

253
	if (sel == NULL || state->updating)
254 255 256 257
		return;

	g_return_if_fail (sel->data != NULL);

Jody Goldberg's avatar
Jody Goldberg committed
258
	expr_name = gtk_object_get_data (GTK_OBJECT (sel->data), LIST_KEY);
259

260
	g_return_if_fail (expr_name != NULL);
Jody Goldberg's avatar
Jody Goldberg committed
261 262 263
	g_return_if_fail (expr_name->name != NULL);
	g_return_if_fail (expr_name->name->str != NULL);

264 265
	state->cur_name = expr_name;

266 267
	name_guru_set_expr (state, expr_name);
	name_guru_update_sensitivity (state, FALSE);
Michael Meeks's avatar
Michael Meeks committed
268 269
}

270

Michael Meeks's avatar
Michael Meeks committed
271
static void
Jody Goldberg's avatar
Jody Goldberg committed
272
name_guru_populate_list (NameGuruState *state)
Michael Meeks's avatar
Michael Meeks committed
273 274
{
	GList *names;
Jody Goldberg's avatar
Jody Goldberg committed
275
	GtkContainer *list;
Michael Meeks's avatar
Michael Meeks committed
276

277 278
	g_return_if_fail (state != NULL);
	g_return_if_fail (state->list != NULL);
Michael Meeks's avatar
Michael Meeks committed
279

280
	state->cur_name = NULL;
Michael Meeks's avatar
Michael Meeks committed
281

282 283
	gtk_list_clear_items (state->list, 0, -1);

Morten Welinder's avatar
Morten Welinder committed
284
	g_list_free (state->expr_names);
285
	state->expr_names = sheet_get_available_names (state->sheet);
286

287
	list = GTK_CONTAINER (state->list);
288
	for (names = state->expr_names ; names != NULL ; names = g_list_next (names)) {
289
		NamedExpression *expr_name = names->data;
290
		GtkWidget *li;
291
		if (expr_name->pos.sheet != NULL) {
292
			char *name = g_strdup_printf ("%s!%s",
293
						      expr_name->pos.sheet->name_unquoted,
294 295 296 297 298
						      expr_name->name->str);
			li = gtk_list_item_new_with_label (name);
			g_free (name);
		} else
			li = gtk_list_item_new_with_label (expr_name->name->str);
299
		gtk_object_set_data (GTK_OBJECT (li), LIST_KEY, expr_name);
Jody Goldberg's avatar
Jody Goldberg committed
300
		gtk_container_add (list, li);
Michael Meeks's avatar
Michael Meeks committed
301
	}
Jody Goldberg's avatar
Jody Goldberg committed
302
	gtk_widget_show_all (GTK_WIDGET (state->list));
303
	name_guru_update_sensitivity (state, TRUE);
304 305
}

306
/**
307
 * name_guru_remove:
308 309 310
 * @ignored:
 * @state:
 *
311 312
 * Remove the state->cur_name
 **/
313
static void
314
name_guru_remove (GtkWidget *ignored, NameGuruState *state)
Michael Meeks's avatar
Michael Meeks committed
315
{
316
	g_return_if_fail (state != NULL);
317
	g_return_if_fail (state->cur_name != NULL);
318

319 320
	if (!name_guru_warn (state))
		return;
321

322 323 324
	state->expr_names = g_list_remove (state->expr_names, state->cur_name);
	expr_name_remove (state->cur_name);
	state->cur_name = NULL;
325

326
	name_guru_populate_list (state);
327 328
}

329
/**
330
 * name_guru_add:
331 332
 * @state:
 *
333
 * Update or add a NamedExpression from the values in the gtkentries.
334
 *
335 336
 * Return Value: FALSE if the expression was invalid, TRUE otherwise
 **/
337
static gboolean
338
name_guru_add (NameGuruState *state)
339
{
340
	NamedExpression *expr_name;
341
	ParseError    perr;
342
	ExprTree *expr;
Jody Goldberg's avatar
Jody Goldberg committed
343
	char const *name, *expr_text, *tmp;
344
	gboolean dirty = FALSE;
345 346 347

	g_return_val_if_fail (state != NULL, FALSE);

348
	expr_text = gtk_entry_get_text (GTK_ENTRY (state->expr_text));
349 350 351 352
	name  = gtk_entry_get_text (state->name);

	if (!name || (name[0] == '\0'))
		return TRUE;
353

354
	expr_name = expr_name_lookup (&state->pp, name);
Jody Goldberg's avatar
Jody Goldberg committed
355

356
	/* strip off optional preceding '=' */
Jody Goldberg's avatar
Jody Goldberg committed
357 358
	if (NULL != (tmp = gnumeric_char_start_expr_p (expr_text)))
		expr_text = tmp;
359
	expr = expr_parse_str (expr_text, &state->pp,
360
		GNM_PARSER_DEFAULT, NULL, parse_error_init (&perr));
Jody Goldberg's avatar
Jody Goldberg committed
361

Jody Goldberg's avatar
Jody Goldberg committed
362 363
	/* If the expression is invalid */
	if (expr == NULL) {
364
		gnumeric_notice (state->wbcg, GNOME_MESSAGE_BOX_ERROR, perr.message);
Jody Goldberg's avatar
Jody Goldberg committed
365
		gtk_widget_grab_focus (GTK_WIDGET (state->expr_text));
366
		parse_error_free (&perr);
Jody Goldberg's avatar
Jody Goldberg committed
367 368
		return FALSE;
	} else if (expr_name) {
Jody Goldberg's avatar
Jody Goldberg committed
369
		if (!expr_name->builtin) {
370
			/* This means that the expresion was updated.
371
			 * FIXME: if the scope has been changed too, call scope
372
			 * chaned first.
373
			 */
374 375
			expr_name_set_expr (expr_name, expr);
			dirty = TRUE;
376
		} else
377
			gnumeric_notice (state->wbcg, GNOME_MESSAGE_BOX_ERROR,
Morten Welinder's avatar
Morten Welinder committed
378
					 _("You cannot redefine a builtin name."));
379
	} else {
380
		char const *error = NULL;
381 382 383 384 385
		ParsePos pos;
		if (name_guru_scope_is_sheet (state))
			parse_pos_init (&pos, NULL, state->sheet,
					state->pp.eval.col,
					state->pp.eval.row);
386
		else
387 388 389
			parse_pos_init (&pos, state->wb, NULL,
					state->pp.eval.col,
					state->pp.eval.row);
390

391
		expr_name = expr_name_add (&pos, name, expr, &error);
392 393 394 395 396 397
		if (expr_name == NULL) {
			g_return_val_if_fail (error != NULL, FALSE);
			gnumeric_notice (state->wbcg, GNOME_MESSAGE_BOX_ERROR, error);
			gtk_widget_grab_focus (GTK_WIDGET (state->expr_text));
			return FALSE;
		}
398
		dirty = TRUE;
399
	}
400

Jody Goldberg's avatar
Jody Goldberg committed
401
	g_return_val_if_fail (expr_name != NULL, FALSE);
402

Jody Goldberg's avatar
Jody Goldberg committed
403
	name_guru_populate_list (state);
404
	gtk_widget_grab_focus (GTK_WIDGET (state->name));
405

406 407 408
	if (dirty)
		sheet_set_dirty (state->sheet, TRUE);

409 410 411 412
	return TRUE;
}

static void
413
cb_name_guru_clicked (GtkWidget *button, NameGuruState *state)
414
{
415 416 417
	if (state->dialog == NULL)
		return;

418
	wbcg_set_entry (state->wbcg, NULL);
419

420
	if (button == state->delete_button) {
421
		name_guru_remove (NULL, state);
422 423 424
		return;
	}

425 426 427 428
	if (button == state->add_button ||
	    button == state->update_button ||
	    button == state->ok_button) {
		/* If adding the name failed, do not exit */
429
		if (!name_guru_add (state)) {
Jody Goldberg's avatar
Jody Goldberg committed
430
			return;
431 432
		}
	}
433

434
	if (button == state->close_button || button == state->ok_button) {
435
		gtk_widget_destroy (state->dialog);
436 437
		return;
	}
438

439 440
}

441 442
static GtkWidget *
name_guru_init_button (NameGuruState *state, char const *name)
443
{
444
	GtkWidget *tmp = glade_xml_get_widget (state->gui, name);
445 446

	g_return_val_if_fail (tmp != NULL, NULL);
447

448 449 450 451
	gtk_signal_connect (GTK_OBJECT (tmp), "clicked",
			    GTK_SIGNAL_FUNC (cb_name_guru_clicked),
			    state);
	return tmp;
452 453
}

454 455
static gboolean
cb_name_guru_destroy (GtkObject *w, NameGuruState *state)
456
{
457 458 459
	g_return_val_if_fail (w != NULL, FALSE);
	g_return_val_if_fail (state != NULL, FALSE);

460
	wbcg_edit_detach_guru (state->wbcg);
461 462 463 464 465 466

	if (state->gui != NULL) {
		gtk_object_unref (GTK_OBJECT (state->gui));
		state->gui = NULL;
	}

467
	wbcg_edit_finish (state->wbcg, FALSE);
468 469 470

	state->dialog = NULL;

Morten Welinder's avatar
Morten Welinder committed
471 472 473
	g_list_free (state->expr_names);
	state->expr_names = NULL;

474
	g_free (state);
475

476
	return FALSE;
477 478
}

479 480
static void
cb_name_guru_set_focus (GtkWidget *window, GtkWidget *focus_widget,
481
			NameGuruState *state)
482
{
Jody Goldberg's avatar
Jody Goldberg committed
483
	if (IS_GNUMERIC_EXPR_ENTRY (focus_widget)) {
484
		wbcg_set_entry (state->wbcg,
485 486 487
				    GNUMERIC_EXPR_ENTRY (focus_widget));
		gnumeric_expr_entry_set_absolute (state->expr_text);
	} else
488
		wbcg_set_entry (state->wbcg, NULL);
489 490
}

491
static gboolean
492
name_guru_init (NameGuruState *state, WorkbookControlGUI *wbcg)
493
{
494
	Workbook *wb = wb_control_workbook (WORKBOOK_CONTROL (wbcg));
495
	GtkTable *table2;
496 497 498 499 500

	state->wbcg  = wbcg;
	state->wb   = wb;
	state->sheet = wb_control_cur_sheet (WORKBOOK_CONTROL (wbcg));
	state->gui = gnumeric_glade_xml_new (state->wbcg, "names.glade");
501 502 503
        if (state->gui == NULL)
                return TRUE;

504 505 506 507 508
	parse_pos_init (&state->pp, state->wb, state->sheet,
			state->sheet->edit_pos.col,
			state->sheet->edit_pos.row);


509
	state->dialog = glade_xml_get_widget (state->gui, "NameGuru");
510
	table2 = GTK_TABLE (glade_xml_get_widget (state->gui, "table2"));
511
	state->name  = GTK_ENTRY (glade_xml_get_widget (state->gui, "name"));
Jody Goldberg's avatar
Jody Goldberg committed
512
	state->expr_text = GNUMERIC_EXPR_ENTRY (gnumeric_expr_entry_new (state->wbcg));
513 514 515 516 517
	gtk_table_attach (table2, GTK_WIDGET (state->expr_text),
			  1, 2, 1, 2,
			  GTK_EXPAND | GTK_FILL, 0,
			  0, 0);
	gtk_widget_show (GTK_WIDGET (state->expr_text));
518
	state->sheet_scope = GTK_TOGGLE_BUTTON (glade_xml_get_widget (state->gui, "sheet_scope"));
519
	state->wb_scope = GTK_TOGGLE_BUTTON (glade_xml_get_widget (state->gui, "workbook_scope"));
520
	state->list  = GTK_LIST (glade_xml_get_widget (state->gui, "name_list"));
521
	state->expr_names = NULL;
522
	state->cur_name   = NULL;
523
	state->updating   = FALSE;
524

525 526 527 528 529 530
	gtk_label_set_text (GTK_LABEL (GTK_BIN (state->sheet_scope)->child),
		state->sheet->name_unquoted);
	name_guru_display_scope (state);
	gtk_signal_connect (GTK_OBJECT (state->sheet_scope),
		"toggled",
		GTK_SIGNAL_FUNC (cb_scope_changed), state);
531

532 533 534
	state->ok_button     = name_guru_init_button (state, "ok_button");
	state->close_button  = name_guru_init_button (state, "close_button");
	state->add_button    = name_guru_init_button (state, "add_button");
535
	state->delete_button = name_guru_init_button (state, "delete_button");
536
	state->update_button = name_guru_init_button (state, "update_button");
537 538

	gtk_signal_connect (GTK_OBJECT (state->list), "selection_changed",
539
			    GTK_SIGNAL_FUNC (cb_name_guru_select_name), state);
540 541
	gtk_signal_connect (GTK_OBJECT (state->dialog), "set-focus",
			    GTK_SIGNAL_FUNC (cb_name_guru_set_focus), state);
542
	gtk_signal_connect (GTK_OBJECT (state->dialog), "destroy",
Jody Goldberg's avatar
Jody Goldberg committed
543
			    GTK_SIGNAL_FUNC (cb_name_guru_destroy), state);
544
	gtk_signal_connect (GTK_OBJECT (state->name), "changed",
545
			    GTK_SIGNAL_FUNC (cb_name_guru_update_sensitivity), state);
546 547 548
	/* We need to connect after because this is an expresion, and it will
	 * be changed by the mouse selecting a range, update after the entry
	 * is updated with the new text.
549
	 */
Jody Goldberg's avatar
Jody Goldberg committed
550
	gtk_signal_connect_after (GTK_OBJECT (state->expr_text), "changed",
551
				  GTK_SIGNAL_FUNC (cb_name_guru_update_sensitivity), state);
552

553
	gnumeric_editable_enters (GTK_WINDOW (state->dialog),
Morten Welinder's avatar
Morten Welinder committed
554
				  GTK_EDITABLE (state->name));
555
	gnumeric_editable_enters (GTK_WINDOW (state->dialog),
Jody Goldberg's avatar
Jody Goldberg committed
556
				  GTK_EDITABLE (state->expr_text));
557 558
	gnumeric_keyed_dialog (state->wbcg, GTK_WINDOW (state->dialog),
			       DEFINE_NAMES_KEY);
559

560 561
	gnumeric_expr_entry_set_scg (state->expr_text,
				     wb_control_gui_cur_sheet (wbcg));
562
	wbcg_edit_attach_guru (state->wbcg, state->dialog);
563 564

	return FALSE;
Michael Meeks's avatar
Michael Meeks committed
565 566
}

567 568
/**
 * dialog_define_names:
569 570
 * @wbcg:
 *
571 572
 * Create and show the define names dialog.
 **/
Miguel de Icaza's avatar
Miguel de Icaza committed
573
void
574
dialog_define_names (WorkbookControlGUI *wbcg)
Miguel de Icaza's avatar
Miguel de Icaza committed
575
{
576
	NameGuruState *state;
Michael Meeks's avatar
Michael Meeks committed
577

578
	g_return_if_fail (wbcg != NULL);
Michael Meeks's avatar
Michael Meeks committed
579

580
	/* Only one guru per workbook. */
581
	if (wbcg_edit_has_guru (wbcg))
582 583
		return;

584 585 586 587
	/* Only pop up one copy per workbook */
	if (gnumeric_dialog_raise_if_exists (wbcg, DEFINE_NAMES_KEY))
		return;

588
	state = g_new0 (NameGuruState, 1);
589
	if (name_guru_init (state, wbcg)) {
590 591
		gnumeric_notice (wbcg, GNOME_MESSAGE_BOX_ERROR,
				 _("Could not create the Name Guru."));
592
		g_free (state);
Michael Meeks's avatar
Michael Meeks committed
593 594 595
		return;
	}

Jody Goldberg's avatar
Jody Goldberg committed
596
	name_guru_populate_list (state);
597
	gtk_widget_show (state->dialog);
Miguel de Icaza's avatar
Miguel de Icaza committed
598
}