1 /*
2  * dialog-autoformat.c : implementation of the autoformat dialog
3  *
4  * Author : Almer S. Tigelaar <almer@gnome.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <https://www.gnu.org/licenses/>.
18  */
19 
20 /*
21  * WORKING NOTE : Once the edit dialog is ready, search for FIXME and
22  * remove the disabling of new/edit/remove buttons!
23  */
24 
25 #include <gnumeric-config.h>
26 #include <glib/gi18n-lib.h>
27 #include <gnumeric.h>
28 #include <dialogs/dialogs.h>
29 #include <dialogs/help.h>
30 
31 #include <gui-util.h>
32 #include <mstyle.h>
33 #include <style-border.h>
34 #include <value.h>
35 #include <preview-grid-impl.h>
36 #include <format-template.h>
37 #include <file-autoft.h>
38 #include <command-context.h>
39 #include <workbook-control.h>
40 #include <workbook.h>
41 #include <wbc-gtk.h>
42 #include <commands.h>
43 #include <selection.h>
44 #include <ranges.h>
45 
46 #include <goffice/goffice.h>
47 #include <gsf/gsf-impl-utils.h>
48 #include <string.h>
49 
50 /* Table to show for
51  * previews, please don't make this larger than 5x5
52  */
53 #define PREVIEW_COLS 5
54 #define PREVIEW_ROWS 5
55 #define NUM_PREVIEWS 6
56 #define DEFAULT_COL_WIDTH  52
57 #define DEFAULT_ROW_HEIGHT 17
58 #define BORDER	     7
59 #define INNER_BORDER 5
60 #define TOTAL_WIDTH  (DEFAULT_COL_WIDTH * PREVIEW_COLS)
61 #define TOTAL_HEIGHT (DEFAULT_ROW_HEIGHT * PREVIEW_ROWS)
62 
63 /* Keep these strings very short.
64    They are used as a sample data for a sheet, so you can put anything here
65    ("One", "Two", "Three" for example) */
66 static char const *const
67 demotable[PREVIEW_ROWS][PREVIEW_COLS] = {
68 	{ N_(" ")    , N_("Jan"), N_("Feb"), N_("Mar"), N_("Total") },
69 	{ N_("North"),   N_("6"),  N_("13"),  N_("20"),    N_("39") },
70 	{ N_("South"),  N_("12"),   N_("4"),  N_("17"),    N_("33") },
71 	{ N_("West") ,   N_("8"),   N_("2"),   N_("0"),    N_("10") },
72 	{ N_("Total"),  N_("26"),  N_("19"),  N_("37"),    N_("81") }
73 };
74 
75 typedef struct {
76 	Workbook           *wb;                              /* Workbook we are working on */
77 	WBCGtk *wbcg;
78 	GocItem		   *grid[NUM_PREVIEWS];              /* Previewgrid's */
79 	GocItem		   *selrect;                         /* Selection rectangle */
80 	GSList             *templates;                       /* List of GnmFT's */
81 	GnmFT  *selected_template;               /* The currently selected template */
82 	GList              *category_groups;                 /* List of groups of categories */
83 
84 	GnmFTCategoryGroup *current_category_group; /* Currently selected category group */
85 
86 	int               preview_top;       /* Top index of the previewlist */
87 	int               preview_index;     /* Selected canvas in previewlist */
88 	gboolean          previews_locked;   /* If true, the preview_free and preview_load will not function */
89 	gboolean          more_down;         /* If true, more was clicked and the button caption is now 'Less' */
90 
91 	/*
92 	 * Gui elements
93 	 */
94 	GtkDialog      *dialog;
95 
96 	GtkComboBox    *category;
97 
98 	GocCanvas	 *canvas[NUM_PREVIEWS];
99 	GtkFrame         *frame[NUM_PREVIEWS];
100 	GtkScrollbar    *scroll;
101 	GtkCheckMenuItem *gridlines;
102 
103 	GtkEntry       *info_name, *info_author, *info_cat;
104 	GtkTextView    *info_descr;
105 
106 	GtkCheckMenuItem *number, *border, *font, *patterns, *alignment;
107 
108 	struct {
109 		GtkCheckMenuItem *left;
110 		GtkCheckMenuItem *right;
111 		GtkCheckMenuItem *top;
112 		GtkCheckMenuItem *bottom;
113 	} edges;
114 
115 	GtkButton      *ok, *cancel;
116 } AutoFormatState;
117 
118 /********************************************************************************/
119 
120 typedef struct {
121 	GnmPreviewGrid base;
122 	GnmFT *ft;
123 } AutoFormatGrid;
124 typedef GnmPreviewGridClass AutoFormatGridClass;
125 
126 static GnmStyle *
afg_get_cell_style(GnmPreviewGrid * pg,int col,int row)127 afg_get_cell_style (GnmPreviewGrid *pg, int col, int row)
128 {
129 	/* If this happens to be NULL the default style
130 	 * will automatically be used. */
131 	AutoFormatGrid *ag = (AutoFormatGrid *) pg;
132 	return gnm_ft_get_style (ag->ft, row, col);
133 }
134 
135 static GnmValue *
afg_get_cell_value(G_GNUC_UNUSED GnmPreviewGrid * pg,int col,int row)136 afg_get_cell_value (G_GNUC_UNUSED GnmPreviewGrid *pg, int col, int row)
137 {
138 	char const *text;
139 	char *endptr = NULL;
140 	double tmp;
141 
142 	if (row >= PREVIEW_ROWS || col >= PREVIEW_COLS)
143 		return NULL;
144 
145 	text = _(demotable[row][col]);
146 	tmp = go_strtod (text, &endptr);
147 
148 	if (*endptr == '\0')
149 		return value_new_float (tmp);
150 	return value_new_string (text);
151 }
152 
153 static void
auto_format_grid_class_init(GnmPreviewGridClass * klass)154 auto_format_grid_class_init (GnmPreviewGridClass *klass)
155 {
156 	klass->get_cell_style = afg_get_cell_style;
157 	klass->get_cell_value = afg_get_cell_value;
158 }
159 
GSF_CLASS(AutoFormatGrid,auto_format_grid,auto_format_grid_class_init,NULL,gnm_preview_grid_get_type ())160 static GSF_CLASS (AutoFormatGrid, auto_format_grid,
161 		  auto_format_grid_class_init, NULL,
162 		  gnm_preview_grid_get_type())
163 
164 static GocItem *
165 auto_format_grid_new (AutoFormatState *state, int i, GnmFT *ft)
166 {
167 	GocItem *item = goc_item_new (
168 		goc_canvas_get_root (state->canvas[i]),
169 		auto_format_grid_get_type (),
170 		"render-gridlines",	gtk_check_menu_item_get_active (state->gridlines),
171 		"default-col-width",	DEFAULT_COL_WIDTH,
172 		"default-row-height",	DEFAULT_ROW_HEIGHT,
173 		"x",			0.,
174 		"y",			0.,
175 		NULL);
176 	((AutoFormatGrid *) item)->ft = ft;
177 	return item;
178 }
179 /********************************************************************************
180  * UTILITY FUNCTIONS
181  ********************************************************************************/
182 
183 static void
templates_free(AutoFormatState * state)184 templates_free (AutoFormatState *state)
185 {
186 	GSList *ptr;
187 
188 	g_return_if_fail (state != NULL);
189 
190 	for (ptr = state->templates; ptr != NULL ; ptr = ptr->next)
191 		gnm_ft_free (ptr->data);
192 	g_slist_free (state->templates);
193 	state->templates = NULL;
194 }
195 
196 /**
197  * templates_load:
198  * @state: AutoFormatState
199  *
200  * This function will load the templates in the currently selected
201  * category group (it looks at state->category_groups to determine the selection)
202  *
203  * Returns: %TRUE if all went well, %FALSE otherwise.
204  **/
205 static gboolean
templates_load(AutoFormatState * state)206 templates_load (AutoFormatState *state)
207 {
208 	GSList *l;
209 	gint n_templates;
210 
211 	g_return_val_if_fail (state != NULL, FALSE);
212 
213 	if (state->category_groups == NULL)
214 		return FALSE;
215 
216 	state->templates = gnm_ft_category_group_get_templates_list (
217 		state->current_category_group, GO_CMD_CONTEXT (state->wbcg));
218 	for (l = state->templates; l != NULL; l = l->next) {
219 		GnmFT *ft = l->data;
220 		range_init (&ft->dimension,
221 			0, 0, PREVIEW_COLS - 1, PREVIEW_ROWS - 1);
222 		ft->invalidate_hash = TRUE;
223 	}
224 	n_templates = g_slist_length (state->templates);
225 
226 	/*
227 	 * We need to temporary lock the preview loading/freeing or
228 	 * else our scrollbar will trigger an event (value_changed) and create the
229 	 * previews. (which we don't want to happen at this moment)
230 	 */
231 	state->previews_locked = TRUE;
232 	{
233 		GtkAdjustment *adjustment = gtk_range_get_adjustment (GTK_RANGE (state->scroll));
234 		gtk_adjustment_configure (adjustment,
235 					  0, 0, n_templates / 2,
236 					  1, 3, 3);
237 	}
238 	state->previews_locked = FALSE;
239 
240 	/*
241 	 * Hide the scrollbar when it's not needed
242 	 */
243 	gtk_widget_set_visible (GTK_WIDGET (state->scroll),
244 				n_templates > NUM_PREVIEWS);
245 
246 	return TRUE;
247 }
248 
249 /**
250  * previews_free:
251  * @state: AutoFormatState
252  *
253  * This function will free all previews.
254  **/
255 static void
previews_free(AutoFormatState * state)256 previews_free (AutoFormatState *state)
257 {
258 	int i;
259 
260 	if (state->previews_locked)
261 		return;
262 
263 	if (state->selrect) {
264 		goc_item_destroy (state->selrect);
265 		state->selrect = NULL;
266 	}
267 
268 	for (i = 0; i < NUM_PREVIEWS; i++) {
269 		GocItem *item = state->grid[i];
270 		if (item) {
271 			goc_item_destroy (state->grid[i]);
272 			state->grid[i] = NULL;
273 		}
274 	}
275 }
276 
277 /**
278  * previews_load:
279  * @state: AutoFormatState
280  * @topindex: The index of the template to be displayed in the upper left corner
281  *
282  * This function will create grids and rects for each canvas and associate
283  * them with the right format templates.
284  * NOTE : if state->preview_locked is %TRUE this function will do nothing,
285  *        this is handy in situation where signals can cause previews_load to be
286  *        called before previews_free.
287  **/
288 static void
previews_load(AutoFormatState * state,int topindex)289 previews_load (AutoFormatState *state, int topindex)
290 {
291 	GSList *iterator, *start;
292 	int i, count = topindex;
293 
294 	g_return_if_fail (state != NULL);
295 
296 	if (state->previews_locked)
297 		return;
298 
299 	iterator = state->templates;
300 	start = iterator;
301 	while (iterator && count > 0) {
302 		iterator = g_slist_next (iterator);
303 		start = iterator;
304 		count--;
305 	}
306 
307 	for (i = 0; i < NUM_PREVIEWS; i++) {
308 		if (start == NULL) {
309 			gtk_widget_hide (GTK_WIDGET (state->canvas[i]));
310 			gtk_frame_set_shadow_type (state->frame[i], GTK_SHADOW_NONE);
311 		} else {
312 			GnmFT *ft = start->data;
313 
314 			state->grid[i] = auto_format_grid_new (state, i, ft);
315 
316 			/* Are we selected? Then draw a selection rectangle */
317 			if (topindex + i == state->preview_index) {
318 				GOStyle *style;
319 				g_return_if_fail (state->selrect == NULL);
320 
321 				state->selrect = goc_item_new (goc_canvas_get_root (state->canvas[i]),
322 					GOC_TYPE_RECTANGLE,
323 					"x", (double)(-INNER_BORDER),
324 					"y", (double)(-INNER_BORDER),
325 					"width", (double)(TOTAL_WIDTH + 2 * INNER_BORDER),
326 					"height", (double)(TOTAL_HEIGHT + 2 * INNER_BORDER),
327 					NULL);
328 				style = go_styled_object_get_style (GO_STYLED_OBJECT (state->selrect));
329 				style->line.width = 3.;
330 				style->line.color = GO_COLOR_RED;
331 				style->fill.pattern.back = 0;
332 
333 				gtk_frame_set_shadow_type (state->frame[i], GTK_SHADOW_IN);
334 			} else
335 				gtk_frame_set_shadow_type (state->frame[i], GTK_SHADOW_ETCHED_IN);
336 
337 			goc_canvas_scroll_to (state->canvas[i],
338 				-BORDER, -BORDER);
339 
340 			gtk_widget_set_tooltip_text
341 				(GTK_WIDGET (state->canvas[i]),
342 				 _(ft->name));
343 
344 			gtk_widget_show (GTK_WIDGET (state->canvas[i]));
345 			start = g_slist_next (start);
346 		}
347 	}
348 
349 	state->preview_top = topindex;
350 }
351 
352 /********************************************************************************
353  * SIGNAL HANDLERS
354  ********************************************************************************/
355 
356 static void
cb_ok_clicked(G_GNUC_UNUSED GtkButton * button,AutoFormatState * state)357 cb_ok_clicked (G_GNUC_UNUSED GtkButton *button,
358 	       AutoFormatState *state)
359 {
360 	if (state->selected_template)
361 		cmd_selection_autoformat (GNM_WBC (state->wbcg),
362 			gnm_ft_clone (state->selected_template));
363 
364 	gtk_widget_destroy (GTK_WIDGET (state->dialog));
365 }
366 
367 static void
cb_autoformat_destroy(AutoFormatState * state)368 cb_autoformat_destroy (AutoFormatState *state)
369 {
370 	templates_free (state);
371 	gnm_ft_category_group_list_free (state->category_groups);
372 	g_free (state);
373 }
374 
375 static void
cb_scroll_value_changed(GtkAdjustment * adjustment,AutoFormatState * state)376 cb_scroll_value_changed (GtkAdjustment *adjustment, AutoFormatState *state)
377 {
378 	previews_free (state);
379 	previews_load (state, rint (gtk_adjustment_get_value (adjustment)) * 2);
380 }
381 
382 static gboolean
cb_canvas_button_press(GocCanvas * canvas,G_GNUC_UNUSED GdkEventButton * event,AutoFormatState * state)383 cb_canvas_button_press (GocCanvas *canvas,
384 			G_GNUC_UNUSED GdkEventButton *event,
385 			AutoFormatState *state)
386 {
387 	GnmFT *ft;
388 	GSList *ptr;
389 	int index = 0;
390 
391 	while (index < NUM_PREVIEWS && canvas != state->canvas[index])
392 		index++;
393 
394 	g_return_val_if_fail (index < NUM_PREVIEWS, FALSE);
395 
396 	state->preview_index = state->preview_top + index;
397 
398 	previews_free (state);
399 	previews_load (state, state->preview_top);
400 
401 	for (ptr = state->templates, index = 0; ptr != NULL ;  ptr = ptr->next, index++)
402 		if (index == state->preview_index)
403 			break;
404 
405 	g_return_val_if_fail (ptr != NULL && ptr->data != NULL, FALSE);
406 
407 	ft = ptr->data;
408 	state->selected_template = ft;
409 	gtk_entry_set_text (state->info_name,   _(ft->name));
410 	gtk_entry_set_text (state->info_author, ft->author);
411 	gnm_textview_set_text (GTK_TEXT_VIEW (state->info_descr),
412 		_(ft->description));
413 
414 	gtk_entry_set_text (state->info_cat, _(ft->category->name));
415 
416 	return TRUE;
417 }
418 
419 static void
cb_check_item_toggled(G_GNUC_UNUSED GtkCheckMenuItem * item,AutoFormatState * state)420 cb_check_item_toggled (G_GNUC_UNUSED GtkCheckMenuItem *item,
421 		       AutoFormatState *state)
422 {
423 	GSList *ptr;
424 	int i;
425 
426 	for (ptr = state->templates; ptr != NULL ; ptr = ptr->next) {
427 		GnmFT *ft = ptr->data;
428 
429 		ft->number    = gtk_check_menu_item_get_active (state->number);
430 		ft->border    = gtk_check_menu_item_get_active (state->border);
431 		ft->font      = gtk_check_menu_item_get_active (state->font);
432 		ft->patterns  = gtk_check_menu_item_get_active (state->patterns);
433 		ft->alignment = gtk_check_menu_item_get_active (state->alignment);
434 
435 		ft->edges.left   = gtk_check_menu_item_get_active (state->edges.left);
436 		ft->edges.right  = gtk_check_menu_item_get_active (state->edges.right);
437 		ft->edges.top    = gtk_check_menu_item_get_active (state->edges.top);
438 		ft->edges.bottom = gtk_check_menu_item_get_active (state->edges.bottom);
439 
440 		ft->invalidate_hash = TRUE;
441 	}
442 
443 	for (i = 0; i < NUM_PREVIEWS; i++)
444 		goc_canvas_invalidate (state->canvas [i],
445 			-2, -2, INT_MAX/2, INT_MAX/2);
446 }
447 
448 static void
cb_category_changed(AutoFormatState * state)449 cb_category_changed (AutoFormatState *state)
450 {
451 	GList *selection = g_list_nth (state->category_groups,
452 		gtk_combo_box_get_active (state->category));
453 	char const *tip = NULL;
454 
455 	state->current_category_group = (selection != NULL) ? selection->data : NULL;
456 	previews_free (state);
457 	templates_free (state);
458 	if (templates_load (state) == FALSE)
459 		g_warning ("Error while loading templates!");
460 
461 	if (NULL != state->current_category_group) {
462 		tip = state->current_category_group->description;
463 		if (NULL == tip)
464 			tip = state->current_category_group->name;
465 	}
466 	gtk_widget_set_tooltip_text (GTK_WIDGET (state->category),
467 		(NULL != tip) ? _(tip) : "");
468 	previews_load (state, 0);
469 	cb_check_item_toggled (NULL, state);
470 	cb_canvas_button_press (state->canvas[0], NULL, state);
471 }
472 
473 static void
cb_gridlines_item_toggled(G_GNUC_UNUSED GtkCheckMenuItem * item,AutoFormatState * state)474 cb_gridlines_item_toggled (G_GNUC_UNUSED GtkCheckMenuItem *item,
475 			   AutoFormatState *state)
476 {
477 	previews_free (state);
478 	previews_load (state, state->preview_top);
479 }
480 
481 /********************************************************************************
482  * MAIN
483  ********************************************************************************/
484 
485 static gboolean
cb_canvas_focus(GtkWidget * canvas,G_GNUC_UNUSED GtkDirectionType direction,AutoFormatState * state)486 cb_canvas_focus (GtkWidget *canvas,
487 		 G_GNUC_UNUSED GtkDirectionType direction,
488 		 AutoFormatState *state)
489 {
490 	if (!gtk_widget_has_focus (canvas)) {
491 		gtk_widget_grab_focus (canvas);
492 		cb_canvas_button_press (GOC_CANVAS (canvas), NULL, state);
493 		return TRUE;
494 	}
495 	return FALSE;
496 }
497 
498 /**
499  * dialog_autoformat:
500  * @wbcg: the control that invoked this dialog
501  *
502  * This function will show the AutoFormatTemplate dialog and apply
503  * the template the user chooses to the current selection in the active
504  * sheet of the workbook if the user desires.
505  **/
506 void
dialog_autoformat(WBCGtk * wbcg)507 dialog_autoformat (WBCGtk *wbcg)
508 {
509 	GtkBuilder *gui;
510 	AutoFormatState *state;
511 	int i;
512 
513 	gui = gnm_gtk_builder_load ("res:ui/autoformat.ui", NULL, GO_CMD_CONTEXT (wbcg));
514 	if (gui == NULL)
515 		return;
516 
517 	state = g_new0 (AutoFormatState, 1);
518 	state->wb              = wb_control_get_workbook (GNM_WBC (wbcg));
519 	state->wbcg            = wbcg;
520 	state->templates       = NULL;
521 	state->category_groups = NULL;
522 	state->selrect         = NULL;
523 	for (i = 0; i < NUM_PREVIEWS; i++)
524 		state->grid[i] = NULL;
525 
526 	state->current_category_group  = NULL;
527 	state->preview_top       = 0;
528 	state->preview_index     = -1;
529 	state->previews_locked   = FALSE;
530 	state->more_down         = FALSE;
531 	state->selected_template = NULL;
532 
533 	state->dialog     = GTK_DIALOG (go_gtk_builder_get_widget (gui, "dialog"));
534 	state->category   = GTK_COMBO_BOX (go_gtk_builder_get_widget (gui, "format_category"));
535 	state->scroll     = GTK_SCROLLBAR (go_gtk_builder_get_widget (gui, "format_scroll"));
536 	state->gridlines  = GTK_CHECK_MENU_ITEM  (go_gtk_builder_get_widget (gui, "format_gridlines"));
537 
538 	state->info_name   = GTK_ENTRY (go_gtk_builder_get_widget (gui, "format_info_name"));
539 	state->info_author = GTK_ENTRY (go_gtk_builder_get_widget (gui, "format_info_author"));
540 	state->info_cat    = GTK_ENTRY (go_gtk_builder_get_widget (gui, "format_info_cat"));
541 	state->info_descr  = GTK_TEXT_VIEW (go_gtk_builder_get_widget (gui, "format_info_descr"));
542 
543 	state->ok     = GTK_BUTTON (go_gtk_builder_get_widget (gui, "format_ok"));
544 	state->cancel = GTK_BUTTON (go_gtk_builder_get_widget (gui, "format_cancel"));
545 
546 #define CHECK_ITEM(v_, w_,h_) do {				\
547 	GtkWidget *w = go_gtk_builder_get_widget (gui, (w_));	\
548 	state->v_ = GTK_CHECK_MENU_ITEM (w);			\
549 	g_signal_connect (w, "activate", G_CALLBACK (h_), state);	\
550 } while (0)
551 
552 	CHECK_ITEM (number, "number_menuitem", cb_check_item_toggled);
553 	CHECK_ITEM (border, "border_menuitem", cb_check_item_toggled);
554 	CHECK_ITEM (font, "font_menuitem", cb_check_item_toggled);
555 	CHECK_ITEM (patterns, "pattern_menuitem", cb_check_item_toggled);
556 	CHECK_ITEM (alignment, "alignment_menuitem", cb_check_item_toggled);
557 	CHECK_ITEM (edges.left, "left_menuitem", cb_check_item_toggled);
558 	CHECK_ITEM (edges.right, "right_menuitem", cb_check_item_toggled);
559 	CHECK_ITEM (edges.top, "top_menuitem", cb_check_item_toggled);
560 	CHECK_ITEM (edges.bottom, "bottom_menuitem", cb_check_item_toggled);
561 	CHECK_ITEM (gridlines, "gridlines_menuitem", cb_gridlines_item_toggled);
562 
563 #undef CHECK_ITEM
564 
565 	for (i = 0; i < NUM_PREVIEWS; i++) {
566 		char *name;
567 
568 		name = g_strdup_printf ("format_frame%d", i+1);
569 		state->frame[i] = GTK_FRAME (go_gtk_builder_get_widget (gui, name));
570 		g_free (name);
571 
572 		state->canvas[i] = GOC_CANVAS (g_object_new (GOC_TYPE_CANVAS, NULL));
573 		gtk_widget_set_size_request (GTK_WIDGET (state->canvas[i]),
574 			TOTAL_WIDTH + (2 * BORDER),
575 			TOTAL_HEIGHT + (2 * BORDER));
576 		gtk_container_add (GTK_CONTAINER (state->frame[i]),
577 				   GTK_WIDGET (state->canvas[i]));
578 
579 		g_signal_connect (G_OBJECT (state->canvas[i]),
580 			"button-press-event",
581 			G_CALLBACK (cb_canvas_button_press), state);
582 		g_signal_connect (G_OBJECT (state->canvas[i]),
583 			"focus",
584 			G_CALLBACK (cb_canvas_focus), state);
585 	}
586 
587 	g_signal_connect (G_OBJECT (gtk_range_get_adjustment (GTK_RANGE (state->scroll))),
588 		"value_changed",
589 		G_CALLBACK (cb_scroll_value_changed), state);
590 	g_signal_connect (G_OBJECT (state->gridlines),
591 		"toggled",
592 		G_CALLBACK (cb_gridlines_item_toggled), state);
593 	g_signal_connect (G_OBJECT (state->ok),
594 		"clicked",
595 		G_CALLBACK (cb_ok_clicked), state);
596 	g_signal_connect_swapped (G_OBJECT (state->cancel), "clicked",
597 		G_CALLBACK (gtk_widget_destroy), state->dialog);
598 
599 	/* Fill category list */
600 	state->category_groups =
601 		g_list_sort (gnm_ft_category_group_list_get (),  gnm_ft_category_group_cmp);
602 
603 	if (state->category_groups == NULL) {
604 		GtkWidget *dialog;
605 
606 		dialog = gtk_message_dialog_new (GTK_WINDOW (state->dialog),
607 				GTK_DIALOG_DESTROY_WITH_PARENT,
608 				GTK_MESSAGE_WARNING,
609 				GTK_BUTTONS_CLOSE,
610 				_("An error occurred while reading the category list"));
611 		gtk_dialog_run (GTK_DIALOG (dialog));
612 	} else {
613 		unsigned i, select = 0;
614 		GList *ptr = state->category_groups;
615 		GtkListStore* store = gtk_list_store_new (1, G_TYPE_STRING);
616 		GtkTreeIter iter;
617 		GtkCellRenderer *renderer = (GtkCellRenderer*) gtk_cell_renderer_text_new();
618 		gtk_combo_box_set_model (state->category, GTK_TREE_MODEL (store));
619 		g_object_unref (store);
620 		gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (state->category), renderer, TRUE);
621 		gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (state->category), renderer,
622 						"text", 0,
623 						NULL);
624 
625 		for (i = 0 ; ptr != NULL ; ptr = ptr->next, i++) {
626 			GnmFTCategoryGroup *group = ptr->data;
627 			if (!strcmp (group->name,   "General" ))
628 				select = i;
629 			gtk_list_store_append (store, &iter);
630 			gtk_list_store_set (store, &iter,
631 						0, _(group->name),
632 						-1);
633 		}
634 
635 		g_signal_connect_swapped (G_OBJECT (state->category),
636 			"changed",
637 			G_CALLBACK (cb_category_changed), state);
638 		gtk_combo_box_set_active (GTK_COMBO_BOX (state->category), select);
639 		gtk_widget_show_all (GTK_WIDGET (state->category));
640 	}
641 
642 	gnm_init_help_button (
643 		go_gtk_builder_get_widget (gui, "help_button"),
644 		GNUMERIC_HELP_LINK_AUTOFORMAT);
645 
646 	gtk_dialog_set_default_response (state->dialog, GTK_RESPONSE_OK);
647 
648 	/* a candidate for merging into attach guru */
649 	go_gtk_nonmodal_dialog (wbcg_toplevel (state->wbcg),
650 				   GTK_WINDOW (state->dialog));
651 	wbc_gtk_attach_guru (state->wbcg, GTK_WIDGET (state->dialog));
652 	g_object_set_data_full (G_OBJECT (state->dialog),
653 		"state", state, (GDestroyNotify)cb_autoformat_destroy);
654 
655 	/* not show all or the scrollbars will appear */
656 	gtk_widget_show (GTK_WIDGET (state->dialog));
657 	g_object_unref (gui);
658 }
659