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