dialog-cell-sort.c 32.4 KB
Newer Older
1 2 3
/*
 * dialog-cell-sort.c:  Implements Cell Sort dialog boxes.
 *
4 5
 * Authors:
 *  JP Rosevear   <jpr@arcavia.com>
6
 *  Michael Meeks <michael@ximian.com>
7
 *  Andreas J. Guelzow <aguelzow@taliesin.ca>
8
 *  Morten Welinder <terra@gnome.org>
jpekka's avatar
jpekka committed
9 10 11 12 13 14 15 16 17 18 19 20
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * 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/>.
22
 */
Jody Goldberg's avatar
Jody Goldberg committed
23

24
#include <gnumeric-config.h>
25
#include <glib/gi18n-lib.h>
26
#include <gnumeric.h>
27 28
#include <dialogs/dialogs.h>
#include <dialogs/help.h>
29 30 31 32 33 34 35 36 37 38 39 40

#include <workbook-view.h>
#include <gui-util.h>
#include <cell.h>
#include <expr.h>
#include <selection.h>
#include <parse-util.h>
#include <ranges.h>
#include <commands.h>
#include <workbook.h>
#include <sort.h>
#include <sheet.h>
41
#include <sheet-view.h>
42
#include <wbc-gtk.h>
43
#include <gnumeric-conf.h>
Morten Welinder's avatar
Morten Welinder committed
44 45
#include <widgets/gnm-cell-renderer-toggle.h>
#include <widgets/gnm-expr-entry.h>
46
#include <value.h>
47

Jody Goldberg's avatar
Jody Goldberg committed
48
#include <gsf/gsf-impl-utils.h>
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
49
#include <gdk/gdkkeysyms.h>
50
#include <goffice/goffice.h>
51

52
#define CELL_SORT_KEY "cell-sort-dialog"
53

Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
54 55


56
typedef struct {
57
	WBCGtk  *wbcg;
58
	Workbook  *wb;
Jody Goldberg's avatar
Jody Goldberg committed
59 60
	SheetView *sv;
	Sheet     *sheet;
61

62
	GtkBuilder *gui;
63 64 65 66
	GtkWidget *dialog;
	GtkWidget *warning_dialog;
	GtkWidget *cancel_button;
	GtkWidget *ok_button;
67 68
	GtkWidget *up_button;
	GtkWidget *down_button;
69 70
	GtkWidget *add_button;
	GtkWidget *delete_button;
71
	GtkWidget *clear_button;
72 73
	GnmExprEntry *range_entry;
	GnmExprEntry *add_entry;
74 75
	GtkListStore  *model;
	GtkTreeView   *treeview;
76
	GtkTreeViewColumn *header_column;
77
	GtkTreeSelection   *selection;
78
	GtkWidget *cell_sort_row_rb;
79
	GtkWidget *cell_sort_col_rb;
80
	GtkWidget *cell_sort_header_check;
81
	GtkWidget *retain_format_check;
82 83
	GdkPixbuf *image_ascending;
	GdkPixbuf *image_descending;
84
	GOLocaleSel *locale_selector;
85

Jody Goldberg's avatar
Jody Goldberg committed
86
	GnmValue  *sel;
87 88
	gboolean   header;
	gboolean   is_cols;
89
	int        sort_items;
90

91 92 93
} SortFlowState;

enum {
94
	ITEM_HEADER,
95 96
	ITEM_NAME,
	ITEM_DESCENDING,
97
	ITEM_DESCENDING_IMAGE,
98 99 100 101
	ITEM_CASE_SENSITIVE,
	ITEM_SORT_BY_VALUE,
	ITEM_MOVE_FORMAT,
	ITEM_NUMBER,
102
	NUM_COLUMNS
103
};
104

Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
105 106 107 108 109 110 111 112 113
static const gint MAX_MENU_SIZE = 20;
typedef struct {
	gint index;
	gint start;
	gint end;
	gboolean done_submenu;
	SortFlowState *state;
} AddSortFieldMenuState;

114 115 116 117 118 119 120 121 122 123 124 125 126 127
static gchar *
header_name (Sheet *sheet, int col, int row)
{
	GnmCell *cell;
	gchar *str = NULL;

	cell = sheet_cell_get (sheet, col, row);
	if (cell)
		str = value_get_as_string (cell->value);

	return str;
}


128
static gchar *
129
col_row_name (Sheet *sheet, int col, int row, gboolean header, gboolean is_cols)
130
{
131
	GnmCell *cell;
132 133
	gchar *str = NULL;

134 135 136 137 138
	if (is_cols)
		str = g_strdup_printf (_("Column %s"), col_name (col));
	else
		str = g_strdup_printf (_("Row %s"), row_name (row));

139 140
	if (header) {
		cell = sheet_cell_get (sheet, col, row);
141
		if (cell && !gnm_cell_is_blank (cell)) {
142 143
			gchar *header_str, *generic_str = str;
			header_str = value_get_as_string (cell->value);
144
			str = g_strdup_printf (_("%s (%s)"), header_str, generic_str);
145 146
			g_free (header_str);
			g_free (generic_str);
147
		}
148 149
	}

150 151 152
	return str;
}

Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
153

154
static gboolean
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
155
already_in_sort_fields(int index, SortFlowState *state)
156
{
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
157 158 159
	GtkTreeIter iter;
	int item = 0;
	gint number;
160

Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
161 162 163 164 165 166 167
	/* See if index is already in the sort fields */
	while (gtk_tree_model_iter_nth_child  (GTK_TREE_MODEL (state->model),
					       &iter, NULL, item)) {
		gtk_tree_model_get (GTK_TREE_MODEL (state->model), &iter,
				    ITEM_NUMBER, &number,
				    -1);
		item++;
168

Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
169 170 171
		if (number == index) {
			return TRUE;
		}
172 173
	}

Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
174 175 176 177 178 179 180 181 182
	/* Here means not already in sort fields */
	return FALSE;
}

static gboolean
range_already_in_sort_criteria(gint start, gint end, SortFlowState *state)
{
	gint i;
	for (i=start; i<=end; i++) {
183
		if (!already_in_sort_fields(i, state))
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
184
			return FALSE;
185
	}
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
186 187 188 189 190
	return TRUE;
}


static void
191
build_sort_field_menu (gint start, gint end, gint index, GtkWidget *menu, SortFlowState *state, int used);
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
192 193 194 195

static void
cb_sort_field_menu_activate(GtkWidget *item, AddSortFieldMenuState *menu_state)
{
196 197 198 199 200 201 202 203 204 205
	GtkWidget *menu = GTK_WIDGET (gtk_menu_item_get_submenu(GTK_MENU_ITEM (item)));

	if (menu_state->done_submenu == FALSE) {
		build_sort_field_menu(menu_state->start,
				      menu_state->end,
				      menu_state->index,
				      menu,
				      menu_state->state, 0);
		menu_state->done_submenu = TRUE;
	}
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
206 207 208
}

static void
209
set_button_sensitivity(SortFlowState *state)
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
210 211
{
	int items;
212 213 214 215 216 217

	if (state->sel == NULL) {
		gtk_widget_set_sensitive (state->ok_button, FALSE);
		return;
	}

Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
218 219
	items = state->is_cols ? (state->sel->v_range.cell.b.row -
				  state->sel->v_range.cell.a.row + 1) :
220 221
		(state->sel->v_range.cell.b.col -
		 state->sel->v_range.cell.a.col + 1);
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
222 223 224 225 226
	if (state->header)
		items -= 1;
	gtk_widget_set_sensitive (state->ok_button,
				  (state->sort_items != 0) &&
				  (items > 1));
227
	gtk_widget_set_sensitive (state->clear_button, state->sort_items != 0);
228 229
}

230 231 232
static void
append_data (SortFlowState *state, int i, int index)
{
233
	gchar *str, *header;
234 235
	GtkTreeIter iter;
	Sheet *sheet = state->sel->v_range.cell.a.sheet;
236
	gboolean sort_asc = gnm_conf_get_core_sort_default_ascending ();
237

238 239 240
	header = state->is_cols
		? header_name (sheet, i, index)
		: header_name (sheet, index, i);
241
	str = state->is_cols
242 243
		? col_row_name (sheet, i, index, FALSE, TRUE)
		: col_row_name (sheet, index, i, FALSE, FALSE);
244 245
	gtk_list_store_append (state->model, &iter);
	gtk_list_store_set (state->model, &iter,
246
			    ITEM_HEADER,  header,
247
			    ITEM_NAME,  str,
248
			    ITEM_DESCENDING, !sort_asc,
Morten Welinder's avatar
Morten Welinder committed
249
			    ITEM_DESCENDING_IMAGE, sort_asc ? state->image_ascending
250
			    : state->image_descending,
251
			    ITEM_CASE_SENSITIVE, gnm_conf_get_core_sort_default_by_case (),
252 253 254 255 256 257
			    ITEM_SORT_BY_VALUE, TRUE,
			    ITEM_MOVE_FORMAT, TRUE,
			    ITEM_NUMBER, i,
			    -1);
	state->sort_items++;
	g_free (str);
258
	g_free (header);
259 260
}

Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
261
static void
Andreas J. Guelzow 's avatar
Andreas J. Guelzow committed
262
cb_sort_field_selection(G_GNUC_UNUSED GtkWidget *item, AddSortFieldMenuState *menu_state)
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
263 264 265 266 267 268
{
	append_data(menu_state->state,
		    menu_state->start,
		    menu_state->index);
	/* Update sensitivity if this is the first sort item. */
	if (menu_state->state->sort_items == 1)
269
		set_button_sensitivity(menu_state->state);
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
270 271 272
}

static void
273
build_sort_field_menu (gint start, gint end, gint index, GtkWidget *menu, SortFlowState *state, int used)
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
274 275 276 277 278 279 280 281 282 283 284
{
	Sheet *sheet = state->sel->v_range.cell.a.sheet;
	GtkWidget *item;
	GtkWidget *submenu;
	int i;
	int this_end;
	char *str;
	char *str_start;
	char *str_end;
	AddSortFieldMenuState *menu_state;
	gint menu_size;
285

Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
286
	menu_size = 1 + end - start;
287 288 289
	if (MAX_MENU_SIZE < menu_size - used) {
		gint submenu_size;
		gint balanced_submenu_size;
290

291
		submenu_size = (menu_size + MAX_MENU_SIZE - 1) / MAX_MENU_SIZE;
292
		balanced_submenu_size = sqrt((double)
293 294 295 296
					     (menu_size + MAX_MENU_SIZE - 1));
		if (balanced_submenu_size > submenu_size)
			submenu_size = balanced_submenu_size;

Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
297 298 299 300
		for (i = start; i <= end; i+=submenu_size) {
			this_end = i + submenu_size - 1;
			if (this_end > end)
				this_end = end;
301

Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
302 303
			/* See if there are any fields in this range that aren't already
			   in the sort.
304
			*/
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
305
			if (range_already_in_sort_criteria(i, this_end, state))
306 307 308 309 310 311 312 313 314 315
				continue;

			str_start = state->is_cols
				? col_row_name (sheet, i, index, state->header, TRUE)
				: col_row_name (sheet, index, i, state->header, FALSE);

			str_end = state->is_cols
				? col_row_name (sheet, this_end, index, state->header, TRUE)
				: col_row_name (sheet, index, this_end, state->header, FALSE);

316
			str = g_strdup_printf(_("%s to %s"), str_start, str_end);
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
317 318 319 320 321 322
			g_free(str_start);
			g_free(str_end);

			item = (GtkWidget *) gtk_menu_item_new_with_label(str);
			gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
			gtk_widget_show (item);
323

Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
324 325 326 327 328 329 330 331 332 333 334 335 336 337
			menu_state = g_new(AddSortFieldMenuState, 1);
			menu_state->start = i;
			menu_state->end = this_end;
			menu_state->index = index;
			menu_state->state = state;
			menu_state->done_submenu = FALSE;
			submenu = gtk_menu_new();
			gtk_menu_item_set_submenu(GTK_MENU_ITEM (item), submenu);
			g_signal_connect (item, "activate",
					  G_CALLBACK (cb_sort_field_menu_activate), menu_state);
		}
	}  else {
		for (i = start; i <= end; i++) {
			if (FALSE == already_in_sort_fields(i, state)) {
338 339

				str = state->is_cols
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
340 341 342 343 344 345 346 347 348 349 350 351
					? col_row_name (sheet, i, index, state->header, TRUE)
					: col_row_name (sheet, index, i, state->header, FALSE);
				item = (GtkWidget *) gtk_menu_item_new_with_label(str);
				gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
				gtk_widget_show (item);
				menu_state = g_new(AddSortFieldMenuState, 1);
				menu_state->start = i;
				menu_state->end = i;
				menu_state->index = index;
				menu_state->state = state;
				menu_state->done_submenu = FALSE;
				g_signal_connect (item, "activate",
352
						  G_CALLBACK (cb_sort_field_selection),
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
353 354 355 356 357 358
						  menu_state);
			}
		}
	}
}

359
static void
360
load_model_data (SortFlowState *state)
361 362 363 364 365
{
	int start;
	int end;
	int index;
	int i;
366
	int limit = gnm_conf_get_core_sort_dialog_max_initial_clauses ();
367 368 369

	if (state->is_cols) {
		start = state->sel->v_range.cell.a.col;
370
		end = state->sel->v_range.cell.b.col;
371 372 373
		index = state->sel->v_range.cell.a.row;
	} else {
		start = state->sel->v_range.cell.a.row;
374
		end = state->sel->v_range.cell.b.row;
375 376 377 378
		index = state->sel->v_range.cell.a.col;
	}

	gtk_list_store_clear (state->model);
379
	state->sort_items = 0;
380

381 382
	if (end >= start + limit)
		end = start + limit - 1;
383

384 385 386 387 388
	for (i = start; i <= end; i++)
		append_data (state, i, index);
}

static void
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
389
translate_range (GnmValue *range, SortFlowState *state)
390
{
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
391 392 393 394
	state->is_cols = !gtk_toggle_button_get_active (
		GTK_TOGGLE_BUTTON (state->cell_sort_row_rb));
	state->header = gtk_toggle_button_get_active (
		GTK_TOGGLE_BUTTON (state->cell_sort_header_check));
395

396
	value_release (state->sel);
397
	state->sel = range;
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
398 399 400 401 402 403 404 405 406
	load_model_data(state);
}

static void
cb_sort_header_check(SortFlowState *state)
{
	state->header = gtk_toggle_button_get_active (
		GTK_TOGGLE_BUTTON (state->cell_sort_header_check));

407
	gtk_tree_view_column_set_visible (state->header_column, state->header);
408
	set_button_sensitivity (state);
409 410 411
}

static void
412
cb_update_to_new_range (SortFlowState *state)
413
{
Jody Goldberg's avatar
Jody Goldberg committed
414
        GnmValue *range;
415

416
        range = gnm_expr_entry_parse_as_value
417
		(GNM_EXPR_ENTRY (state->range_entry), state->sheet);
418 419 420 421 422
	if (range == NULL) {
		if (state->sel != NULL) {
			value_release (state->sel);
			state->sel = NULL;
			gtk_list_store_clear (state->model);
423
			state->sort_items = 0;
424
		}
425
	} else
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
426
		translate_range (range, state);
427
	set_button_sensitivity (state);
428 429
}

430 431
static void
cb_dialog_destroy (SortFlowState  *state)
432
{
433 434
	value_release (state->sel);
	state->sel = NULL;
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
435

Morten Welinder's avatar
Morten Welinder committed
436 437
	g_clear_object (&state->model);
	g_clear_object (&state->gui);
438

439
	wbcg_edit_finish (state->wbcg, WBC_EDIT_REJECT, NULL);
440 441 442

	state->dialog = NULL;

Morten Welinder's avatar
Morten Welinder committed
443 444
	g_clear_object (&state->image_ascending);
	g_clear_object (&state->image_descending);
445

446 447 448
	g_free (state);
}

449
static void
450
cb_dialog_ok_clicked (SortFlowState *state)
451
{
452
	GnmSortData *data, *data_copy;
Jody Goldberg's avatar
Jody Goldberg committed
453
	GnmSortClause *array, *this_array_item;
454 455
	int item = 0;
	GtkTreeIter iter;
456
	gboolean descending, case_sensitive, sort_by_value, move_format;
457
	gint number;
458
	gint base;
459
	char const *text;
460

Jody Goldberg's avatar
Jody Goldberg committed
461
	array = g_new (GnmSortClause, state->sort_items);
462
	this_array_item = array;
463
	base = (state->is_cols ? state->sel->v_range.cell.a.col : state->sel->v_range.cell.a.row);
464 465 466 467 468 469 470 471 472 473 474

	while (gtk_tree_model_iter_nth_child  (GTK_TREE_MODEL (state->model),
					       &iter, NULL, item)) {
		gtk_tree_model_get (GTK_TREE_MODEL (state->model), &iter,
				    ITEM_DESCENDING,&descending,
				    ITEM_CASE_SENSITIVE, &case_sensitive,
				    ITEM_SORT_BY_VALUE, &sort_by_value,
				    ITEM_MOVE_FORMAT, &move_format,
				    ITEM_NUMBER, &number,
				    -1);
		item++;
475
		this_array_item->offset = number - base;
476
		this_array_item->asc = !!descending;
477 478 479
		this_array_item->cs = case_sensitive;
		this_array_item->val = sort_by_value;
		this_array_item++;
480 481 482
	}


Jody Goldberg's avatar
Jody Goldberg committed
483
	data = g_new (GnmSortData, 1);
484
	data->sheet = state->sel->v_range.cell.a.sheet;
Jody Goldberg's avatar
Jody Goldberg committed
485
	data->range = g_new (GnmRange, 1);
486 487
	data->range = range_init (data->range, state->sel->v_range.cell.a.col
				  + ((state->header && !state->is_cols) ? 1 : 0),
488
				  state->sel->v_range.cell.a.row
489
				  + ((state->header && state->is_cols) ? 1 : 0),
490
				  state->sel->v_range.cell.b.col,
491 492 493 494
				  state->sel->v_range.cell.b.row);
	data->num_clause = state->sort_items;
	data->clauses = array;
	data->top = state->is_cols;
495 496
	data->retain_formats = gtk_toggle_button_get_active (
		GTK_TOGGLE_BUTTON (state->retain_format_check));
497
	data->locale = go_locale_sel_get_locale (state->locale_selector);
Morten Welinder's avatar
Morten Welinder committed
498

499 500 501
	data_copy = gnm_sort_data_copy (data);
	text = gnm_expr_entry_get_text (state->range_entry);
	gnm_sheet_add_sort_setup
Morten Welinder's avatar
Morten Welinder committed
502 503
		(data->sheet,
		 g_strdup((text != NULL && text[0] != '\0') ? text : "Other"),
504
		 data_copy);
Morten Welinder's avatar
Morten Welinder committed
505

Morten Welinder's avatar
Morten Welinder committed
506
	cmd_sort (GNM_WBC (state->wbcg), data);
507 508 509 510 511

	gtk_widget_destroy (state->dialog);
	return;
}

512
static void
Morten Welinder's avatar
Morten Welinder committed
513
cb_dialog_cancel_clicked (G_GNUC_UNUSED GtkWidget *button,
514
			  SortFlowState *state)
515 516 517 518
{
	gtk_widget_destroy (state->dialog);
}

519 520 521
static void
dialog_cell_sort_load_sort_setup (SortFlowState *state, GnmSortData const *data)
{
522 523 524 525
	int i;
	GnmSortClause *this = data->clauses;
	gint base, max, index;
	Sheet *sheet = state->sel->v_range.cell.a.sheet;
526

527 528
	if (sheet == NULL)
		sheet = state->sheet;
Morten Welinder's avatar
Morten Welinder committed
529

530
	go_locale_sel_set_locale (state->locale_selector, data->locale);
Morten Welinder's avatar
Morten Welinder committed
531

532 533
	gtk_toggle_button_set_active (
		GTK_TOGGLE_BUTTON (state->retain_format_check), data->retain_formats);
Morten Welinder's avatar
Morten Welinder committed
534

535 536 537
	gtk_toggle_button_set_active (
		GTK_TOGGLE_BUTTON (state->cell_sort_row_rb), !data->top);
	state->is_cols = data->top;
538

539 540 541 542 543
	index = (data->top ? state->sel->v_range.cell.a.row : state->sel->v_range.cell.a.col);
	base = (data->top ? state->sel->v_range.cell.a.col : state->sel->v_range.cell.a.row);
	max = (data->top ? state->sel->v_range.cell.b.col : state->sel->v_range.cell.b.row);
	gtk_list_store_clear (state->model);
	state->sort_items = 0;
Morten Welinder's avatar
Morten Welinder committed
544
	for (i = 0; i < data->num_clause; i++) {
545 546 547 548 549 550 551 552 553 554 555 556 557 558 559
		if (data->clauses[i].offset <= max ) {
			GtkTreeIter iter;
			gchar *str, *header;
			int id = data->clauses[i].offset + base;

			header = state->is_cols
				? header_name (sheet, id, index)
				: header_name (sheet, index, id);
			str = col_row_name (sheet, id, id, FALSE, state->is_cols);

			gtk_list_store_append (state->model, &iter);
			gtk_list_store_set (state->model, &iter,
					    ITEM_HEADER,  header,
					    ITEM_NAME,  str,
					    ITEM_DESCENDING, data->clauses[i].asc,
Morten Welinder's avatar
Morten Welinder committed
560
					    ITEM_DESCENDING_IMAGE,
561 562 563 564 565 566 567 568 569 570 571 572
					    !data->clauses[i].asc
					    ? state->image_ascending
					    : state->image_descending,
					    ITEM_CASE_SENSITIVE, data->clauses[i].cs,
					    ITEM_SORT_BY_VALUE, data->clauses[i].val,
					    ITEM_MOVE_FORMAT, TRUE,
					    ITEM_NUMBER, id,
					    -1);
			state->sort_items++;
		}
		this++;
	}
573
	set_button_sensitivity (state);
574 575
}

576 577
static GnmRange const *
dialog_load_selection (SortFlowState *state, gboolean *col_rb)
578
{
Jody Goldberg's avatar
Jody Goldberg committed
579
	GnmRange const *first;
580
	GnmSortData const *data;
581

Jody Goldberg's avatar
Jody Goldberg committed
582
	first = selection_first_range (state->sv, NULL, NULL);
583

584
	if (first != NULL) {
585 586
		gtk_toggle_button_set_active (
			GTK_TOGGLE_BUTTON (state->cell_sort_col_rb),
587
			(*col_rb = (first->end.row - first->start.row > first->end.col - first->start.col)));
588
		gnm_expr_entry_load_from_range (state->range_entry,
589
						state->sheet, first);
590
	} else
591 592
		gtk_toggle_button_set_active (
			GTK_TOGGLE_BUTTON (state->cell_sort_col_rb),
593
			(*col_rb = TRUE));
594 595

	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (state->cell_sort_header_check),
Morten Welinder's avatar
Morten Welinder committed
596
				      sheet_range_has_heading
597 598
				      (state->sheet, first, *col_rb, FALSE));
	cb_sort_header_check (state);
Morten Welinder's avatar
Morten Welinder committed
599

Morten Welinder's avatar
Morten Welinder committed
600
	data = gnm_sheet_find_sort_setup (state->sheet,
601 602 603
					  gnm_expr_entry_get_text (state->range_entry));
	if (data != NULL)
		dialog_cell_sort_load_sort_setup (state, data);
604 605
	else
		cb_update_to_new_range (state);
Morten Welinder's avatar
Morten Welinder committed
606

607
	return first;
608 609
}

610
/**
611
 * cb_sort_selection_changed:
612
 *
613
 * Refreshes the buttons on a row (un)selection
614 615
 */
static void
616
cb_sort_selection_changed (SortFlowState *state)
617
{
618
	GtkTreeIter iter, test;
619

620 621 622
	if (!gtk_tree_selection_get_selected (state->selection, NULL, &iter)) {
		gtk_widget_set_sensitive (state->up_button, FALSE);
		gtk_widget_set_sensitive (state->down_button, FALSE);
623
		gtk_widget_set_sensitive (state->delete_button, FALSE);
624 625 626
		return;
	}

627 628 629
	test = iter;
	gtk_widget_set_sensitive
		(state->up_button,
Morten Welinder's avatar
Morten Welinder committed
630
		 gtk_tree_model_iter_previous (GTK_TREE_MODEL (state->model),
631 632 633 634 635 636 637
					   &test));

	test = iter;
	gtk_widget_set_sensitive
		(state->down_button,
		 gtk_tree_model_iter_next (GTK_TREE_MODEL (state->model),
					   &test));
638

639
	gtk_widget_set_sensitive (state->delete_button, TRUE);
640
	set_button_sensitivity (state);
641 642 643
}

static void
644
toggled (SortFlowState *state, const gchar *path_string, int column)
645
{
646 647 648 649
	GtkTreeModel *model = GTK_TREE_MODEL (state->model);
	GtkTreeIter iter;
	GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
	gboolean value;
650

651 652 653
	if (gtk_tree_model_get_iter (model, &iter, path)) {
		gtk_tree_model_get (model, &iter, column, &value, -1);
		value = !value;
654
		gtk_list_store_set (GTK_LIST_STORE (model),
655 656 657 658
				    &iter, column, value, -1);
	} else {
		g_warning ("Did not get a valid iterator");
	}
659

660
	gtk_tree_path_free (path);
661 662 663
}

static void
664 665
move_cb (SortFlowState *state,
	 gboolean (*mover) (GtkTreeModel *, GtkTreeIter *))
666
{
667
	GtkTreeIter iter, this_iter;
668

669 670
	if (!gtk_tree_selection_get_selected (state->selection, NULL,
					      &this_iter))
671
		return;
672

673 674 675
	iter = this_iter;
	if (!mover (GTK_TREE_MODEL(state->model), &iter))
		return;
676 677 678

	gtk_list_store_swap (state->model, &this_iter, &iter);
	cb_sort_selection_changed (state);
679 680
}

681
static void
682
cb_up (SortFlowState *state)
683
{
Morten Welinder's avatar
Morten Welinder committed
684
	move_cb (state, gtk_tree_model_iter_previous);
685 686 687
}

static void
688
cb_down (SortFlowState *state)
689
{
690
	move_cb (state, gtk_tree_model_iter_next);
691
}
692

693
static void
Morten Welinder's avatar
Morten Welinder committed
694
cb_delete_clicked (G_GNUC_UNUSED GtkWidget *w, SortFlowState *state)
695
{
696 697 698
	GtkTreeIter iter, iter2;
	gboolean ok;

699 700
	if (!gtk_tree_selection_get_selected (state->selection, NULL, &iter))
		return;
701 702 703 704 705

	iter2 = iter;
	ok = gtk_tree_model_iter_next (GTK_TREE_MODEL (state->model), &iter2);
	if (!ok) {
		iter2 = iter;
Morten Welinder's avatar
Morten Welinder committed
706
		ok = gtk_tree_model_iter_previous (GTK_TREE_MODEL (state->model), &iter2);
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
707
	}
708 709 710 711 712 713

	if (ok)
		gtk_tree_selection_select_iter (state->selection, &iter2);

	gtk_list_store_remove (state->model, &iter);
	state->sort_items--;
714
	set_button_sensitivity (state);
715
}
716

Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
717

718
static void
719
cb_clear_clicked (SortFlowState *state)
720 721 722
{
	state->sort_items = 0;
	gtk_list_store_clear (state->model);
723
	set_button_sensitivity (state);
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
724 725 726 727 728 729 730 731 732 733
}

static GtkMenu *
build_sort_field_base_menu (SortFlowState *state)
{
	gint start;
	gint end;
	gint index;

	GtkWidget *menu = gtk_menu_new ();
734 735 736
	GList* items = NULL;

	if (state->sel != NULL) {
737 738 739 740 741 742 743 744 745 746 747 748 749 750
		if (state->is_cols) {
			start = state->sel->v_range.cell.a.col;
			end = state->sel->v_range.cell.b.col;
			index = state->sel->v_range.cell.a.row;
		} else {
			start = state->sel->v_range.cell.a.row;
			end = state->sel->v_range.cell.b.row;
			index = state->sel->v_range.cell.a.col;
		}

		build_sort_field_menu (start, end, index, menu, state,
				       state->sort_items);

		items = gtk_container_get_children (GTK_CONTAINER (menu));
751
	}
752

Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
753 754
	if (items == NULL) {
		GtkWidget *item;
755
		item = (GtkWidget *) gtk_menu_item_new_with_label(state->is_cols ?
756
								  _("no available column"): _("no available row"));
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
757 758 759 760 761 762
		gtk_widget_set_sensitive( GTK_WIDGET (item), FALSE);
		gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
		gtk_widget_show (item);
	}

	g_list_free (items);
763

Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
764 765 766 767 768 769
	return GTK_MENU (menu);
}

static void
show_add_menu (SortFlowState *state)
{
770 771
	gnumeric_popup_menu (build_sort_field_base_menu(state),
			     NULL);
772 773
}

774
static void
775
cb_add_clicked (SortFlowState *state)
776
{
Jody Goldberg's avatar
Jody Goldberg committed
777
        GnmValue *range_add;
778
	GnmSheetRange grange_sort, grange_add;
Jody Goldberg's avatar
Jody Goldberg committed
779
	GnmRange intersection;
780 781 782 783
	int start;
	int end;
	int index;
	int i;
784
	gboolean had_items = (state->sort_items > 0);
785 786

        range_add = gnm_expr_entry_parse_as_value
787
		(GNM_EXPR_ENTRY (state->add_entry), state->sheet);
788

Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
789 790 791 792 793
	if (range_add == NULL) {
		show_add_menu (state);
		return;
	}

794
	g_return_if_fail (range_add != NULL && state->sel != NULL);
Morten Welinder's avatar
Morten Welinder committed
795

796 797
	gnm_sheet_range_from_value (&grange_sort, state->sel);
	gnm_sheet_range_from_value (&grange_add, range_add);
798

799 800
	value_release (range_add);

801
	if (range_intersection (&intersection, &grange_sort.range, &grange_add.range)) {
802 803 804

		if (state->is_cols) {
			start = intersection.start.col;
805
			end = intersection.end.col;
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
806
			index = state->sel->v_range.cell.a.row;
807 808
		} else {
			start = intersection.start.row;
809
			end = intersection.end.row;
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
810
			index = state->sel->v_range.cell.a.col;
811
		}
Morten Welinder's avatar
Morten Welinder committed
812

813
		for (i = start; i <= end; i++) {
Morten Welinder's avatar
Morten Welinder committed
814

815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830
			int item = 0;
			GtkTreeIter iter;
			gboolean found = FALSE;
			gint number;

			while (gtk_tree_model_iter_nth_child  (GTK_TREE_MODEL (state->model),
							       &iter, NULL, item)) {
				gtk_tree_model_get (GTK_TREE_MODEL (state->model), &iter,
						    ITEM_NUMBER, &number,
						    -1);
				item++;
				if (number == i) {
					found = TRUE;
					break;
				}
			}
Morten Welinder's avatar
Morten Welinder committed
831

832 833 834
			if (!found) {
				append_data (state, i, index);
			}
Morten Welinder's avatar
Morten Welinder committed
835
		}
836
		if (!had_items && (state->sort_items > 0))
837
			set_button_sensitivity(state);
838
	} else
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
839 840 841 842 843
		show_add_menu (state);
	gnm_expr_entry_load_from_text (GNM_EXPR_ENTRY (state->add_entry), "");
}

static gint
Andreas J. Guelzow 's avatar
Andreas J. Guelzow committed
844
cb_treeview_button_press(G_GNUC_UNUSED GtkWidget *w, GdkEvent *event, SortFlowState *state)
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
845 846 847
{
	if ((event->type == GDK_BUTTON_PRESS) &&
	    (event->button.button == 3)) {
848
		gnumeric_popup_menu (build_sort_field_base_menu(state),
849
				     event);
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
850 851
		return TRUE;
	}
852

Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
853 854 855 856 857 858 859 860 861
	return FALSE;
}

static gint
cb_treeview_keypress (G_GNUC_UNUSED GtkWidget *w, GdkEventKey *event,
		      SortFlowState *state)
{
	gboolean ctrl = (event->state & GDK_CONTROL_MASK);
	GtkTreeIter iter;
862

Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
863
	switch (event->keyval) {
864 865
	case GDK_KEY_Delete:
	case GDK_KEY_KP_Delete:
866
		cb_delete_clicked (w, state);
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
867
		return TRUE;
868 869
	case GDK_KEY_KP_Up:
	case GDK_KEY_Up:
870 871 872
		if (ctrl) {
			cb_up (state);
			return TRUE;
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
873
		}
874 875

		if (gtk_tree_selection_get_selected (state->selection, NULL, &iter) &&
Morten Welinder's avatar
Morten Welinder committed
876
		    gtk_tree_model_iter_previous (GTK_TREE_MODEL (state->model), &iter))
877 878
			gtk_tree_selection_select_iter (state->selection,
							&iter);
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
879 880
		return TRUE;

881 882
	case GDK_KEY_KP_Down:
	case GDK_KEY_Down:
883 884 885
		if (ctrl) {
			cb_down (state);
			return TRUE;
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
886
		}
887 888 889 890 891

		if (gtk_tree_selection_get_selected (state->selection, NULL, &iter) &&
		    gtk_tree_model_iter_next (GTK_TREE_MODEL (state->model), &iter))
			gtk_tree_selection_select_iter (state->selection,
							&iter);
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
892
		return TRUE;
893
	}
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
894
	return FALSE;
895 896
}

897
static void
Morten Welinder's avatar
Morten Welinder committed
898
cb_toggled_descending (G_GNUC_UNUSED GtkCellRendererToggle *cell,
899 900
		       const gchar *path_string,
		       SortFlowState *state)
901
{
902 903 904 905
	GtkTreeModel *model = GTK_TREE_MODEL (state->model);
	GtkTreeIter iter;
	GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
	gboolean value;
906

907 908 909
	if (gtk_tree_model_get_iter (model, &iter, path)) {
		gtk_tree_model_get (model, &iter, ITEM_DESCENDING, &value, -1);
		if (value) {
910
			gtk_list_store_set (GTK_LIST_STORE (model), &iter,
911
					    ITEM_DESCENDING, FALSE,
912
					    ITEM_DESCENDING_IMAGE, state->image_ascending,
913 914
					    -1);
		} else {
915
			gtk_list_store_set (GTK_LIST_STORE (model), &iter,
916
					    ITEM_DESCENDING, TRUE,
917
					    ITEM_DESCENDING_IMAGE, state->image_descending,
918 919
					    -1);
		}
920
	} else {
921
		g_warning ("Did not get a valid iterator");
922
	}
923
	gtk_tree_path_free (path);
924 925
}

Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
926 927
#if 0
/* We are currently not supporting `by-value' vs not. */
928
static void
Andreas J. Guelzow 's avatar
Andreas J. Guelzow committed
929
cb_toggled_sort_by_value (G_GNUC_UNUSED GtkCellRendererToggle *cell,
930 931
			  const gchar *path_string,
			  SortFlowState *state)
932
{
933
	toggled (state, path_string, ITEM_SORT_BY_VALUE);
934
}
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
935
#endif
936 937

static void
Andreas J. Guelzow 's avatar
Andreas J. Guelzow committed
938
cb_toggled_case_sensitive (G_GNUC_UNUSED GtkCellRendererToggle *cell,
939 940
			   const gchar           *path_string,
			   SortFlowState *state)
941
{
942
	toggled (state, path_string, ITEM_CASE_SENSITIVE);
943 944
}

Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
945

946
static void
947 948
dialog_init (SortFlowState *state)
{
949
	GtkGrid *grid;
950 951
	GtkWidget *scrolled;
	GtkTreeViewColumn *column;
952
	GtkCellRenderer *renderer;
953
	gboolean col_rb;
954

955
	grid = GTK_GRID (go_gtk_builder_get_widget (state->gui, "cell-sort-grid"));
956
	/* setup range entry */
957
	state->range_entry = gnm_expr_entry_new (state->wbcg, TRUE);
958
	gnm_expr_entry_set_flags (state->range_entry,
959 960
				  GNM_EE_SINGLE_RANGE,
				  GNM_EE_MASK);
961 962 963
	gtk_widget_set_hexpand (GTK_WIDGET (state->range_entry), TRUE);
	gtk_grid_attach (grid, GTK_WIDGET (state->range_entry),
			  1, 1, 2, 1);
Morten Welinder's avatar
Morten Welinder committed
964
	gnm_editable_enters (GTK_WINDOW (state->dialog),
965
				  GTK_WIDGET (state->range_entry));
966
	gnm_expr_entry_set_update_policy (state->range_entry, GNM_UPDATE_DISCONTINUOUS);
967
	gtk_widget_show (GTK_WIDGET (state->range_entry));
968
	g_signal_connect_swapped (G_OBJECT (state->range_entry),
969
				  "changed",
970
				  G_CALLBACK (cb_update_to_new_range), state);
971

972
	state->locale_selector = GO_LOCALE_SEL (go_locale_sel_new ());
973
	gtk_widget_set_hexpand (GTK_WIDGET (state->locale_selector), TRUE);
974
	gtk_widget_show_all (GTK_WIDGET (state->locale_selector));
975 976
	gtk_grid_attach (grid, GTK_WIDGET (state->locale_selector),
			  1, 5, 2, 1);
977

978
	grid = GTK_GRID (go_gtk_builder_get_widget (state->gui, "cell-sort-spec-grid"));
979
	/* setup add entry */
980
	state->add_entry = gnm_expr_entry_new (state->wbcg, TRUE);
981
	gnm_expr_entry_set_flags (state->add_entry,
982 983
				  GNM_EE_SINGLE_RANGE,
				  GNM_EE_MASK);
984 985 986
	gtk_widget_set_hexpand (GTK_WIDGET (state->add_entry), TRUE);
	gtk_grid_attach (grid, GTK_WIDGET (state->add_entry),
			  0, 5, 1, 1);
Morten Welinder's avatar
Morten Welinder committed
987
	gnm_editable_enters (GTK_WINDOW (state->dialog),
988 989 990
				  GTK_WIDGET (state->add_entry));
	gtk_widget_show (GTK_WIDGET (state->add_entry));

991
	/* Set-up tree view */
Morten Welinder's avatar
Morten Welinder committed
992
	scrolled = go_gtk_builder_get_widget (state->gui, "scrolled_cell_sort_list");
993
	state->model = gtk_list_store_new (NUM_COLUMNS, G_TYPE_STRING,
994
					   G_TYPE_STRING, G_TYPE_BOOLEAN,
995
					   GDK_TYPE_PIXBUF, G_TYPE_BOOLEAN,
996
					   G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
997
					   G_TYPE_INT);
998 999 1000 1001
	state->treeview = GTK_TREE_VIEW (
		gtk_tree_view_new_with_model (GTK_TREE_MODEL (state->model)));
	state->selection = gtk_tree_view_get_selection (state->treeview);
	gtk_tree_selection_set_mode (state->selection, GTK_SELECTION_BROWSE);
1002
	g_signal_connect_swapped (state->selection,
1003 1004
				  "changed",
				  G_CALLBACK (cb_sort_selection_changed), state);
1005

Morten Welinder's avatar
Morten Welinder committed
1006 1007 1008 1009
	state->header_column = gtk_tree_view_column_new_with_attributes
		(_("Header"),
		 gtk_cell_renderer_text_new (),
		 "text", ITEM_HEADER, NULL);
1010 1011
	gtk_tree_view_append_column (state->treeview, state->header_column);

Morten Welinder's avatar
Morten Welinder committed
1012 1013 1014 1015
	column = gtk_tree_view_column_new_with_attributes
		(_("Row/Column"),
		 gtk_cell_renderer_text_new (),
		 "text", ITEM_NAME, NULL);
1016 1017
	gtk_tree_view_append_column (state->treeview, column);

Morten Welinder's avatar
Morten Welinder committed
1018
	renderer = gnm_cell_renderer_toggle_new ();
1019
	g_signal_connect (G_OBJECT (renderer),
1020 1021
			  "toggled",
			  G_CALLBACK (cb_toggled_descending), state);
1022
	column = gtk_tree_view_column_new_with_attributes ("",
1023
							   renderer,
Morten Welinder's avatar
Morten Welinder committed
1024
							   "active", ITEM_DESCENDING,
1025 1026
							   "pixbuf", ITEM_DESCENDING_IMAGE,
							   NULL);
1027 1028
	gtk_tree_view_append_column (state->treeview, column);

1029
	renderer = gtk_cell_renderer_toggle_new ();
1030
	g_signal_connect (G_OBJECT (renderer),
1031 1032
			  "toggled",
			  G_CALLBACK (cb_toggled_case_sensitive), state);
1033
	column = gtk_tree_view_column_new_with_attributes (_("Case Sensitive"),
1034 1035 1036 1037
							   renderer,
							   "active", ITEM_CASE_SENSITIVE, NULL);
	gtk_tree_view_append_column (state->treeview, column);

Jody Goldberg's avatar
Jody Goldberg committed
1038
	gtk_tree_view_columns_autosize (state->treeview);
1039 1040


Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
1041
	g_signal_connect (G_OBJECT (state->treeview),
1042 1043
			  "key_press_event",
			  G_CALLBACK (cb_treeview_keypress), state);
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
1044
	g_signal_connect (G_OBJECT (state->treeview),
1045 1046
			  "button_press_event",
			  G_CALLBACK (cb_treeview_button_press), state);
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
1047 1048
#if 0
	/* We are currently not supporting `by-value' vs not. */
1049
	renderer = gtk_cell_renderer_toggle_new ();
1050
	g_signal_connect (G_OBJECT (renderer),
1051 1052
			  "toggled",
			  G_CALLBACK (cb_toggled_sort_by_value), state);
1053
	column = gtk_tree_view_column_new_with_attributes (_("By Value"),
1054 1055 1056
							   renderer,
							   "active", ITEM_SORT_BY_VALUE, NULL);
	gtk_tree_view_append_column (state->treeview, column);
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
1057
#endif
1058

1059 1060
	gtk_tree_view_set_reorderable (state->treeview,TRUE);

1061 1062 1063
	gtk_container_add (GTK_CONTAINER (scrolled), GTK_WIDGET (state->treeview));
	gtk_widget_show (GTK_WIDGET (state->treeview));

1064
	/* Set-up other widgets */
Morten Welinder's avatar
Morten Welinder committed
1065 1066
	state->cell_sort_row_rb = go_gtk_builder_get_widget (state->gui, "cell_sort_row_rb");
	state->cell_sort_col_rb = go_gtk_builder_get_widget (state->gui, "cell_sort_col_rb");
1067
	g_signal_connect_swapped (G_OBJECT (state->cell_sort_row_rb),
1068
				  "toggled",
1069
				  G_CALLBACK (cb_update_to_new_range), state);
1070

Morten Welinder's avatar
Morten Welinder committed
1071
	state->cell_sort_header_check = go_gtk_builder_get_widget (state->gui,
1072
							      "cell_sort_header_check");
1073
	g_signal_connect_swapped (G_OBJECT (state->cell_sort_header_check),
1074 1075
				  "toggled",
				  G_CALLBACK (cb_sort_header_check), state);
1076

Morten Welinder's avatar
Morten Welinder committed
1077
	state->retain_format_check = go_gtk_builder_get_widget (state->gui,
1078
							   "retain_format_button");
1079
	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (state->retain_format_check),
1080
				      gnm_conf_get_core_sort_default_retain_formats ());
1081 1082


1083
	/* Set-up buttons */
Morten Welinder's avatar
Morten Welinder committed
1084
	state->up_button = go_gtk_builder_get_widget (state->gui, "up_button");
1085 1086 1087
	g_signal_connect_swapped (G_OBJECT (state->up_button),
				  "clicked",
				  G_CALLBACK (cb_up), state);
Morten Welinder's avatar
Morten Welinder committed
1088
	state->down_button = go_gtk_builder_get_widget (state->gui, "down_button");
1089 1090 1091
	g_signal_connect_swapped (G_OBJECT (state->down_button),
				  "clicked",
				  G_CALLBACK (cb_down), state);
Morten Welinder's avatar
Morten Welinder committed
1092
	state->add_button = go_gtk_builder_get_widget (state->gui, "add_button");
1093 1094 1095
	g_signal_connect_swapped (G_OBJECT (state->add_button),
				  "clicked",
				  G_CALLBACK (cb_add_clicked), state);
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
1096
	gtk_widget_set_sensitive (state->add_button, TRUE);
Morten Welinder's avatar
Morten Welinder committed
1097
	state->delete_button = go_gtk_builder_get_widget (state->gui, "delete_button");
Jody Goldberg's avatar
Jody Goldberg committed
1098
	g_signal_connect (G_OBJECT (state->delete_button),
1099 1100
			  "clicked",
			  G_CALLBACK (cb_delete_clicked), state);
1101 1102
	gtk_widget_set_sensitive (state->delete_button, FALSE);

Morten Welinder's avatar
Morten Welinder committed
1103
	state->clear_button = go_gtk_builder_get_widget (state->gui, "clear_button");
1104 1105 1106
	g_signal_connect_swapped (G_OBJECT (state->clear_button),
				  "clicked",
				  G_CALLBACK (cb_clear_clicked), state);
1107 1108
	gtk_widget_set_sensitive (state->clear_button, FALSE);

1109 1110 1111 1112 1113
	gtk_button_set_alignment (GTK_BUTTON (state->up_button), 0., .