dialog-tabulate.c 9.27 KB
Newer Older
Morten Welinder's avatar
Morten Welinder committed
1 2 3 4 5
/*
 * dialog-tabulate.c:
 *   Dialog for making tables of function dependcies.
 *
 * Author:
Morten Welinder's avatar
Morten Welinder committed
6
 *   COPYRIGHT (C) Morten Welinder (terra@gnome.org)
7
 *
8 9 10 11
 * 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) version 3.
12 13 14 15 16 17 18 19
 *
 * 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
 * along with this program; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
21
 * USA
Morten Welinder's avatar
Morten Welinder committed
22
 */
23

Morten Welinder's avatar
Morten Welinder committed
24
#include <gnumeric-config.h>
25
#include <glib/gi18n-lib.h>
Morten Welinder's avatar
Morten Welinder committed
26
#include <gnumeric.h>
27 28
#include <dialogs/dialogs.h>
#include <dialogs/help.h>
29

Morten Welinder's avatar
Morten Welinder committed
30
#include <gui-util.h>
Morten Welinder's avatar
Morten Welinder committed
31
#include <widgets/gnm-expr-entry.h>
32
#include <tools/tabulate.h>
33
#include <wbc-gtk.h>
34 35 36 37 38 39 40 41 42 43 44 45 46 47
#include <ranges.h>
#include <value.h>
#include <sheet.h>
#include <mstyle.h>
#include <workbook.h>
#include <mathfunc.h>
#include <cell.h>
#include <commands.h>
#include <gnm-format.h>
#include <number-match.h>
#include <mstyle.h>
#include <style-border.h>
#include <sheet-style.h>
#include <style-color.h>
Morten Welinder's avatar
Morten Welinder committed
48

49 50
#include <string.h>

Morten Welinder's avatar
Morten Welinder committed
51 52 53 54 55 56 57 58 59 60 61 62
#define TABULATE_KEY "tabulate-dialog"

/* ------------------------------------------------------------------------- */

enum {
	COL_CELL = 0,
	COL_MIN,
	COL_MAX,
	COL_STEP
};

typedef struct {
63
	WBCGtk *wbcg;
Morten Welinder's avatar
Morten Welinder committed
64 65
	Sheet *sheet;

66
	GtkBuilder *gui;
67
	GtkDialog *dialog;
Morten Welinder's avatar
Morten Welinder committed
68

69
	GtkGrid *grid;
70
	GnmExprEntry *resultrangetext;
Morten Welinder's avatar
Morten Welinder committed
71 72
} DialogState;

73
static const char * const mode_group[] = {
Morten Welinder's avatar
Morten Welinder committed
74 75
	"mode_visual",
	"mode_coordinate",
76
	NULL
Morten Welinder's avatar
Morten Welinder committed
77 78 79 80 81
};

/* ------------------------------------------------------------------------- */

static void
82
non_model_dialog (WBCGtk *wbcg,
83
		  GtkDialog *dialog,
Morten Welinder's avatar
Morten Welinder committed
84 85
		  const char *key)
{
Morten Welinder's avatar
Morten Welinder committed
86
	gnm_keyed_dialog (wbcg, GTK_WINDOW (dialog), key);
Morten Welinder's avatar
Morten Welinder committed
87 88 89 90

	gtk_widget_show (GTK_WIDGET (dialog));
}

91
static GnmCell *
92
single_cell (Sheet *sheet, GnmExprEntry *gee)
Morten Welinder's avatar
Morten Welinder committed
93 94 95
{
	int col, row;
	gboolean issingle;
Jody Goldberg's avatar
Jody Goldberg committed
96
	GnmValue *v = gnm_expr_entry_parse_as_value (gee, sheet);
Morten Welinder's avatar
Morten Welinder committed
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111

	if (!v) return NULL;

	col = v->v_range.cell.a.col;
	row = v->v_range.cell.a.row;
	issingle = (col == v->v_range.cell.b.col && row == v->v_range.cell.b.row);

	value_release (v);

	if (issingle)
		return sheet_cell_fetch (sheet, col, row);
	else
		return NULL;
}

112
static int
113
get_grid_float_entry (GtkGrid *g, int y, int x, GnmCell *cell, gnm_float *number,
114
		       GtkEntry **wp, gboolean with_default, gnm_float default_float)
115
{
116
	GOFormat const *format;
117
	GtkWidget *w = gtk_grid_get_child_at (g, x, y + 1);
118 119 120 121 122

	g_return_val_if_fail (GTK_IS_ENTRY (w), 3);

	*wp = GTK_ENTRY (w);
	format = gnm_style_get_format (gnm_cell_get_style (cell));
123

124 125 126 127
	return (with_default?
	        entry_to_float_with_format_default (*wp, number, TRUE, format,
	                                            default_float):
			entry_to_float_with_format (*wp, number, TRUE, format));
128 129
}

Morten Welinder's avatar
Morten Welinder committed
130
static void
131
cb_dialog_destroy (DialogState *dd)
Morten Welinder's avatar
Morten Welinder committed
132
{
133
	g_object_unref (dd->gui);
134 135
	memset (dd, 0, sizeof (*dd));
	g_free (dd);
Morten Welinder's avatar
Morten Welinder committed
136 137 138
}

static void
Morten Welinder's avatar
Morten Welinder committed
139
cancel_clicked (G_GNUC_UNUSED GtkWidget *widget, DialogState *dd)
Morten Welinder's avatar
Morten Welinder committed
140
{
141
	GtkDialog *dialog = dd->dialog;
Morten Welinder's avatar
Morten Welinder committed
142 143 144 145
	gtk_widget_destroy (GTK_WIDGET (dialog));
}

static void
Morten Welinder's avatar
Morten Welinder committed
146
tabulate_ok_clicked (G_GNUC_UNUSED GtkWidget *widget, DialogState *dd)
Morten Welinder's avatar
Morten Welinder committed
147
{
148
	GtkDialog *dialog = dd->dialog;
149
	GnmCell *resultcell;
Morten Welinder's avatar
Morten Welinder committed
150 151 152
	int dims = 0;
	int row;
	gboolean with_coordinates;
153
	GnmTabulateInfo *data;
154 155
	/* we might get the 4 below from the positon of some of the widgets inside the grid */
	int nrows = 4;
156 157
	GnmCell **cells;
	gnm_float *minima, *maxima, *steps;
Morten Welinder's avatar
Morten Welinder committed
158

159 160 161 162
	cells = g_new (GnmCell *, nrows);
	minima = g_new (gnm_float, nrows);
	maxima = g_new (gnm_float, nrows);
	steps = g_new (gnm_float, nrows);
Morten Welinder's avatar
Morten Welinder committed
163

164
	for (row = 1; row < nrows; row++) {
165
		GtkEntry *e_w;
166
		GnmExprEntry *w = GNM_EXPR_ENTRY (gtk_grid_get_child_at (dd->grid, COL_CELL, row + 1));
Morten Welinder's avatar
Morten Welinder committed
167

168
		if (!w || gnm_expr_entry_is_blank (w))
Morten Welinder's avatar
Morten Welinder committed
169 170
			continue;

171
		cells[dims] = single_cell (dd->sheet, w);
Morten Welinder's avatar
Morten Welinder committed
172
		if (!cells[dims]) {
Jody Goldberg's avatar
Jody Goldberg committed
173
			go_gtk_notice_dialog (GTK_WINDOW (dd->dialog),
174
					 GTK_MESSAGE_ERROR,
Morten Welinder's avatar
Morten Welinder committed
175
					 _("You should introduce a single valid cell as dependency cell"));
176
			gnm_expr_entry_grab_focus (GNM_EXPR_ENTRY (w), TRUE);
Morten Welinder's avatar
Morten Welinder committed
177 178
			goto error;
		}
179
		if (gnm_cell_has_expr (cells[dims])) {
Jody Goldberg's avatar
Jody Goldberg committed
180
			go_gtk_notice_dialog (GTK_WINDOW (dd->dialog),
181
					 GTK_MESSAGE_ERROR,
Morten Welinder's avatar
Morten Welinder committed
182
					 _("The dependency cells should not contain an expression"));
183
			gnm_expr_entry_grab_focus (GNM_EXPR_ENTRY (w), TRUE);
Morten Welinder's avatar
Morten Welinder committed
184 185 186
			goto error;
		}

187
		if (get_grid_float_entry (dd->grid, row, COL_MIN, cells[dims],
188
					   &(minima[dims]), &e_w, FALSE, 0.0)) {
Jody Goldberg's avatar
Jody Goldberg committed
189
			go_gtk_notice_dialog (GTK_WINDOW (dd->dialog),
190
					 GTK_MESSAGE_ERROR,
Morten Welinder's avatar
Morten Welinder committed
191
					 _("You should introduce a valid number as minimum"));
192
			focus_on_entry (e_w);
Morten Welinder's avatar
Morten Welinder committed
193 194 195
			goto error;
		}

196
		if (get_grid_float_entry (dd->grid, row, COL_MAX, cells[dims],
197
					   &(maxima[dims]), &e_w, FALSE, 0.0)) {
Jody Goldberg's avatar
Jody Goldberg committed
198
			go_gtk_notice_dialog (GTK_WINDOW (dd->dialog),
199
					 GTK_MESSAGE_ERROR,
Morten Welinder's avatar
Morten Welinder committed
200
					 _("You should introduce a valid number as maximum"));
201
			focus_on_entry (e_w);
Morten Welinder's avatar
Morten Welinder committed
202 203 204 205
			goto error;
		}

		if (maxima[dims] < minima[dims]) {
Jody Goldberg's avatar
Jody Goldberg committed
206
			go_gtk_notice_dialog (GTK_WINDOW (dd->dialog),
207
					 GTK_MESSAGE_ERROR,
Morten Welinder's avatar
Morten Welinder committed
208
					 _("The maximum value should be bigger than the minimum"));
209
			focus_on_entry (e_w);
Morten Welinder's avatar
Morten Welinder committed
210 211 212
			goto error;
		}

213
		if (get_grid_float_entry (dd->grid, row, COL_STEP, cells[dims],
214
					   &(steps[dims]), &e_w, TRUE, 1.0)) {
Jody Goldberg's avatar
Jody Goldberg committed
215
			go_gtk_notice_dialog (GTK_WINDOW (dd->dialog),
216
					 GTK_MESSAGE_ERROR,
217 218 219 220
					 _("You should introduce a valid number as step size"));
			focus_on_entry (e_w);
			goto error;
		}
Morten Welinder's avatar
Morten Welinder committed
221

222
		if (steps[dims] <= 0) {
Jody Goldberg's avatar
Jody Goldberg committed
223
			go_gtk_notice_dialog (GTK_WINDOW (dd->dialog),
224
					 GTK_MESSAGE_ERROR,
225 226 227 228
					 _("The step size should be positive"));
			focus_on_entry (e_w);
			goto error;
		}
Morten Welinder's avatar
Morten Welinder committed
229 230 231 232 233

		dims++;
	}

	if (dims == 0) {
Jody Goldberg's avatar
Jody Goldberg committed
234
		go_gtk_notice_dialog (GTK_WINDOW (dd->dialog),
235
				 GTK_MESSAGE_ERROR,
Morten Welinder's avatar
Morten Welinder committed
236 237 238 239 240
				 _("You should introduce one or more dependency cells"));
		goto error;
	}

	{
241
		resultcell = single_cell (dd->sheet, dd->resultrangetext);
Morten Welinder's avatar
Morten Welinder committed
242 243

		if (!resultcell) {
Jody Goldberg's avatar
Jody Goldberg committed
244
			go_gtk_notice_dialog (GTK_WINDOW (dd->dialog),
245
					 GTK_MESSAGE_ERROR,
Morten Welinder's avatar
Morten Welinder committed
246
					 _("You should introduce a single valid cell as result cell"));
247
			gnm_expr_entry_grab_focus (dd->resultrangetext, TRUE);
Morten Welinder's avatar
Morten Welinder committed
248 249 250
			goto error;
		}

251
		if (!gnm_cell_has_expr (resultcell)) {
Jody Goldberg's avatar
Jody Goldberg committed
252
			go_gtk_notice_dialog (GTK_WINDOW (dd->dialog),
253
					 GTK_MESSAGE_ERROR,
Morten Welinder's avatar
Morten Welinder committed
254
					 _("The target cell should contain an expression"));
255
			gnm_expr_entry_grab_focus (dd->resultrangetext, TRUE);
Morten Welinder's avatar
Morten Welinder committed
256 257 258 259 260
			goto error;
		}
	}

	{
261
		int i = gnm_gui_group_value (dd->gui, mode_group);
Morten Welinder's avatar
Morten Welinder committed
262 263 264
		with_coordinates = (i == -1) ? TRUE : (gboolean)i;
	}

265
	data = g_new (GnmTabulateInfo, 1);
266 267 268 269 270 271 272 273
	data->target = resultcell;
	data->dims = dims;
	data->cells = cells;
	data->minima = minima;
	data->maxima = maxima;
	data->steps = steps;
	data->with_coordinates = with_coordinates;

Morten Welinder's avatar
Morten Welinder committed
274
	if (!cmd_tabulate (GNM_WBC (dd->wbcg), data)) {
275 276 277
		gtk_widget_destroy (GTK_WIDGET (dialog));
		return;
	}
Morten Welinder's avatar
Morten Welinder committed
278

279
	g_free (data);
Morten Welinder's avatar
Morten Welinder committed
280 281 282 283 284 285 286 287
 error:
	g_free (minima);
	g_free (maxima);
	g_free (steps);
	g_free (cells);
}

void
288
dialog_tabulate (WBCGtk *wbcg, Sheet *sheet)
Morten Welinder's avatar
Morten Welinder committed
289
{
290
	GtkBuilder *gui;
291
	GtkDialog *dialog;
Morten Welinder's avatar
Morten Welinder committed
292 293 294 295 296 297
	DialogState *dd;
	int i;

	g_return_if_fail (wbcg != NULL);

	/* Only one guru per workbook. */
298
	if (wbc_gtk_get_guru (wbcg))
Morten Welinder's avatar
Morten Welinder committed
299 300
		return;

Morten Welinder's avatar
Morten Welinder committed
301
	if (gnm_dialog_raise_if_exists (wbcg, TABULATE_KEY))
Morten Welinder's avatar
Morten Welinder committed
302
		return;
303
	gui = gnm_gtk_builder_load ("res:ui/tabulate.ui", NULL, GO_CMD_CONTEXT (wbcg));
Morten Welinder's avatar
Morten Welinder committed
304 305 306
        if (gui == NULL)
                return;

Morten Welinder's avatar
Morten Welinder committed
307
	dialog = GTK_DIALOG (go_gtk_builder_get_widget (gui, "tabulate_dialog"));
Morten Welinder's avatar
Morten Welinder committed
308 309 310 311 312

	dd = g_new (DialogState, 1);
	dd->wbcg = wbcg;
	dd->gui = gui;
	dd->dialog = dialog;
313
	dd->sheet = sheet;
Morten Welinder's avatar
Morten Welinder committed
314

315 316 317
	dd->grid = GTK_GRID (go_gtk_builder_get_widget (gui, "main-grid"));
	/* we might get the 4 below from the positon of some of the widgets inside the grid */
	for (i = 1; i < 4; i++) {
318
		GnmExprEntry *ge = gnm_expr_entry_new (wbcg, TRUE);
319
		gnm_expr_entry_set_flags (ge,
320 321
			GNM_EE_SINGLE_RANGE | GNM_EE_SHEET_OPTIONAL,
			GNM_EE_MASK);
Morten Welinder's avatar
Morten Welinder committed
322

323 324
		gtk_grid_attach (dd->grid, GTK_WIDGET (ge), COL_CELL, i + 1, 1, 1);
		gtk_widget_set_margin_left (GTK_WIDGET (ge), 18);
Morten Welinder's avatar
Morten Welinder committed
325 326 327
		gtk_widget_show (GTK_WIDGET (ge));
	}

328
	dd->resultrangetext = gnm_expr_entry_new (wbcg, TRUE);
329
	gnm_expr_entry_set_flags (dd->resultrangetext,
330 331
		GNM_EE_SINGLE_RANGE | GNM_EE_SHEET_OPTIONAL,
		GNM_EE_MASK);
332 333
	gtk_grid_attach (dd->grid, GTK_WIDGET (dd->resultrangetext), 0, 6, 4, 1);
	gtk_widget_set_margin_left (GTK_WIDGET (dd->resultrangetext), 18);
Morten Welinder's avatar
Morten Welinder committed
334 335
	gtk_widget_show (GTK_WIDGET (dd->resultrangetext));

Morten Welinder's avatar
Morten Welinder committed
336
	g_signal_connect (G_OBJECT (go_gtk_builder_get_widget (gui, "ok_button")),
337
		"clicked",
338
		G_CALLBACK (tabulate_ok_clicked), dd);
339

Morten Welinder's avatar
Morten Welinder committed
340
	g_signal_connect (G_OBJECT (go_gtk_builder_get_widget (gui, "cancel_button")),
341 342
		"clicked",
		G_CALLBACK (cancel_clicked), dd);
343
/* FIXME: Add correct helpfile address */
Morten Welinder's avatar
Morten Welinder committed
344
	gnm_init_help_button (
Morten Welinder's avatar
Morten Welinder committed
345
		go_gtk_builder_get_widget (gui, "help_button"),
346
		GNUMERIC_HELP_LINK_TABULATE);
347 348
	g_object_set_data_full (G_OBJECT (dialog),
		"state", dd, (GDestroyNotify) cb_dialog_destroy);
Morten Welinder's avatar
Morten Welinder committed
349

350 351 352
	gnm_dialog_setup_destroy_handlers (dialog, wbcg,
					   GNM_DIALOG_DESTROY_SHEET_REMOVED);

353
	gtk_widget_show_all (gtk_dialog_get_content_area (dialog));
354
	wbc_gtk_attach_guru (wbcg, GTK_WIDGET (dialog));
Morten Welinder's avatar
Morten Welinder committed
355 356 357

	non_model_dialog (wbcg, dialog, TABULATE_KEY);
}
358