2 * dialog-autoformat.c : implementation of the autoformat dialog
4 * Author : Almer S. Tigelaar <almer@gnome.org>
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.
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.
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/>.
21 * WORKING NOTE : Once the edit dialog is ready, search for FIXME and
22 * remove the disabling of new/edit/remove buttons!
25 #include <gnumeric-config.h>
26 #include <glib/gi18n-lib.h>
28 #include <dialogs/dialogs.h>
29 #include <dialogs/help.h>
33 #include <style-border.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>
43 #include <selection.h>
46 #include <goffice/goffice.h>
47 #include <gsf/gsf-impl-utils.h>
51 * previews, please don't make this larger than 5x5
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
59 #define INNER_BORDER 5
60 #define TOTAL_WIDTH (DEFAULT_COL_WIDTH * PREVIEW_COLS)
61 #define TOTAL_HEIGHT (DEFAULT_ROW_HEIGHT * PREVIEW_ROWS)
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") }
76 Workbook
*wb
; /* Workbook we are working on */
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 */
84 GnmFTCategoryGroup
*current_category_group
; /* Currently selected category group */
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' */
96 GtkComboBox
*category
;
98 GocCanvas
*canvas
[NUM_PREVIEWS
];
99 GtkFrame
*frame
[NUM_PREVIEWS
];
100 GtkScrollbar
*scroll
;
101 GtkCheckMenuItem
*gridlines
;
103 GtkEntry
*info_name
, *info_author
, *info_cat
;
104 GtkTextView
*info_descr
;
106 GtkCheckMenuItem
*number
, *border
, *font
, *patterns
, *alignment
;
109 GtkCheckMenuItem
*left
;
110 GtkCheckMenuItem
*right
;
111 GtkCheckMenuItem
*top
;
112 GtkCheckMenuItem
*bottom
;
115 GtkButton
*ok
, *cancel
;
118 /********************************************************************************/
124 typedef GnmPreviewGridClass AutoFormatGridClass
;
127 afg_get_cell_style (GnmPreviewGrid
*pg
, int col
, int row
)
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
);
136 afg_get_cell_value (G_GNUC_UNUSED GnmPreviewGrid
*pg
, int col
, int row
)
142 if (row
>= PREVIEW_ROWS
|| col
>= PREVIEW_COLS
)
145 text
= _(demotable
[row
][col
]);
146 tmp
= go_strtod (text
, &endptr
);
149 return value_new_float (tmp
);
150 return value_new_string (text
);
154 auto_format_grid_class_init (GnmPreviewGridClass
*klass
)
156 klass
->get_cell_style
= afg_get_cell_style
;
157 klass
->get_cell_value
= afg_get_cell_value
;
160 static GSF_CLASS (AutoFormatGrid
, auto_format_grid
,
161 auto_format_grid_class_init
, NULL
,
162 gnm_preview_grid_get_type())
165 auto_format_grid_new (AutoFormatState
*state
, int i
, GnmFT
*ft
)
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
,
176 ((AutoFormatGrid
*) item
)->ft
= ft
;
179 /********************************************************************************
181 ********************************************************************************/
184 templates_free (AutoFormatState
*state
)
188 g_return_if_fail (state
!= NULL
);
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
;
198 * @state: AutoFormatState
200 * This function will load the templates in the currently selected
201 * category group (it looks at state->category_groups to determine the selection)
203 * Returns: %TRUE if all went well, %FALSE otherwise.
206 templates_load (AutoFormatState
*state
)
211 g_return_val_if_fail (state
!= NULL
, FALSE
);
213 if (state
->category_groups
== NULL
)
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
) {
220 range_init (&ft
->dimension
,
221 0, 0, PREVIEW_COLS
- 1, PREVIEW_ROWS
- 1);
222 ft
->invalidate_hash
= TRUE
;
224 n_templates
= g_slist_length (state
->templates
);
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)
231 state
->previews_locked
= TRUE
;
233 GtkAdjustment
*adjustment
= gtk_range_get_adjustment (GTK_RANGE (state
->scroll
));
234 gtk_adjustment_configure (adjustment
,
235 0, 0, n_templates
/ 2,
238 state
->previews_locked
= FALSE
;
241 * Hide the scrollbar when it's not needed
243 gtk_widget_set_visible (GTK_WIDGET (state
->scroll
),
244 n_templates
> NUM_PREVIEWS
);
251 * @state: AutoFormatState
253 * This function will free all previews.
256 previews_free (AutoFormatState
*state
)
260 if (state
->previews_locked
)
263 if (state
->selrect
) {
264 goc_item_destroy (state
->selrect
);
265 state
->selrect
= NULL
;
268 for (i
= 0; i
< NUM_PREVIEWS
; i
++) {
269 GocItem
*item
= state
->grid
[i
];
271 goc_item_destroy (state
->grid
[i
]);
272 state
->grid
[i
] = NULL
;
279 * @state: AutoFormatState
280 * @topindex: The index of the template to be displayed in the upper left corner
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.
289 previews_load (AutoFormatState
*state
, int topindex
)
291 GSList
*iterator
, *start
;
292 int i
, count
= topindex
;
294 g_return_if_fail (state
!= NULL
);
296 if (state
->previews_locked
)
299 iterator
= state
->templates
;
301 while (iterator
&& count
> 0) {
302 iterator
= g_slist_next (iterator
);
307 for (i
= 0; i
< NUM_PREVIEWS
; i
++) {
309 gtk_widget_hide (GTK_WIDGET (state
->canvas
[i
]));
310 gtk_frame_set_shadow_type (state
->frame
[i
], GTK_SHADOW_NONE
);
312 GnmFT
*ft
= start
->data
;
314 state
->grid
[i
] = auto_format_grid_new (state
, i
, ft
);
316 /* Are we selected? Then draw a selection rectangle */
317 if (topindex
+ i
== state
->preview_index
) {
319 g_return_if_fail (state
->selrect
== NULL
);
321 state
->selrect
= goc_item_new (goc_canvas_get_root (state
->canvas
[i
]),
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
),
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;
333 gtk_frame_set_shadow_type (state
->frame
[i
], GTK_SHADOW_IN
);
335 gtk_frame_set_shadow_type (state
->frame
[i
], GTK_SHADOW_ETCHED_IN
);
337 goc_canvas_scroll_to (state
->canvas
[i
],
340 gtk_widget_set_tooltip_text
341 (GTK_WIDGET (state
->canvas
[i
]),
344 gtk_widget_show (GTK_WIDGET (state
->canvas
[i
]));
345 start
= g_slist_next (start
);
349 state
->preview_top
= topindex
;
352 /********************************************************************************
354 ********************************************************************************/
357 cb_ok_clicked (G_GNUC_UNUSED GtkButton
*button
,
358 AutoFormatState
*state
)
360 if (state
->selected_template
)
361 cmd_selection_autoformat (GNM_WBC (state
->wbcg
),
362 gnm_ft_clone (state
->selected_template
));
364 gtk_widget_destroy (GTK_WIDGET (state
->dialog
));
368 cb_autoformat_destroy (AutoFormatState
*state
)
370 templates_free (state
);
371 gnm_ft_category_group_list_free (state
->category_groups
);
376 cb_scroll_value_changed (GtkAdjustment
*adjustment
, AutoFormatState
*state
)
378 previews_free (state
);
379 previews_load (state
, rint (gtk_adjustment_get_value (adjustment
)) * 2);
383 cb_canvas_button_press (GocCanvas
*canvas
,
384 G_GNUC_UNUSED GdkEventButton
*event
,
385 AutoFormatState
*state
)
391 while (index
< NUM_PREVIEWS
&& canvas
!= state
->canvas
[index
])
394 g_return_val_if_fail (index
< NUM_PREVIEWS
, FALSE
);
396 state
->preview_index
= state
->preview_top
+ index
;
398 previews_free (state
);
399 previews_load (state
, state
->preview_top
);
401 for (ptr
= state
->templates
, index
= 0; ptr
!= NULL
; ptr
= ptr
->next
, index
++)
402 if (index
== state
->preview_index
)
405 g_return_val_if_fail (ptr
!= NULL
&& ptr
->data
!= NULL
, FALSE
);
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
),
414 gtk_entry_set_text (state
->info_cat
, _(ft
->category
->name
));
420 cb_check_item_toggled (G_GNUC_UNUSED GtkCheckMenuItem
*item
,
421 AutoFormatState
*state
)
426 for (ptr
= state
->templates
; ptr
!= NULL
; ptr
= ptr
->next
) {
427 GnmFT
*ft
= ptr
->data
;
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
);
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
);
440 ft
->invalidate_hash
= TRUE
;
443 for (i
= 0; i
< NUM_PREVIEWS
; i
++)
444 goc_canvas_invalidate (state
->canvas
[i
],
445 -2, -2, INT_MAX
/2, INT_MAX
/2);
449 cb_category_changed (AutoFormatState
*state
)
451 GList
*selection
= g_list_nth (state
->category_groups
,
452 gtk_combo_box_get_active (state
->category
));
453 char const *tip
= NULL
;
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!");
461 if (NULL
!= state
->current_category_group
) {
462 tip
= state
->current_category_group
->description
;
464 tip
= state
->current_category_group
->name
;
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
);
474 cb_gridlines_item_toggled (G_GNUC_UNUSED GtkCheckMenuItem
*item
,
475 AutoFormatState
*state
)
477 previews_free (state
);
478 previews_load (state
, state
->preview_top
);
481 /********************************************************************************
483 ********************************************************************************/
486 cb_canvas_focus (GtkWidget
*canvas
,
487 G_GNUC_UNUSED GtkDirectionType direction
,
488 AutoFormatState
*state
)
490 if (!gtk_widget_has_focus (canvas
)) {
491 gtk_widget_grab_focus (canvas
);
492 cb_canvas_button_press (GOC_CANVAS (canvas
), NULL
, state
);
500 * @wbcg: the control that invoked this dialog
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.
507 dialog_autoformat (WBCGtk
*wbcg
)
510 AutoFormatState
*state
;
513 gui
= gnm_gtk_builder_load ("res:ui/autoformat.ui", NULL
, GO_CMD_CONTEXT (wbcg
));
517 state
= g_new0 (AutoFormatState
, 1);
518 state
->wb
= wb_control_get_workbook (GNM_WBC (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
;
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
;
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"));
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"));
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"));
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); \
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
);
565 for (i
= 0; i
< NUM_PREVIEWS
; i
++) {
568 name
= g_strdup_printf ("format_frame%d", i
+1);
569 state
->frame
[i
] = GTK_FRAME (go_gtk_builder_get_widget (gui
, name
));
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
]));
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
]),
584 G_CALLBACK (cb_canvas_focus
), state
);
587 g_signal_connect (G_OBJECT (gtk_range_get_adjustment (GTK_RANGE (state
->scroll
))),
589 G_CALLBACK (cb_scroll_value_changed
), state
);
590 g_signal_connect (G_OBJECT (state
->gridlines
),
592 G_CALLBACK (cb_gridlines_item_toggled
), state
);
593 g_signal_connect (G_OBJECT (state
->ok
),
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
);
599 /* Fill category list */
600 state
->category_groups
=
601 g_list_sort (gnm_ft_category_group_list_get (), gnm_ft_category_group_cmp
);
603 if (state
->category_groups
== NULL
) {
606 dialog
= gtk_message_dialog_new (GTK_WINDOW (state
->dialog
),
607 GTK_DIALOG_DESTROY_WITH_PARENT
,
610 _("An error occurred while reading the category list"));
611 gtk_dialog_run (GTK_DIALOG (dialog
));
613 unsigned i
, select
= 0;
614 GList
*ptr
= state
->category_groups
;
615 GtkListStore
* store
= gtk_list_store_new (1, G_TYPE_STRING
);
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
,
625 for (i
= 0 ; ptr
!= NULL
; ptr
= ptr
->next
, i
++) {
626 GnmFTCategoryGroup
*group
= ptr
->data
;
627 if (!strcmp (group
->name
, "General" ))
629 gtk_list_store_append (store
, &iter
);
630 gtk_list_store_set (store
, &iter
,
635 g_signal_connect_swapped (G_OBJECT (state
->category
),
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
));
642 gnm_init_help_button (
643 go_gtk_builder_get_widget (gui
, "help_button"),
644 GNUMERIC_HELP_LINK_AUTOFORMAT
);
646 gtk_dialog_set_default_response (state
->dialog
, GTK_RESPONSE_OK
);
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
);
655 /* not show all or the scrollbars will appear */
656 gtk_widget_show (GTK_WIDGET (state
->dialog
));
657 g_object_unref (gui
);