dialog-define-names.c 14.3 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;
Jody Goldberg's avatar
Jody Goldberg committed
37
	GnumericExprEntry *expr_entry;
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) {
Jody Goldberg's avatar
Jody Goldberg committed
114 115
		char *txt = expr_name_as_string (expr_name, &state->pp);
		gnm_expr_entry_load_from_text  (state->expr_entry, txt);
116
		g_free (txt);
Jody Goldberg's avatar
Jody Goldberg committed
117
		gtk_entry_set_text (state->name, expr_name->name->str);
118
	} else {
Jody Goldberg's avatar
Jody Goldberg committed
119
		gnm_expr_entry_load_from_text (state->expr_entry, "");
120 121 122 123
		gtk_entry_set_text (state->name, "");
	}
	state->updating = FALSE;

124
	name_guru_display_scope (state);
125 126 127 128
}

/**
 * name_guru_clear_selection:
129 130
 * @state:
 *
131 132 133 134 135 136 137 138 139 140 141 142 143 144
 * 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:
145
 * @name:
146
 * @state:
147
 *
148
 * Given a name, it searches for it inside the list of Names
149
 *
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
 * 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:
178 179 180
 * @state:
 * @update_entries:
 *
181 182 183 184 185 186 187 188 189
 * 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
190
	char const *name;
191 192 193 194 195

	g_return_if_fail (state->list != NULL);

	if (state->updating)
		return;
196

197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
	name  = gtk_entry_get_text (state->name);

	/** 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);
}

/**
222
 * cb_name_guru_update_sensitivity:
223 224 225
 * @dummy:
 * @state:
 *
226 227
 **/
static void
228
cb_name_guru_update_sensitivity (GtkWidget *dummy, NameGuruState *state)
229 230 231 232 233 234
{
	name_guru_update_sensitivity (state, FALSE);
}

/**
 * cb_name_guru_select_name:
235 236 237
 * @list:
 * @state:
 *
238 239
 * Set the expression from the selected row in the gtklist
 **/
240
static void
Jody Goldberg's avatar
Jody Goldberg committed
241
cb_name_guru_select_name (GtkWidget *list, NameGuruState *state)
242
{
Jody Goldberg's avatar
Jody Goldberg committed
243
	NamedExpression *expr_name;
Morten Welinder's avatar
Morten Welinder committed
244
	GList *sel = GTK_LIST (list)->selection;
245

246
	if (sel == NULL || state->updating)
247 248 249 250
		return;

	g_return_if_fail (sel->data != NULL);

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

253
	g_return_if_fail (expr_name != NULL);
Jody Goldberg's avatar
Jody Goldberg committed
254 255 256
	g_return_if_fail (expr_name->name != NULL);
	g_return_if_fail (expr_name->name->str != NULL);

257 258
	state->cur_name = expr_name;

259 260
	name_guru_set_expr (state, expr_name);
	name_guru_update_sensitivity (state, FALSE);
Michael Meeks's avatar
Michael Meeks committed
261 262
}

263

Michael Meeks's avatar
Michael Meeks committed
264
static void
Jody Goldberg's avatar
Jody Goldberg committed
265
name_guru_populate_list (NameGuruState *state)
Michael Meeks's avatar
Michael Meeks committed
266 267
{
	GList *names;
Jody Goldberg's avatar
Jody Goldberg committed
268
	GtkContainer *list;
Michael Meeks's avatar
Michael Meeks committed
269

270 271
	g_return_if_fail (state != NULL);
	g_return_if_fail (state->list != NULL);
Michael Meeks's avatar
Michael Meeks committed
272

273
	state->cur_name = NULL;
Michael Meeks's avatar
Michael Meeks committed
274

275 276
	gtk_list_clear_items (state->list, 0, -1);

Morten Welinder's avatar
Morten Welinder committed
277
	g_list_free (state->expr_names);
278
	state->expr_names = sheet_get_available_names (state->sheet);
279

280
	list = GTK_CONTAINER (state->list);
281
	for (names = state->expr_names ; names != NULL ; names = g_list_next (names)) {
282
		NamedExpression *expr_name = names->data;
283
		GtkWidget *li;
284
		if (expr_name->pos.sheet != NULL) {
285
			char *name = g_strdup_printf ("%s!%s",
286
						      expr_name->pos.sheet->name_unquoted,
287 288 289 290 291
						      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);
292
		gtk_object_set_data (GTK_OBJECT (li), LIST_KEY, expr_name);
Jody Goldberg's avatar
Jody Goldberg committed
293
		gtk_container_add (list, li);
Michael Meeks's avatar
Michael Meeks committed
294
	}
Jody Goldberg's avatar
Jody Goldberg committed
295
	gtk_widget_show_all (GTK_WIDGET (state->list));
296
	name_guru_update_sensitivity (state, TRUE);
297 298
}

299
/**
300
 * name_guru_remove:
301 302 303
 * @ignored:
 * @state:
 *
304 305
 * Remove the state->cur_name
 **/
306
static void
307
name_guru_remove (GtkWidget *ignored, NameGuruState *state)
Michael Meeks's avatar
Michael Meeks committed
308
{
309
	g_return_if_fail (state != NULL);
310
	g_return_if_fail (state->cur_name != NULL);
311

312 313
	if (!name_guru_warn (state))
		return;
314

315 316 317
	state->expr_names = g_list_remove (state->expr_names, state->cur_name);
	expr_name_remove (state->cur_name);
	state->cur_name = NULL;
318

319
	name_guru_populate_list (state);
320 321
}

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

	g_return_val_if_fail (state != NULL, FALSE);

	name  = gtk_entry_get_text (state->name);

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

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

Jody Goldberg's avatar
Jody Goldberg committed
348 349
	expr = gnm_expr_entry_parse (state->expr_entry,
		&state->pp, &perr, FALSE);
Jody Goldberg's avatar
Jody Goldberg committed
350
	if (expr == NULL) {
351
		gnumeric_notice (state->wbcg, GTK_MESSAGE_ERROR, perr.message);
Jody Goldberg's avatar
Jody Goldberg committed
352
		gtk_widget_grab_focus (GTK_WIDGET (state->expr_entry));
353
		parse_error_free (&perr);
Jody Goldberg's avatar
Jody Goldberg committed
354
		return FALSE;
Jody Goldberg's avatar
Jody Goldberg committed
355 356 357
	}

	if (expr_name) {
Jody Goldberg's avatar
Jody Goldberg committed
358
		if (!expr_name->builtin) {
359
			/* This means that the expresion was updated.
360
			 * FIXME: if the scope has been changed too, call scope
361
			 * chaned first.
362
			 */
363 364
			expr_name_set_expr (expr_name, expr);
			dirty = TRUE;
365
		} else
366
			gnumeric_notice (state->wbcg, GTK_MESSAGE_ERROR,
Morten Welinder's avatar
Morten Welinder committed
367
					 _("You cannot redefine a builtin name."));
368
	} else {
369
		char const *error = NULL;
370 371 372 373 374
		ParsePos pos;
		if (name_guru_scope_is_sheet (state))
			parse_pos_init (&pos, NULL, state->sheet,
					state->pp.eval.col,
					state->pp.eval.row);
375
		else
376 377 378
			parse_pos_init (&pos, state->wb, NULL,
					state->pp.eval.col,
					state->pp.eval.row);
379

380
		expr_name = expr_name_add (&pos, name, expr, &error);
381 382
		if (expr_name == NULL) {
			g_return_val_if_fail (error != NULL, FALSE);
383
			gnumeric_notice (state->wbcg, GTK_MESSAGE_ERROR, error);
Jody Goldberg's avatar
Jody Goldberg committed
384
			gtk_widget_grab_focus (GTK_WIDGET (state->expr_entry));
385 386
			return FALSE;
		}
387
		dirty = TRUE;
388
	}
389

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

Jody Goldberg's avatar
Jody Goldberg committed
392
	name_guru_populate_list (state);
393
	gtk_widget_grab_focus (GTK_WIDGET (state->name));
394

395 396 397
	if (dirty)
		sheet_set_dirty (state->sheet, TRUE);

398 399 400 401
	return TRUE;
}

static void
402
cb_name_guru_clicked (GtkWidget *button, NameGuruState *state)
403
{
404 405 406
	if (state->dialog == NULL)
		return;

407
	wbcg_set_entry (state->wbcg, NULL);
408

409
	if (button == state->delete_button) {
410
		name_guru_remove (NULL, state);
411 412 413
		return;
	}

414 415 416 417
	if (button == state->add_button ||
	    button == state->update_button ||
	    button == state->ok_button) {
		/* If adding the name failed, do not exit */
418
		if (!name_guru_add (state)) {
Jody Goldberg's avatar
Jody Goldberg committed
419
			return;
420 421
		}
	}
422

423
	if (button == state->close_button || button == state->ok_button) {
424
		gtk_widget_destroy (state->dialog);
425 426
		return;
	}
427

428 429
}

430 431
static GtkWidget *
name_guru_init_button (NameGuruState *state, char const *name)
432
{
433
	GtkWidget *tmp = glade_xml_get_widget (state->gui, name);
434 435

	g_return_val_if_fail (tmp != NULL, NULL);
436

Jody Goldberg's avatar
Jody Goldberg committed
437 438 439
	g_signal_connect (G_OBJECT (tmp),
		"clicked",
		G_CALLBACK (cb_name_guru_clicked), state);
440
	return tmp;
441 442
}

443 444
static gboolean
cb_name_guru_destroy (GtkObject *w, NameGuruState *state)
445
{
446 447 448
	g_return_val_if_fail (w != NULL, FALSE);
	g_return_val_if_fail (state != NULL, FALSE);

449
	wbcg_edit_detach_guru (state->wbcg);
450 451

	if (state->gui != NULL) {
Jody Goldberg's avatar
Jody Goldberg committed
452
		g_object_unref (G_OBJECT (state->gui));
453 454 455
		state->gui = NULL;
	}

456
	wbcg_edit_finish (state->wbcg, FALSE);
457 458 459

	state->dialog = NULL;

Morten Welinder's avatar
Morten Welinder committed
460 461 462
	g_list_free (state->expr_names);
	state->expr_names = NULL;

463
	g_free (state);
464

465
	return FALSE;
466 467
}

468
static gboolean
469
name_guru_init (NameGuruState *state, WorkbookControlGUI *wbcg)
470
{
471
	Workbook *wb = wb_control_workbook (WORKBOOK_CONTROL (wbcg));
472
	GtkTable *table2;
473 474 475 476 477

	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");
478 479 480
        if (state->gui == NULL)
                return TRUE;

481 482 483 484 485
	parse_pos_init (&state->pp, state->wb, state->sheet,
			state->sheet->edit_pos.col,
			state->sheet->edit_pos.row);


486
	state->dialog = glade_xml_get_widget (state->gui, "NameGuru");
487
	table2 = GTK_TABLE (glade_xml_get_widget (state->gui, "table2"));
488
	state->name  = GTK_ENTRY (glade_xml_get_widget (state->gui, "name"));
Jody Goldberg's avatar
Jody Goldberg committed
489 490
	state->expr_entry = gnumeric_expr_entry_new (state->wbcg, TRUE);
	gtk_table_attach (table2, GTK_WIDGET (state->expr_entry),
491 492 493
			  1, 2, 1, 2,
			  GTK_EXPAND | GTK_FILL, 0,
			  0, 0);
494
	gnm_expr_entry_set_absolute (state->expr_entry);
Jody Goldberg's avatar
Jody Goldberg committed
495
	gtk_widget_show (GTK_WIDGET (state->expr_entry));
496
	state->sheet_scope = GTK_TOGGLE_BUTTON (glade_xml_get_widget (state->gui, "sheet_scope"));
497
	state->wb_scope = GTK_TOGGLE_BUTTON (glade_xml_get_widget (state->gui, "workbook_scope"));
498
	state->list  = GTK_LIST (glade_xml_get_widget (state->gui, "name_list"));
499
	state->expr_names = NULL;
500
	state->cur_name   = NULL;
501
	state->updating   = FALSE;
502

503 504 505
	gtk_label_set_text (GTK_LABEL (GTK_BIN (state->sheet_scope)->child),
		state->sheet->name_unquoted);
	name_guru_display_scope (state);
Jody Goldberg's avatar
Jody Goldberg committed
506
	g_signal_connect (G_OBJECT (state->sheet_scope),
507
		"toggled",
Jody Goldberg's avatar
Jody Goldberg committed
508
		G_CALLBACK (cb_scope_changed), state);
509

510 511 512
	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");
513
	state->delete_button = name_guru_init_button (state, "delete_button");
514
	state->update_button = name_guru_init_button (state, "update_button");
515

Jody Goldberg's avatar
Jody Goldberg committed
516 517 518 519 520 521
	g_signal_connect (G_OBJECT (state->list),
		"selection_changed",
		G_CALLBACK (cb_name_guru_select_name), state);
	g_signal_connect (G_OBJECT (state->name),
		"changed",
		G_CALLBACK (cb_name_guru_update_sensitivity), state);
522 523 524
	/* 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.
525
	 */
Jody Goldberg's avatar
Jody Goldberg committed
526 527 528
	g_signal_connect_after (G_OBJECT (state->expr_entry),
		"changed",
		G_CALLBACK (cb_name_guru_update_sensitivity), state);
529

530
	gnumeric_editable_enters (GTK_WINDOW (state->dialog),
Jody Goldberg's avatar
Jody Goldberg committed
531
				  GTK_WIDGET (state->name));
532
	gnumeric_editable_enters (GTK_WINDOW (state->dialog),
Jody Goldberg's avatar
Jody Goldberg committed
533
				  GTK_WIDGET (state->expr_entry));
534 535
	gnumeric_keyed_dialog (state->wbcg, GTK_WINDOW (state->dialog),
			       DEFINE_NAMES_KEY);
536

Jody Goldberg's avatar
Jody Goldberg committed
537 538 539 540 541 542 543 544 545
	gnm_expr_entry_set_scg (state->expr_entry, wbcg_cur_scg (wbcg));

	name_guru_populate_list (state);

	/* a candidate for merging into attach guru */
	g_signal_connect (GTK_OBJECT (state->dialog),
		"destroy",
		G_CALLBACK (cb_name_guru_destroy), state);
	gnumeric_non_modal_dialog (state->wbcg, GTK_WINDOW (state->dialog));
546
	wbcg_edit_attach_guru (state->wbcg, state->dialog);
Jody Goldberg's avatar
Jody Goldberg committed
547
	gtk_widget_show_all (GTK_WIDGET (state->dialog));
548 549

	return FALSE;
Michael Meeks's avatar
Michael Meeks committed
550 551
}

552 553
/**
 * dialog_define_names:
554 555
 * @wbcg:
 *
556 557
 * Create and show the define names dialog.
 **/
Miguel de Icaza's avatar
Miguel de Icaza committed
558
void
559
dialog_define_names (WorkbookControlGUI *wbcg)
Miguel de Icaza's avatar
Miguel de Icaza committed
560
{
561
	NameGuruState *state;
Michael Meeks's avatar
Michael Meeks committed
562

563
	g_return_if_fail (wbcg != NULL);
Michael Meeks's avatar
Michael Meeks committed
564

565
	/* Only one guru per workbook. */
566
	if (wbcg_edit_has_guru (wbcg))
567 568
		return;

569 570 571 572
	/* Only pop up one copy per workbook */
	if (gnumeric_dialog_raise_if_exists (wbcg, DEFINE_NAMES_KEY))
		return;

573
	state = g_new0 (NameGuruState, 1);
574
	if (name_guru_init (state, wbcg)) {
575
		gnumeric_notice (wbcg, GTK_MESSAGE_ERROR,
576
				 _("Could not create the Name Guru."));
577
		g_free (state);
Michael Meeks's avatar
Michael Meeks committed
578 579
		return;
	}
Miguel de Icaza's avatar
Miguel de Icaza committed
580
}