1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
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>
34 #include <style-border.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>
44 #include <selection.h>
47 #include <goffice/goffice.h>
49 #include <gsf/gsf-impl-utils.h>
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
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") }
78 Workbook
*wb
; /* Workbook we are working on */
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' */
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
;
111 GtkCheckMenuItem
*left
;
112 GtkCheckMenuItem
*right
;
113 GtkCheckMenuItem
*top
;
114 GtkCheckMenuItem
*bottom
;
117 GtkButton
*ok
, *cancel
;
120 /********************************************************************************/
126 typedef GnmPreviewGridClass AutoFormatGridClass
;
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
);
138 afg_get_cell_value (G_GNUC_UNUSED GnmPreviewGrid
*pg
, int col
, int row
)
144 if (row
>= PREVIEW_ROWS
|| col
>= PREVIEW_COLS
)
147 text
= _(demotable
[row
][col
]);
148 tmp
= go_strtod (text
, &endptr
);
151 return value_new_float (tmp
);
152 return value_new_string (text
);
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())
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
,
178 ((AutoFormatGrid
*) item
)->ft
= ft
;
181 /********************************************************************************
183 ********************************************************************************/
186 templates_free (AutoFormatState
*state
)
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
;
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.
208 templates_load (AutoFormatState
*state
)
213 g_return_val_if_fail (state
!= NULL
, FALSE
);
215 if (state
->category_groups
== NULL
)
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
) {
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,
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
);
253 * @state: AutoFormatState
255 * This function will free all previews.
258 previews_free (AutoFormatState
*state
)
262 if (state
->previews_locked
)
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
];
273 goc_item_destroy (state
->grid
[i
]);
274 state
->grid
[i
] = NULL
;
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.
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
)
301 iterator
= state
->templates
;
303 while (iterator
&& count
> 0) {
304 iterator
= g_slist_next (iterator
);
309 for (i
= 0; i
< NUM_PREVIEWS
; i
++) {
311 gtk_widget_hide (GTK_WIDGET (state
->canvas
[i
]));
312 gtk_frame_set_shadow_type (state
->frame
[i
], GTK_SHADOW_NONE
);
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
) {
321 g_return_if_fail (state
->selrect
== NULL
);
323 state
->selrect
= goc_item_new (goc_canvas_get_root (state
->canvas
[i
]),
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
),
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
);
337 gtk_frame_set_shadow_type (state
->frame
[i
], GTK_SHADOW_ETCHED_IN
);
339 goc_canvas_scroll_to (state
->canvas
[i
],
342 gtk_widget_set_tooltip_text
343 (GTK_WIDGET (state
->canvas
[i
]),
346 gtk_widget_show (GTK_WIDGET (state
->canvas
[i
]));
347 start
= g_slist_next (start
);
351 state
->preview_top
= topindex
;
354 /********************************************************************************
356 ********************************************************************************/
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
));
370 cb_autoformat_destroy (AutoFormatState
*state
)
372 templates_free (state
);
373 gnm_ft_category_group_list_free (state
->category_groups
);
378 cb_scroll_value_changed (GtkAdjustment
*adjustment
, AutoFormatState
*state
)
380 previews_free (state
);
381 previews_load (state
, rint (gtk_adjustment_get_value (adjustment
)) * 2);
385 cb_canvas_button_press (GocCanvas
*canvas
,
386 G_GNUC_UNUSED GdkEventButton
*event
,
387 AutoFormatState
*state
)
393 while (index
< NUM_PREVIEWS
&& canvas
!= state
->canvas
[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
)
407 g_return_val_if_fail (ptr
!= NULL
&& ptr
->data
!= NULL
, FALSE
);
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
),
416 gtk_entry_set_text (state
->info_cat
, _(ft
->category
->name
));
422 cb_check_item_toggled (G_GNUC_UNUSED GtkCheckMenuItem
*item
,
423 AutoFormatState
*state
)
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);
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
;
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
);
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 /********************************************************************************
485 ********************************************************************************/
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
);
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.
509 dialog_autoformat (WBCGtk
*wbcg
)
512 AutoFormatState
*state
;
515 gui
= gnm_gtk_builder_load ("res:ui/autoformat.ui", NULL
, GO_CMD_CONTEXT (wbcg
));
519 state
= g_new0 (AutoFormatState
, 1);
520 state
->wb
= wb_control_get_workbook (GNM_WBC (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); \
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
);
567 for (i
= 0; i
< NUM_PREVIEWS
; i
++) {
570 name
= g_strdup_printf ("format_frame%d", i
+1);
571 state
->frame
[i
] = GTK_FRAME (go_gtk_builder_get_widget (gui
, 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
]),
586 G_CALLBACK (cb_canvas_focus
), state
);
589 g_signal_connect (G_OBJECT (gtk_range_get_adjustment (GTK_RANGE (state
->scroll
))),
591 G_CALLBACK (cb_scroll_value_changed
), state
);
592 g_signal_connect (G_OBJECT (state
->gridlines
),
594 G_CALLBACK (cb_gridlines_item_toggled
), state
);
595 g_signal_connect (G_OBJECT (state
->ok
),
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
) {
608 dialog
= gtk_message_dialog_new (GTK_WINDOW (state
->dialog
),
609 GTK_DIALOG_DESTROY_WITH_PARENT
,
612 _("An error occurred while reading the category list"));
613 gtk_dialog_run (GTK_DIALOG (dialog
));
615 unsigned i
, select
= 0;
616 GList
*ptr
= state
->category_groups
;
617 GtkListStore
* store
= gtk_list_store_new (1, G_TYPE_STRING
);
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
,
627 for (i
= 0 ; ptr
!= NULL
; ptr
= ptr
->next
, i
++) {
628 GnmFTCategoryGroup
*group
= ptr
->data
;
629 if (!strcmp (group
->name
, "General" ))
631 gtk_list_store_append (store
, &iter
);
632 gtk_list_store_set (store
, &iter
,
637 g_signal_connect_swapped (G_OBJECT (state
->category
),
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
);