1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * dialog-cell-format.c: Implements a dialog to format cells.
6 * Jody Goldberg <jody@gnome.org>
7 * Almer S. Tigelaar <almer@gnome.org>
8 * Andreas J. Guelzow <aguelzow@pyrshep.ca>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, see <https://www.gnu.org/licenses/>.
24 #include <gnumeric-config.h>
25 #include <glib/gi18n-lib.h>
31 #include <sheet-view.h>
32 #include <sheet-merge.h>
33 #include <sheet-style.h>
34 #include <style-color.h>
36 #include <selection.h>
41 #include <gnm-format.h>
45 #include <application.h>
46 #include <validation.h>
47 #include <input-msg.h>
52 #include <preview-grid.h>
53 #include <widgets/gnumeric-dashed-canvas-line.h>
54 #include <widgets/gnm-format-sel.h>
55 #include <style-conditions.h>
57 #include <goffice/goffice.h>
58 #include <goffice/canvas/goc-canvas.h>
59 #include <goffice/canvas/goc-item.h>
60 #include <goffice/canvas/goc-rectangle.h>
65 #define CELL_FORMAT_KEY "cell-format-dialog"
70 } const underline_types
[] = {
71 /* xgettext: This refers to a "none underline" */
72 { NC_("underline", "None"), UNDERLINE_NONE
},
73 { NC_("underline", "Single"), UNDERLINE_SINGLE
},
74 { NC_("underline", "Double"), UNDERLINE_DOUBLE
},
75 /* xgettext: This refers to a "single low underline" */
76 { NC_("underline", "Single Low"), UNDERLINE_SINGLE_LOW
},
77 /* xgettext: This refers to a "double low underline" */
78 { NC_("underline", "Double Low"), UNDERLINE_DOUBLE_LOW
}
81 /* The order corresponds to border_preset_buttons */
85 BORDER_PRESET_OUTLINE
,
94 struct _FormatState
*state
;
96 GtkToggleButton
*current_pattern
;
97 GtkToggleButton
*default_button
;
98 void (*draw_preview
) (struct _FormatState
*);
102 struct _FormatState
*state
;
105 GCallback preview_update
;
109 struct _FormatState
*state
;
110 GtkToggleButton
*button
;
111 GnmStyleBorderType pattern_index
;
112 gboolean is_selected
; /* Is it selected */
113 GnmStyleBorderLocation index
;
115 gboolean is_auto_color
;
116 gboolean is_set
; /* Has the element been changed */
124 typedef struct _FormatState
{
128 GtkNotebook
*notebook
;
129 GtkWidget
*apply_button
;
130 GtkWidget
*ok_button
;
135 unsigned int conflicts
;
136 GnmStyle
*style
, *result
;
137 GnmBorder
*borders
[GNM_STYLE_BORDER_EDGE_MAX
];
140 gboolean enable_edit
;
142 GtkWidget
* format_sel
;
145 GtkCheckButton
*wrap
;
146 GtkSpinButton
*indent_button
;
147 GtkWidget
*indent_label
;
149 GORotationSel
*rotation
;
153 GtkWidget
*underline_picker
;
154 GnmUnderline underline
;
158 GtkButton
*preset
[BORDER_PRESET_MAX
];
162 BorderPicker edge
[GNM_STYLE_BORDER_EDGE_MAX
];
165 gboolean is_auto_color
;
166 PatternPicker pattern
;
170 GnmPreviewGrid
*grid
;
173 ColorPicker back_color
;
174 ColorPicker pattern_color
;
175 PatternPicker pattern
;
178 GtkCheckButton
*hidden
, *locked
, *sheet_protected
;
180 gboolean sheet_protected_changed
;
181 gboolean sheet_protected_value
;
184 GtkGrid
*criteria_grid
;
185 GtkComboBox
*constraint_type
;
186 GtkLabel
*operator_label
;
188 ExprEntry expr0
, expr1
;
189 GtkToggleButton
*allow_blank
;
190 GtkToggleButton
*use_dropdown
;
193 GtkLabel
*action_label
;
194 GtkLabel
*title_label
;
205 GtkToggleButton
*flag
;
207 GtkLabel
*title_label
;
213 gboolean is_selector
;
222 CONDITIONS_NUM_COLUMNS
227 /*****************************************************************************/
228 /* Some utility routines shared by all pages */
231 * A utility routine to help mark the attributes as being changed
232 * VERY stupid for now.
235 fmt_dialog_changed (FormatState
*state
)
241 if (!state
->enable_edit
)
244 gfs
= GO_FORMAT_SEL (state
->format_sel
);
245 fmt
= go_format_sel_get_fmt (gfs
);
246 ok
= !go_format_is_invalid (fmt
);
248 gtk_widget_set_sensitive (state
->apply_button
, ok
);
249 gtk_widget_set_sensitive (state
->ok_button
, ok
);
252 /* Default to the 'Format' page but remember which page we were on between
254 static FormatDialogPosition_t fmt_dialog_page
= FD_NUMBER
;
257 /* The last currency selected */
258 static int fmt_dialog_currency
= 0;
262 * Callback routine to help remember which format tab was selected
263 * between dialog invocations.
266 cb_page_select (G_GNUC_UNUSED GtkNotebook
*notebook
,
267 G_GNUC_UNUSED GtkWidget
*page
,
269 G_GNUC_UNUSED gpointer user_data
)
271 fmt_dialog_page
= page_num
;
275 cb_notebook_destroy (GtkWidget
*nb
, gpointer page_sig_ptr
)
277 g_signal_handler_disconnect (nb
, GPOINTER_TO_UINT (page_sig_ptr
));
281 * Callback routine to give radio button like behaviour to the
282 * set of toggle buttons used for line & background patterns.
285 cb_toggle_changed (GtkToggleButton
*button
, PatternPicker
*picker
)
287 if (gtk_toggle_button_get_active (button
) &&
288 picker
->current_pattern
!= button
) {
289 gtk_toggle_button_set_active (picker
->current_pattern
, FALSE
);
290 picker
->current_pattern
= button
;
291 picker
->cur_index
= GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button
), "index"));
292 if (picker
->draw_preview
)
293 picker
->draw_preview (picker
->state
);
298 * Setup routine to associate images with toggle buttons
299 * and to adjust the relief so it looks nice.
302 setup_pattern_button (GdkScreen
*screen
,
304 char const *const name
,
305 PatternPicker
*picker
,
309 int const select_index
,
312 GtkWidget
*tmp
= go_gtk_builder_get_widget (gui
, name
);
314 GtkButton
*button
= GTK_BUTTON (tmp
);
316 char *res
= g_strconcat ("/org/gnumeric/gnumeric/images/", name
, ".png", NULL
);
319 image
= gtk_image_new_from_icon_name (name
, GTK_ICON_SIZE_DIALOG
);
321 /* gtk_image_new_from_resource() is unable to load pixdata with gdk-pixbuf >= 2.36.1
322 * because it uses the gdk_pixbuf_loader API and the pixdata module has been removed
323 * because of a security issue. See #776004. */
324 GdkPixbuf
*pixbuf
= gdk_pixbuf_new_from_resource (res
, NULL
);
325 image
= gtk_image_new_from_pixbuf (pixbuf
);
326 g_object_unref (pixbuf
);
329 gtk_widget_show (image
);
330 gtk_container_add (GTK_CONTAINER (tmp
), image
);
333 if (picker
->current_pattern
== NULL
) {
334 picker
->default_button
= GTK_TOGGLE_BUTTON (button
);
335 picker
->current_pattern
= picker
->default_button
;
336 picker
->cur_index
= index
;
339 gtk_button_set_relief (button
, GTK_RELIEF_NONE
);
340 g_signal_connect (G_OBJECT (button
),
342 G_CALLBACK (cb_toggle_changed
), picker
);
343 g_object_set_data (G_OBJECT (button
), "index",
344 GINT_TO_POINTER (index
));
346 /* Set the state AFTER the signal to get things redrawn correctly */
347 if (index
== select_index
) {
348 picker
->cur_index
= index
;
349 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button
),
353 g_warning ("CellFormat: Unexpected missing widget");
357 setup_color_pickers (FormatState
*state
,
359 char const *color_group
,
360 char const *placeholder
,
362 char const *default_caption
,
364 GCallback preview_update
,
366 gboolean allow_alpha
)
368 GtkWidget
*combo
, *w
, *frame
;
370 GnmColor
*mcolor
= NULL
;
371 GnmColor
*def_sc
= NULL
;
374 case MSTYLE_COLOR_PATTERN
:
375 if (0 == (state
->conflicts
& (1 << MSTYLE_COLOR_PATTERN
)))
376 mcolor
= gnm_style_get_pattern_color (state
->style
);
380 case MSTYLE_BORDER_TOP
: /* MSTYLE_BORDER_TOP is abused as representing all borders. */
381 def_sc
= sheet_style_get_auto_pattern_color (state
->sheet
);
383 case MSTYLE_FONT_COLOR
:
384 if (0 == (state
->conflicts
& (1 << MSTYLE_FONT_COLOR
)))
385 mcolor
= gnm_style_get_font_color (state
->style
);
386 def_sc
= style_color_auto_font ();
388 case MSTYLE_COLOR_BACK
:
389 if (0 == (state
->conflicts
& (1 << MSTYLE_COLOR_BACK
)))
390 mcolor
= gnm_style_get_back_color (state
->style
);
391 def_sc
= style_color_auto_back ();
394 g_warning ("Unhandled style element!");
396 cg
= go_color_group_fetch (color_group
, NULL
);
397 combo
= go_combo_color_new (NULL
, default_caption
,
398 def_sc
? def_sc
->go_color
: GO_COLOR_BLACK
, cg
);
400 go_combo_box_set_title (GO_COMBO_BOX (combo
), caption
);
402 /* Connect to the sample canvas and redraw it */
403 g_signal_connect (G_OBJECT (combo
),
405 G_CALLBACK (preview_update
), state
);
407 if (mcolor
&& !mcolor
->is_auto
)
408 go_combo_color_set_color (GO_COMBO_COLOR (combo
),
411 go_combo_color_set_color_to_default (GO_COMBO_COLOR (combo
));
414 go_combo_color_set_allow_alpha (GO_COMBO_COLOR (combo
), TRUE
);
415 frame
= gtk_frame_new (NULL
);
416 gtk_frame_set_shadow_type (GTK_FRAME (frame
), GTK_SHADOW_OUT
);
417 gtk_container_add (GTK_CONTAINER (frame
), combo
);
418 gtk_widget_show_all (frame
);
420 w
= go_gtk_builder_get_widget (state
->gui
, placeholder
);
421 go_gtk_widget_replace (w
, frame
);
423 w
= go_gtk_builder_get_widget (state
->gui
, label
);
424 gtk_label_set_mnemonic_widget (GTK_LABEL (w
), combo
);
426 style_color_unref (def_sc
);
428 if (picker
!= NULL
) {
429 picker
->combo
= combo
;
430 picker
->preview_update
= preview_update
;
434 /*****************************************************************************/
437 cb_number_format_changed (G_GNUC_UNUSED GtkWidget
*widget
,
441 gboolean changed
= FALSE
;
442 g_return_if_fail (state
!= NULL
);
444 if (!state
->enable_edit
)
448 GOFormat
*format
= go_format_new_from_XL (fmt
);
449 gnm_style_set_format (state
->result
, format
);
450 go_format_unref (format
);
455 fmt_dialog_changed (state
);
459 fmt_dialog_init_format_page (FormatState
*state
)
462 GODateConventions
const *date_conv
= sheet_date_conv (state
->sheet
);
464 state
->format_sel
= gnm_format_sel_new ();
465 gfs
= GO_FORMAT_SEL (state
->format_sel
);
467 gtk_notebook_prepend_page (GTK_NOTEBOOK (state
->notebook
),
469 gtk_label_new (_("Number")));
470 gtk_widget_show (GTK_WIDGET (gfs
));
472 if (0 == (state
->conflicts
& (1 << MSTYLE_FORMAT
))) {
473 GOFormat
const *fmt
= gnm_style_get_format (state
->style
);
474 go_format_sel_set_style_format (gfs
, fmt
);
476 go_format_sel_set_dateconv (gfs
, date_conv
);
477 go_format_sel_editable_enters (gfs
, GTK_WINDOW (state
->dialog
));
479 g_signal_connect (G_OBJECT (state
->format_sel
), "format_changed",
480 G_CALLBACK (cb_number_format_changed
), state
);
483 /*****************************************************************************/
486 cb_indent_changed (GtkEditable
*editable
, FormatState
*state
)
488 if (state
->enable_edit
) {
489 GtkSpinButton
*sb
= GTK_SPIN_BUTTON (editable
);
490 int val
= gtk_spin_button_get_value_as_int (sb
);
492 if (state
->align
.indent
!= val
) {
493 state
->align
.indent
= val
;
494 gnm_style_set_indent (state
->result
, val
);
495 fmt_dialog_changed (state
);
501 cb_align_h_toggle (GtkToggleButton
*button
, FormatState
*state
)
503 if (!gtk_toggle_button_get_active (button
))
506 if (state
->enable_edit
) {
507 GnmHAlign
const new_h
=
508 GPOINTER_TO_INT (g_object_get_data (
509 G_OBJECT (button
), "align"));
510 gboolean
const supports_indent
=
511 (new_h
== GNM_HALIGN_LEFT
|| new_h
== GNM_HALIGN_RIGHT
);
512 gnm_style_set_align_h (state
->result
, new_h
);
513 gtk_widget_set_sensitive (GTK_WIDGET (state
->align
.indent_button
),
515 gtk_widget_set_sensitive (GTK_WIDGET (state
->align
.indent_label
),
517 /* TODO: Should we 0 the indent ? */
518 fmt_dialog_changed (state
);
523 cb_align_v_toggle (GtkToggleButton
*button
, FormatState
*state
)
525 if (!gtk_toggle_button_get_active (button
))
528 if (state
->enable_edit
) {
529 gnm_style_set_align_v (
531 GPOINTER_TO_INT (g_object_get_data (
532 G_OBJECT (button
), "align")));
533 fmt_dialog_changed (state
);
538 cb_align_wrap_toggle (GtkToggleButton
*button
, FormatState
*state
)
540 if (state
->enable_edit
) {
541 gnm_style_set_wrap_text (state
->result
,
542 gtk_toggle_button_get_active (button
));
543 fmt_dialog_changed (state
);
548 fmt_dialog_init_align_radio (char const *const name
,
549 int const val
, int const target
,
553 GtkWidget
*tmp
= go_gtk_builder_get_widget (state
->gui
, name
);
555 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tmp
),
557 g_object_set_data (G_OBJECT (tmp
), "align",
558 GINT_TO_POINTER (val
));
559 g_signal_connect (G_OBJECT (tmp
),
566 cb_rotation_changed (G_GNUC_UNUSED GORotationSel
*grs
, int angle
, FormatState
*state
)
570 gnm_style_set_rotation (state
->result
, angle
);
571 fmt_dialog_changed (state
);
575 fmt_dialog_init_align_page (FormatState
*state
)
578 char const *const name
;
580 } const h_buttons
[] = {
581 { "halign_left", GNM_HALIGN_LEFT
},
582 { "halign_center", GNM_HALIGN_CENTER
},
583 { "halign_right", GNM_HALIGN_RIGHT
},
584 { "halign_general", GNM_HALIGN_GENERAL
},
585 { "halign_justify", GNM_HALIGN_JUSTIFY
},
586 { "halign_fill", GNM_HALIGN_FILL
},
587 { "halign_center_across_selection", GNM_HALIGN_CENTER_ACROSS_SELECTION
},
588 { "halign_distributed", GNM_HALIGN_DISTRIBUTED
},
592 char const *const name
;
594 } const v_buttons
[] = {
595 { "valign_top", GNM_VALIGN_TOP
},
596 { "valign_center", GNM_VALIGN_CENTER
},
597 { "valign_bottom", GNM_VALIGN_BOTTOM
},
598 { "valign_justify", GNM_VALIGN_JUSTIFY
},
599 { "valign_distributed", GNM_VALIGN_DISTRIBUTED
},
604 gboolean wrap
= FALSE
;
605 GnmHAlign h
= GNM_HALIGN_GENERAL
;
606 GnmVAlign v
= GNM_VALIGN_CENTER
;
610 if (0 == (state
->conflicts
& (1 << MSTYLE_ALIGN_H
)))
611 h
= gnm_style_get_align_h (state
->style
);
612 if (0 == (state
->conflicts
& (1 << MSTYLE_ALIGN_V
)))
613 v
= gnm_style_get_align_v (state
->style
);
615 /* Setup the horizontal buttons */
616 for (i
= 0; (name
= h_buttons
[i
].name
) != NULL
; ++i
)
617 fmt_dialog_init_align_radio (name
, h_buttons
[i
].align
,
619 G_CALLBACK (cb_align_h_toggle
));
621 /* Setup the vertical buttons */
622 for (i
= 0; (name
= v_buttons
[i
].name
) != NULL
; ++i
)
623 fmt_dialog_init_align_radio (name
, v_buttons
[i
].align
,
625 G_CALLBACK (cb_align_v_toggle
));
627 /* Setup the wrap button, and assign the current value */
628 if (0 == (state
->conflicts
& (1 << MSTYLE_WRAP_TEXT
)))
629 wrap
= gnm_style_get_wrap_text (state
->style
);
631 w
= go_gtk_builder_get_widget (state
->gui
, "align_wrap");
632 state
->align
.wrap
= GTK_CHECK_BUTTON (w
);
633 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w
), wrap
);
634 g_signal_connect (G_OBJECT (w
),
636 G_CALLBACK (cb_align_wrap_toggle
), state
);
638 if (0 != (state
->conflicts
& (1 << MSTYLE_INDENT
)) ||
639 (h
!= GNM_HALIGN_LEFT
&& h
!= GNM_HALIGN_RIGHT
))
640 state
->align
.indent
= 0;
642 state
->align
.indent
= gnm_style_get_indent (state
->style
);
644 state
->align
.indent_label
=
645 go_gtk_builder_get_widget (state
->gui
, "halign_indent_label");
646 w
= go_gtk_builder_get_widget (state
->gui
, "halign_indent");
647 state
->align
.indent_button
= GTK_SPIN_BUTTON (w
);
648 gtk_spin_button_set_value (state
->align
.indent_button
, state
->align
.indent
);
649 gtk_widget_set_sensitive (GTK_WIDGET (state
->align
.indent_button
),
650 (h
== GNM_HALIGN_LEFT
|| h
== GNM_HALIGN_RIGHT
));
651 gtk_widget_set_sensitive (GTK_WIDGET (state
->align
.indent_label
),
652 (h
== GNM_HALIGN_LEFT
|| h
== GNM_HALIGN_RIGHT
));
654 /* Catch changes to the spin box */
655 g_signal_connect (G_OBJECT (w
),
657 G_CALLBACK (cb_indent_changed
), state
);
659 /* Catch <return> in the spin box */
660 gnm_editable_enters (
661 GTK_WINDOW (state
->dialog
),
664 /* setup the rotation canvas */
665 if (0 == (state
->conflicts
& (1 << MSTYLE_ROTATION
))) {
666 r
= gnm_style_get_rotation (state
->style
);
671 state
->align
.rotation
= (GORotationSel
*) go_rotation_sel_new ();
672 go_rotation_sel_set_rotation (state
->align
.rotation
, r
);
673 g_signal_connect (G_OBJECT (state
->align
.rotation
), "rotation-changed",
674 G_CALLBACK (cb_rotation_changed
), state
);
675 go_gtk_widget_replace (go_gtk_builder_get_widget (state
->gui
, "rotation_placeholder"),
676 GTK_WIDGET (state
->align
.rotation
));
679 /*****************************************************************************/
682 cb_font_changed (G_GNUC_UNUSED GtkWidget
*widget
,
683 PangoAttrList
*attrs
, FormatState
*state
)
685 PangoAttrIterator
*aiter
;
686 const PangoAttribute
*attr
;
687 GnmStyle
*res
= state
->result
;
688 GOFontScript script
= GO_FONT_SCRIPT_STANDARD
;
689 gboolean has_script_attr
= FALSE
;
692 gboolean changed
= FALSE
;
693 g_return_if_fail (state
!= NULL
);
695 if (!state
->enable_edit
)
698 aiter
= pango_attr_list_get_iterator (attrs
);
700 attr
= pango_attr_iterator_get (aiter
, PANGO_ATTR_FAMILY
);
702 const char *s
= ((PangoAttrString
*)attr
)->value
;
703 if (!gnm_style_is_element_set (res
, MSTYLE_FONT_NAME
) ||
704 !g_str_equal (s
, gnm_style_get_font_name (res
))) {
706 gnm_style_set_font_name (res
, s
);
710 attr
= pango_attr_iterator_get (aiter
, PANGO_ATTR_SIZE
);
712 int i
= ((PangoAttrInt
*)attr
)->value
;
713 double d
= i
/ (double)PANGO_SCALE
;
714 if (!gnm_style_is_element_set (res
, MSTYLE_FONT_SIZE
) ||
715 d
!= gnm_style_get_font_size (res
)) {
717 gnm_style_set_font_size (res
, d
);
721 attr
= pango_attr_iterator_get (aiter
, PANGO_ATTR_WEIGHT
);
723 int i
= ((PangoAttrInt
*)attr
)->value
;
724 gboolean b
= (i
>= PANGO_WEIGHT_BOLD
);
725 if (!gnm_style_is_element_set (res
, MSTYLE_FONT_BOLD
) ||
726 b
!= gnm_style_get_font_bold (res
)) {
728 gnm_style_set_font_bold (res
, b
);
732 attr
= pango_attr_iterator_get (aiter
, PANGO_ATTR_STYLE
);
734 int i
= ((PangoAttrInt
*)attr
)->value
;
735 gboolean b
= (i
!= PANGO_STYLE_NORMAL
);
736 if (!gnm_style_is_element_set (res
, MSTYLE_FONT_ITALIC
) ||
737 b
!= gnm_style_get_font_italic (res
)) {
739 gnm_style_set_font_italic (res
, b
);
743 attr
= pango_attr_iterator_get (aiter
, PANGO_ATTR_UNDERLINE
);
745 /* Underline is special: we go beyond what pango has */
746 GnmUnderline u
= state
->font
.underline
;
747 if (!gnm_style_is_element_set (res
, MSTYLE_FONT_UNDERLINE
) ||
748 u
!= gnm_style_get_font_uline (res
)) {
750 gnm_style_set_font_uline (res
, u
);
754 attr
= pango_attr_iterator_get (aiter
, PANGO_ATTR_STRIKETHROUGH
);
756 int i
= ((PangoAttrInt
*)attr
)->value
;
757 gboolean b
= (i
!= 0);
758 if (!gnm_style_is_element_set (res
, MSTYLE_FONT_STRIKETHROUGH
) ||
759 b
!= gnm_style_get_font_strike (res
)) {
761 gnm_style_set_font_strike (res
, b
);
765 attr
= pango_attr_iterator_get (aiter
, go_pango_attr_subscript_get_attr_type ());
767 has_script_attr
= TRUE
;
768 if (((GOPangoAttrSubscript
*)attr
)->val
)
769 script
= GO_FONT_SCRIPT_SUB
;
771 attr
= pango_attr_iterator_get (aiter
, go_pango_attr_superscript_get_attr_type ());
773 has_script_attr
= TRUE
;
774 if (((GOPangoAttrSuperscript
*)attr
)->val
)
775 script
= GO_FONT_SCRIPT_SUPER
;
777 if (has_script_attr
&&
778 (!gnm_style_is_element_set (res
, MSTYLE_FONT_SCRIPT
) ||
779 script
!= gnm_style_get_font_script (res
))) {
781 gnm_style_set_font_script (res
, script
);
784 attr
= pango_attr_iterator_get (aiter
, PANGO_ATTR_FOREGROUND
);
786 ? gnm_color_new_pango (&((PangoAttrColor
*)attr
)->color
)
787 : style_color_auto_font ();
788 if (!gnm_style_is_element_set (res
, MSTYLE_FONT_COLOR
) ||
789 !style_color_equal (c
, gnm_style_get_font_color (res
))) {
791 gnm_style_set_font_color (res
, c
);
793 style_color_unref (c
);
795 pango_attr_iterator_destroy (aiter
);
798 fmt_dialog_changed (state
);
802 change_font_attr (FormatState
*state
, PangoAttribute
*attr
)
804 GOFontSel
*gfs
= state
->font
.selector
;
805 PangoAttrList
*attrs
= pango_attr_list_copy
806 (go_font_sel_get_sample_attributes (gfs
));
807 attr
->start_index
= 0;
808 attr
->end_index
= -1;
809 pango_attr_list_change (attrs
, attr
);
810 go_font_sel_set_sample_attributes (gfs
, attrs
);
811 cb_font_changed (NULL
, attrs
, state
);
812 pango_attr_list_unref (attrs
);
816 set_font_underline (FormatState
*state
, GnmUnderline uline
)
818 PangoUnderline pu
= gnm_translate_underline_to_pango (uline
);
819 GOOptionMenu
*om
= GO_OPTION_MENU (state
->font
.underline_picker
);
820 GtkMenuShell
*ms
= GTK_MENU_SHELL (go_option_menu_get_menu (om
));
823 if (uline
!= state
->font
.underline
) {
824 state
->font
.underline
= uline
;
825 change_font_attr (state
, pango_attr_underline_new (pu
));
828 children
= gtk_container_get_children (GTK_CONTAINER (ms
));
829 for (l
= children
; l
; l
= l
->next
) {
830 GtkMenuItem
*item
= GTK_MENU_ITEM (l
->data
);
831 GnmUnderline u
= GPOINTER_TO_INT
832 (g_object_get_data (G_OBJECT (item
), "value"));
834 go_option_menu_select_item (om
, item
);
836 g_list_free (children
);
840 cb_underline_changed (GOOptionMenu
*om
, FormatState
*state
)
842 GtkWidget
*selected
= go_option_menu_get_history (om
);
848 u
= GPOINTER_TO_INT (g_object_get_data (G_OBJECT (selected
), "value"));
849 set_font_underline (state
, u
);
853 /* Manually insert the font selector, and setup signals */
855 fmt_dialog_init_font_page (FormatState
*state
)
858 GtkWidget
*font_widget
;
859 gboolean strikethrough
= FALSE
;
860 GOFontScript script
= GO_FONT_SCRIPT_STANDARD
;
861 GODateConventions
const *date_conv
= sheet_date_conv (state
->sheet
);
862 GnmColor
*mcolor
= NULL
;
866 up
= state
->font
.underline_picker
= go_option_menu_build
867 (C_("underline", "None"), UNDERLINE_NONE
,
868 C_("underline", "Single"), UNDERLINE_SINGLE
,
869 C_("underline", "Double"), UNDERLINE_DOUBLE
,
870 C_("underline", "Single Low"), UNDERLINE_SINGLE_LOW
,
871 C_("underline", "Double Low"), UNDERLINE_DOUBLE_LOW
,
873 g_signal_connect (up
,
874 "changed", G_CALLBACK (cb_underline_changed
), state
);
875 def_sc
= style_color_auto_font ();
876 cg
= go_color_group_fetch ("fore_color_group", NULL
);
877 font_widget
= g_object_new (GO_TYPE_FONT_SEL
,
880 "color-unset-text", _("Automatic"),
882 "color-default", def_sc
->go_color
,
883 "show-underline", TRUE
,
884 "underline-picker", up
,
886 "show-strikethrough", TRUE
,
891 style_color_unref (def_sc
);
892 state
->font
.selector
= GO_FONT_SEL (font_widget
);
895 gtk_widget_show (font_widget
);
896 gtk_container_add (GTK_CONTAINER (go_gtk_builder_get_widget (state
->gui
, "font_sel_placeholder")),
899 go_font_sel_editable_enters (state
->font
.selector
,
900 GTK_WINDOW (state
->dialog
));
903 char *s
= format_value (NULL
, state
->value
, -1, date_conv
);
904 go_font_sel_set_sample_text (state
->font
.selector
, s
);
908 if (0 == (state
->conflicts
& (1 << MSTYLE_FONT_NAME
))) {
909 const char *family
= gnm_style_get_font_name (state
->style
);
910 go_font_sel_set_family (state
->font
.selector
, family
);
913 if (0 == (state
->conflicts
& (1 << MSTYLE_FONT_BOLD
)) &&
914 0 == (state
->conflicts
& (1 << MSTYLE_FONT_ITALIC
))) {
915 gboolean is_bold
= gnm_style_get_font_bold (state
->style
);
916 gboolean is_italic
= gnm_style_get_font_italic (state
->style
);
918 go_font_sel_set_style
919 (state
->font
.selector
,
920 is_bold
? PANGO_WEIGHT_BOLD
: PANGO_WEIGHT_NORMAL
,
921 is_italic
? PANGO_STYLE_ITALIC
: PANGO_STYLE_NORMAL
);
924 if (0 == (state
->conflicts
& (1 << MSTYLE_FONT_SIZE
))) {
925 double pts
= gnm_style_get_font_size (state
->style
);
926 go_font_sel_set_size (state
->font
.selector
,
930 state
->font
.underline
= UNDERLINE_NONE
;
931 if (0 == (state
->conflicts
& (1 << MSTYLE_FONT_UNDERLINE
))) {
932 GnmUnderline ut
= gnm_style_get_font_uline (state
->style
);
933 set_font_underline (state
, ut
);
936 if (0 == (state
->conflicts
& (1 << MSTYLE_FONT_COLOR
)))
937 mcolor
= gnm_style_get_font_color (state
->style
);
938 go_font_sel_set_color (state
->font
.selector
,
939 mcolor
? mcolor
->go_color
: GO_COLOR_BLACK
,
940 !mcolor
|| mcolor
->is_auto
);
942 if (0 == (state
->conflicts
& (1 << MSTYLE_FONT_STRIKETHROUGH
)))
943 strikethrough
= gnm_style_get_font_strike (state
->style
);
944 go_font_sel_set_strikethrough (state
->font
.selector
, strikethrough
);
946 if (0 == (state
->conflicts
& (1 << MSTYLE_FONT_SCRIPT
)))
947 script
= gnm_style_get_font_script (state
->style
);
948 go_font_sel_set_script (state
->font
.selector
, script
);
950 if (0 == (state
->conflicts
& (1 << MSTYLE_FONT_COLOR
)))
953 go_color_to_pango (gnm_style_get_font_color (state
->style
)->go_color
,
956 g_signal_connect (G_OBJECT (state
->font
.selector
),
958 G_CALLBACK (cb_font_changed
), state
);
961 /*****************************************************************************/
964 back_style_changed (FormatState
*state
)
966 g_return_if_fail (state
->back
.style
!= NULL
);
968 fmt_dialog_changed (state
);
970 if (state
->enable_edit
) {
971 gnm_style_merge_element (state
->result
, state
->back
.style
, MSTYLE_PATTERN
);
972 gnm_style_merge_element (state
->result
, state
->back
.style
, MSTYLE_COLOR_BACK
);
973 gnm_style_merge_element (state
->result
, state
->back
.style
, MSTYLE_COLOR_PATTERN
);
974 goc_item_set (GOC_ITEM (state
->back
.grid
),
975 "default-style", state
->back
.style
,
981 cb_back_preview_color (G_GNUC_UNUSED GOComboColor
*combo
,
983 G_GNUC_UNUSED gboolean is_custom
,
984 G_GNUC_UNUSED gboolean by_user
,
990 g_return_if_fail (c
);
993 sc
= style_color_auto_back ();
994 gnm_style_set_pattern (state
->back
.style
, 0);
996 sc
= gnm_color_new_go (c
);
997 gnm_style_set_pattern (state
->back
.style
, state
->back
.pattern
.cur_index
);
1000 gnm_style_set_back_color (state
->back
.style
, sc
);
1001 back_style_changed (state
);
1005 cb_pattern_preview_color (G_GNUC_UNUSED GOComboColor
*combo
,
1007 G_GNUC_UNUSED gboolean is_custom
,
1008 G_GNUC_UNUSED gboolean by_user
,
1009 gboolean is_default
, FormatState
*state
)
1011 GnmColor
*col
= is_default
1012 ? sheet_style_get_auto_pattern_color (state
->sheet
)
1013 : gnm_color_new_go (c
);
1015 gnm_style_set_pattern_color (state
->back
.style
, col
);
1017 back_style_changed (state
);
1021 draw_pattern_selected (FormatState
*state
)
1023 gnm_style_set_pattern (state
->back
.style
, state
->back
.pattern
.cur_index
);
1024 back_style_changed (state
);
1028 fmt_dialog_init_background_page (FormatState
*state
)
1034 widget
= g_object_new (GOC_TYPE_CANVAS
, NULL
);
1035 state
->back
.canvas
= GOC_CANVAS (widget
);
1036 gtk_widget_set_size_request (widget
, w
, h
);
1038 widget
= go_gtk_builder_get_widget (state
->gui
, "back_sample_frame");
1039 gtk_container_add (GTK_CONTAINER (widget
),
1040 GTK_WIDGET (state
->back
.canvas
));
1041 gtk_widget_show_all (widget
);
1043 state
->back
.grid
= GNM_PREVIEW_GRID (goc_item_new (
1044 goc_canvas_get_root (state
->back
.canvas
),
1045 gnm_preview_grid_get_type (),
1046 "render-gridlines", FALSE
,
1047 "default-col-width", w
,
1048 "default-row-height", h
,
1049 "default-style", state
->back
.style
,
1053 /*****************************************************************************/
1056 * This is self-evident.
1057 * I stared at it for 15 minutes before realizing that it's self-evident,
1058 * but it is. - jon_kare
1060 * @points: x, y coordinates for the endpoints of the line segment.
1061 * @states: A bitmap of states the coordinates are valid for.
1062 * @location: Location.
1064 #define L 10. /* Left */
1065 #define R 140. /* Right */
1066 #define T 10. /* Top */
1067 #define B 90. /* Bottom */
1068 #define H 50. /* Horizontal Middle */
1069 #define V 75. /* Vertical Middle */
1073 double const points
[4];
1075 GnmStyleBorderLocation
const location
;
1076 } const line_info
[] = {
1078 state 1 = single cell;
1079 state 2 = multi vert, single horiz (A1:A2);
1080 state 3 = single vert, multi horiz (A1:B1);
1081 state 4 = multi vertical & multi horizontal
1085 { { L
, T
, R
, T
}, 0xf, GNM_STYLE_BORDER_TOP
},
1086 { { L
, B
, R
, B
}, 0xf, GNM_STYLE_BORDER_BOTTOM
},
1087 { { L
, T
, L
, B
}, 0xf, GNM_STYLE_BORDER_LEFT
},
1088 { { R
, T
, R
, B
}, 0xf, GNM_STYLE_BORDER_RIGHT
},
1090 /* Only for state 2 & 4 */
1091 { { L
, H
, R
, H
}, 0xa, GNM_STYLE_BORDER_HORIZ
},
1093 /* Only for state 3 & 4 */
1094 { { V
, T
, V
, B
}, 0xc, GNM_STYLE_BORDER_VERT
},
1096 /* Only for state 1 & 4 */
1097 { { L
, T
, R
, B
}, 0x9, GNM_STYLE_BORDER_REV_DIAG
},
1098 { { L
, B
, R
, T
}, 0x9, GNM_STYLE_BORDER_DIAG
},
1100 /* Only for state 2 */
1101 { { L
, T
, R
, H
}, 0x2, GNM_STYLE_BORDER_REV_DIAG
},
1102 { { L
, H
, R
, B
}, 0x2, GNM_STYLE_BORDER_REV_DIAG
},
1103 { { L
, H
, R
, T
}, 0x2, GNM_STYLE_BORDER_DIAG
},
1104 { { L
, B
, R
, H
}, 0x2, GNM_STYLE_BORDER_DIAG
},
1106 /* Only for state 3 */
1107 { { L
, T
, V
, B
}, 0x4, GNM_STYLE_BORDER_REV_DIAG
},
1108 { { V
, T
, R
, B
}, 0x4, GNM_STYLE_BORDER_REV_DIAG
},
1109 { { L
, B
, V
, T
}, 0x4, GNM_STYLE_BORDER_DIAG
},
1110 { { V
, B
, R
, T
}, 0x4, GNM_STYLE_BORDER_DIAG
},
1112 /* Only for state 4 */
1113 { { L
, H
, V
, B
}, 0x8, GNM_STYLE_BORDER_REV_DIAG
},
1114 { { V
, T
, R
, H
}, 0x8, GNM_STYLE_BORDER_REV_DIAG
},
1115 { { L
, H
, V
, T
}, 0x8, GNM_STYLE_BORDER_DIAG
},
1116 { { V
, B
, R
, H
}, 0x8, GNM_STYLE_BORDER_DIAG
},
1118 { { 0., 0., 0., 0. }, 0, 0 }
1122 border_get_mstyle (FormatState
const *state
, GnmStyleBorderLocation
const loc
)
1124 BorderPicker
const *edge
= & state
->border
.edge
[loc
];
1126 /* Don't set borders that have not been changed */
1130 if (!edge
->is_selected
)
1131 return gnm_style_border_ref (gnm_style_border_none ());
1133 if (edge
->is_auto_color
) {
1134 color
= sheet_style_get_auto_pattern_color (state
->sheet
);
1136 guint8
const r
= (guint8
) (edge
->rgba
>> 24);
1137 guint8
const g
= (guint8
) (edge
->rgba
>> 16);
1138 guint8
const b
= (guint8
) (edge
->rgba
>> 8);
1139 guint8
const a
= (guint8
) (edge
->rgba
>> 0);
1140 color
= gnm_color_new_rgba8 (r
, g
, b
, a
);
1142 return gnm_style_border_fetch
1143 (state
->border
.edge
[loc
].pattern_index
, color
,
1144 gnm_style_border_get_orientation (loc
));
1147 /* See if either the color or pattern for any segment has changed and
1148 * apply the change to all of the lines that make up the segment.
1151 border_format_has_changed (FormatState
*state
, BorderPicker
*edge
)
1154 gboolean changed
= FALSE
;
1156 edge
->is_set
= TRUE
;
1157 if (edge
->is_auto_color
) {
1158 if (!state
->border
.is_auto_color
) {
1159 edge
->is_auto_color
= state
->border
.is_auto_color
;
1162 } else if (edge
->rgba
!= state
->border
.rgba
)
1165 if (edge
->rgba
!= state
->border
.rgba
) {
1166 edge
->rgba
= state
->border
.rgba
;
1168 for (i
= 0; line_info
[i
].states
!= 0 ; ++i
) {
1169 if (line_info
[i
].location
== edge
->index
&&
1170 state
->border
.lines
[i
] != NULL
)
1171 go_styled_object_get_style (
1172 GO_STYLED_OBJECT (state
->border
.lines
[i
]))->line
.color
= edge
->rgba
;
1175 if ((int)edge
->pattern_index
!= state
->border
.pattern
.cur_index
) {
1176 edge
->pattern_index
= state
->border
.pattern
.cur_index
;
1177 for (i
= 0; line_info
[i
].states
!= 0 ; ++i
) {
1178 if (line_info
[i
].location
== edge
->index
&&
1179 state
->border
.lines
[i
] != NULL
) {
1180 gnumeric_dashed_canvas_line_set_dash_index (
1181 GNM_DASHED_CANVAS_LINE (state
->border
.lines
[i
]),
1182 edge
->pattern_index
);
1192 * Map canvas x.y coords to a border type
1193 * Handle all of the various permutations of lines
1196 border_event (GtkWidget
*widget
, GdkEventButton
*event
, FormatState
*state
)
1198 double x
= event
->x
;
1199 double y
= event
->y
;
1201 GnmStyleBorderLocation which
;
1203 if (event
->button
!= 1)
1206 /* If we receive a double or triple translate them into single clicks */
1207 if (event
->type
== GDK_2BUTTON_PRESS
|| event
->type
== GDK_3BUTTON_PRESS
) {
1208 GdkEventType type
= event
->type
;
1209 event
->type
= GDK_BUTTON_PRESS
;
1210 border_event (widget
, event
, state
);
1211 if (event
->type
== GDK_3BUTTON_PRESS
)
1212 border_event (widget
, event
, state
);
1216 /* The edges are always there */
1217 if (x
<= L
+5.) which
= GNM_STYLE_BORDER_LEFT
;
1218 else if (y
<= T
+5.) which
= GNM_STYLE_BORDER_TOP
;
1219 else if (y
>= B
-5.) which
= GNM_STYLE_BORDER_BOTTOM
;
1220 else if (x
>= R
-5.) which
= GNM_STYLE_BORDER_RIGHT
;
1221 else switch (state
->selection_mask
) {
1223 if ((x
< V
) == (y
< H
))
1224 which
= GNM_STYLE_BORDER_REV_DIAG
;
1226 which
= GNM_STYLE_BORDER_DIAG
;
1229 if (H
-5. < y
&& y
< H
+5.)
1230 which
= GNM_STYLE_BORDER_HORIZ
;
1232 /* Map everything back to the top */
1233 if (y
> H
) y
-= H
-10.;
1235 if ((x
< V
) == (y
< H
/2.))
1236 which
= GNM_STYLE_BORDER_REV_DIAG
;
1238 which
= GNM_STYLE_BORDER_DIAG
;
1242 if (V
-5. < x
&& x
< V
+5.)
1243 which
= GNM_STYLE_BORDER_VERT
;
1245 /* Map everything back to the left */
1246 if (x
> V
) x
-= V
-10.;
1248 if ((x
< V
/2.) == (y
< H
))
1249 which
= GNM_STYLE_BORDER_REV_DIAG
;
1251 which
= GNM_STYLE_BORDER_DIAG
;
1255 if (V
-5. < x
&& x
< V
+5.)
1256 which
= GNM_STYLE_BORDER_VERT
;
1257 else if (H
-5. < y
&& y
< H
+5.)
1258 which
= GNM_STYLE_BORDER_HORIZ
;
1260 /* Map everything back to the 1st quadrant */
1261 if (x
> V
) x
-= V
-10.;
1262 if (y
> H
) y
-= H
-10.;
1264 if ((x
< V
/2.) == (y
< H
/2.))
1265 which
= GNM_STYLE_BORDER_REV_DIAG
;
1267 which
= GNM_STYLE_BORDER_DIAG
;
1272 which
= GNM_STYLE_BORDER_LEFT
;
1273 g_assert_not_reached ();
1276 edge
= &state
->border
.edge
[which
];
1277 if (!border_format_has_changed (state
, edge
) || !edge
->is_selected
)
1278 gtk_toggle_button_set_active (edge
->button
,
1279 !edge
->is_selected
);
1281 fmt_dialog_changed (state
);
1287 draw_border_preview (FormatState
*state
)
1289 static double const corners
[12][6] = {
1290 { L
-5., T
, L
, T
, L
, T
-5. },
1291 { R
+5., T
, R
, T
, R
, T
-5 },
1292 { L
-5., B
, L
, B
, L
, B
+5. },
1293 { R
+5., B
, R
, B
, R
, B
+5. },
1295 { V
-5., T
-1., V
, T
-1., V
, T
-5. },
1296 { V
+5., T
-1., V
, T
-1., V
, T
-5. },
1298 { V
-5., B
+1., V
, B
+1., V
, B
+5. },
1299 { V
+5., B
+1., V
, B
+1., V
, B
+5. },
1301 { L
-1., H
-5., L
-1., H
, L
-5., H
},
1302 { L
-1., H
+5., L
-1., H
, L
-5., H
},
1304 { R
+1., H
-5., R
+1., H
, R
+5., H
},
1305 { R
+1., H
+5., R
+1., H
, R
+5., H
}
1309 /* The first time through lets initialize */
1310 if (state
->border
.canvas
== NULL
) {
1315 state
->border
.canvas
= GOC_CANVAS (g_object_new (GOC_TYPE_CANVAS
, NULL
));
1316 gtk_widget_show (GTK_WIDGET (state
->border
.canvas
));
1317 gtk_widget_set_size_request (GTK_WIDGET (state
->border
.canvas
),
1319 go_gtk_widget_replace (go_gtk_builder_get_widget (state
->gui
, "border_sample_placeholder"),
1320 GTK_WIDGET (state
->border
.canvas
));
1321 group
= GOC_GROUP (goc_canvas_get_root (state
->border
.canvas
));
1323 g_signal_connect (G_OBJECT (state
->border
.canvas
),
1324 "button-press-event",
1325 G_CALLBACK (border_event
), state
);
1327 state
->border
.back
= goc_item_new (group
,
1329 "x", L
-10., "y", T
-10.,
1330 "width", R
-L
+20., "height", B
-T
+20.,
1332 style
= go_styled_object_get_style (GO_STYLED_OBJECT (state
->border
.back
));
1333 style
->line
.dash_type
= GO_LINE_NONE
;
1335 /* Draw the corners */
1336 points
= goc_points_new (3);
1338 for (i
= 0; i
< 12 ; ++i
) {
1340 if (!(state
->selection_mask
& 0xa))
1342 } else if (i
>= 4) {
1343 if (!(state
->selection_mask
& 0xc))
1347 for (j
= 3, k
= 5 ; --j
>= 0 ;) {
1348 points
->points
[j
].y
= corners
[i
][k
--] + .5;
1349 points
->points
[j
].x
= corners
[i
][k
--] + .5;
1353 style
= go_styled_object_get_style (GO_STYLED_OBJECT (
1354 goc_item_new (group
,
1355 goc_polyline_get_type (),
1358 style
->line
.color
= GO_COLOR_FROM_RGBA (0xa1, 0xa1, 0xa1, 0xff); /* gray63 */
1359 style
->line
.width
= 0.;
1361 goc_points_unref (points
);
1363 for (i
= 0; line_info
[i
].states
!= 0 ; ++i
) {
1364 if (line_info
[i
].states
& state
->selection_mask
) {
1365 BorderPicker
const *p
=
1366 & state
->border
.edge
[line_info
[i
].location
];
1367 state
->border
.lines
[i
] =
1368 goc_item_new (group
,
1369 gnumeric_dashed_canvas_line_get_type (),
1370 "x0", line_info
[i
].points
[0],
1371 "y0", line_info
[i
].points
[1],
1372 "x1", line_info
[i
].points
[2],
1373 "y1", line_info
[i
].points
[3],
1375 style
= go_styled_object_get_style (GO_STYLED_OBJECT (state
->border
.lines
[i
]));
1376 style
->line
.color
= p
->rgba
;
1377 gnumeric_dashed_canvas_line_set_dash_index (
1378 GNM_DASHED_CANVAS_LINE (state
->border
.lines
[i
]),
1381 state
->border
.lines
[i
] = NULL
;
1385 for (i
= 0; i
< GNM_STYLE_BORDER_EDGE_MAX
; ++i
) {
1386 BorderPicker
const *border
= &state
->border
.edge
[i
];
1389 for (j
= 0; line_info
[j
].states
!= 0 ; ++j
) {
1390 if ((int)line_info
[j
].location
== i
&&
1391 state
->border
.lines
[j
] != NULL
)
1392 goc_item_set_visible (state
->border
.lines
[j
],
1393 border
->is_selected
);
1397 fmt_dialog_changed (state
);
1401 cb_border_preset_clicked (GtkButton
*btn
, FormatState
*state
)
1403 gboolean target_state
;
1404 GnmStyleBorderLocation i
, last
;
1406 if (state
->border
.preset
[BORDER_PRESET_NONE
] == btn
) {
1407 i
= GNM_STYLE_BORDER_TOP
;
1408 last
= GNM_STYLE_BORDER_VERT
;
1409 target_state
= FALSE
;
1410 } else if (state
->border
.preset
[BORDER_PRESET_OUTLINE
] == btn
) {
1411 i
= GNM_STYLE_BORDER_TOP
;
1412 last
= GNM_STYLE_BORDER_RIGHT
;
1413 target_state
= TRUE
;
1414 } else if (state
->border
.preset
[BORDER_PRESET_INSIDE
] == btn
) {
1415 i
= GNM_STYLE_BORDER_HORIZ
;
1416 last
= GNM_STYLE_BORDER_VERT
;
1417 target_state
= TRUE
;
1419 g_warning ("Unknown border preset button");
1423 /* If we are turning things on, TOGGLE the states to
1424 * capture the current pattern and color */
1425 for (; i
<= last
; ++i
) {
1426 gtk_toggle_button_set_active (
1427 state
->border
.edge
[i
].button
,
1431 gtk_toggle_button_set_active (
1432 state
->border
.edge
[i
].button
,
1434 else if (gtk_toggle_button_get_active (
1435 state
->border
.edge
[i
].button
))
1436 /* Turn off damn it !
1437 * we really want things off not just to pick up
1440 gtk_toggle_button_set_active (
1441 state
->border
.edge
[i
].button
,
1447 * Callback routine to update the border preview when a button is clicked
1450 cb_border_toggle (GtkToggleButton
*button
, BorderPicker
*picker
)
1452 picker
->is_selected
= gtk_toggle_button_get_active (button
);
1454 /* If the format has changed and we were just toggled off,
1455 * turn ourselves back on.
1457 if (border_format_has_changed (picker
->state
, picker
) &&
1458 !picker
->is_selected
)
1459 gtk_toggle_button_set_active (button
, TRUE
);
1461 /* Update the preview lines and enable/disable them */
1462 draw_border_preview (picker
->state
);
1466 cb_border_color (G_GNUC_UNUSED GOComboColor
*combo
,
1468 G_GNUC_UNUSED gboolean is_custom
,
1469 G_GNUC_UNUSED gboolean by_user
,
1470 gboolean is_default
, FormatState
*state
)
1472 state
->border
.rgba
= c
;
1473 state
->border
.is_auto_color
= is_default
;
1485 GnmStyleBorderLocation t
;
1486 GnmBorder
const *res
;
1487 } check_border_closure_t
;
1490 * Initialize the fields of a BorderPicker, connect signals and
1494 init_border_button (FormatState
*state
, GnmStyleBorderLocation
const i
,
1496 GnmBorder
const * const border
)
1498 if (border
== NULL
) {
1499 state
->border
.edge
[i
].rgba
= 0;
1500 state
->border
.edge
[i
].is_auto_color
= TRUE
;
1501 state
->border
.edge
[i
].pattern_index
= GNM_STYLE_BORDER_INCONSISTENT
;
1502 state
->border
.edge
[i
].is_selected
= TRUE
;
1504 GnmColor
const *c
= border
->color
;
1505 state
->border
.edge
[i
].rgba
= c
->go_color
;
1506 state
->border
.edge
[i
].is_auto_color
= c
->is_auto
;
1507 state
->border
.edge
[i
].pattern_index
= border
->line_type
;
1508 state
->border
.edge
[i
].is_selected
= (border
->line_type
!= GNM_STYLE_BORDER_NONE
);
1511 state
->border
.edge
[i
].state
= state
;
1512 state
->border
.edge
[i
].index
= i
;
1513 state
->border
.edge
[i
].button
= GTK_TOGGLE_BUTTON (button
);
1514 state
->border
.edge
[i
].is_set
= FALSE
;
1516 g_return_if_fail (button
!= NULL
);
1518 gtk_toggle_button_set_active (state
->border
.edge
[i
].button
,
1519 state
->border
.edge
[i
].is_selected
);
1521 g_signal_connect (G_OBJECT (button
),
1523 G_CALLBACK (cb_border_toggle
), &state
->border
.edge
[i
]);
1525 if ((i
== GNM_STYLE_BORDER_HORIZ
&& !(state
->selection_mask
& 0xa)) ||
1526 (i
== GNM_STYLE_BORDER_VERT
&& !(state
->selection_mask
& 0xc)))
1527 gtk_widget_hide (button
);
1530 /*****************************************************************************/
1533 cb_protection_locked_toggle (GtkToggleButton
*button
, FormatState
*state
)
1535 if (state
->enable_edit
) {
1536 gnm_style_set_contents_locked (state
->result
,
1537 gtk_toggle_button_get_active (button
));
1538 fmt_dialog_changed (state
);
1543 cb_protection_hidden_toggle (GtkToggleButton
*button
, FormatState
*state
)
1545 if (state
->enable_edit
) {
1546 gnm_style_set_contents_hidden (state
->result
,
1547 gtk_toggle_button_get_active (button
));
1548 fmt_dialog_changed (state
);
1553 cb_protection_sheet_protected_toggle (GtkToggleButton
*button
, FormatState
*state
)
1555 if (state
->enable_edit
) {
1556 state
->protection
.sheet_protected_value
=
1557 gtk_toggle_button_get_active (button
);
1558 state
->protection
.sheet_protected_changed
= TRUE
;
1559 fmt_dialog_changed (state
);
1564 fmt_dialog_init_protection_page (FormatState
*state
)
1567 gboolean flag
= FALSE
;
1569 flag
= (state
->conflicts
& (1 << MSTYLE_CONTENTS_LOCKED
))
1570 ? FALSE
: gnm_style_get_contents_locked (state
->style
);
1571 w
= go_gtk_builder_get_widget (state
->gui
, "protection_locked");
1572 state
->protection
.locked
= GTK_CHECK_BUTTON (w
);
1573 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w
), flag
);
1574 g_signal_connect (G_OBJECT (w
),
1576 G_CALLBACK (cb_protection_locked_toggle
), state
);
1578 flag
= (state
->conflicts
& (1 << MSTYLE_CONTENTS_HIDDEN
))
1579 ? FALSE
: gnm_style_get_contents_hidden (state
->style
);
1580 w
= go_gtk_builder_get_widget (state
->gui
, "protection_hidden");
1581 state
->protection
.hidden
= GTK_CHECK_BUTTON (w
);
1582 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w
), flag
);
1583 g_signal_connect (G_OBJECT (w
),
1585 G_CALLBACK (cb_protection_hidden_toggle
), state
);
1587 state
->protection
.sheet_protected_changed
= FALSE
;
1588 flag
= state
->sheet
->is_protected
;
1589 w
= go_gtk_builder_get_widget (state
->gui
, "protection_sheet_protected");
1590 state
->protection
.sheet_protected
= GTK_CHECK_BUTTON (w
);
1591 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w
), flag
);
1592 g_signal_connect (G_OBJECT (w
),
1594 G_CALLBACK (cb_protection_sheet_protected_toggle
), state
);
1597 /*****************************************************************************/
1599 static GnmExprTop
const *
1600 validation_entry_to_expr (Sheet
*sheet
, GnmExprEntry
*gee
)
1603 parse_pos_init_sheet (&pp
, sheet
);
1604 return gnm_expr_entry_parse (gee
, &pp
, NULL
, FALSE
, GNM_EXPR_PARSE_DEFAULT
);
1608 validation_rebuild_validation (FormatState
*state
)
1610 ValidationType type
;
1612 if (!state
->enable_edit
)
1615 state
->validation
.changed
= FALSE
;
1616 type
= gtk_combo_box_get_active (
1617 state
->validation
.constraint_type
);
1619 if (type
!= GNM_VALIDATION_TYPE_ANY
) {
1620 ValidationStyle style
= gtk_combo_box_get_active (state
->validation
.error
.action
);
1621 ValidationOp op
= gtk_combo_box_get_active (state
->validation
.op
);
1622 char *title
= gtk_editable_get_chars (GTK_EDITABLE (state
->validation
.error
.title
), 0, -1);
1623 char *msg
= gnm_textview_get_text (state
->validation
.error
.msg
);
1624 GnmExprTop
const *texpr0
=
1625 validation_entry_to_expr (state
->sheet
,
1626 state
->validation
.expr0
.entry
);
1627 GnmExprTop
const *texpr1
= NULL
;
1629 if (texpr0
!= NULL
) {
1630 if (type
== GNM_VALIDATION_TYPE_CUSTOM
|| type
== GNM_VALIDATION_TYPE_IN_LIST
) {
1631 state
->validation
.valid
= 1;
1632 op
= GNM_VALIDATION_OP_NONE
;
1633 } else if (op
== GNM_VALIDATION_OP_BETWEEN
|| op
== GNM_VALIDATION_OP_NOT_BETWEEN
) {
1634 texpr1
= validation_entry_to_expr (state
->sheet
,
1635 state
->validation
.expr1
.entry
);
1637 state
->validation
.valid
= 2;
1639 state
->validation
.valid
= -2;
1640 gnm_expr_top_unref (texpr0
);
1643 state
->validation
.valid
= 1;
1645 state
->validation
.valid
= -1;
1647 if (state
->validation
.valid
> 0) {
1648 gboolean allow_blank
= gtk_toggle_button_get_active (state
->validation
.allow_blank
);
1649 gboolean use_dropdown
= gtk_toggle_button_get_active (state
->validation
.use_dropdown
);
1650 gnm_style_set_validation
1665 gnm_style_set_validation (state
->result
, NULL
);
1666 fmt_dialog_changed (state
);
1671 const char *icon_name
;
1672 } validation_error_actions
[] = {
1674 N_("None (silently accept invalid input)"),
1678 N_("Stop (never allow invalid input)"),
1682 N_("Warning (accept/discard invalid input)"),
1686 N_("Information (allow invalid input)"),
1687 "dialog-information"
1692 cb_validation_error_action_changed (G_GNUC_UNUSED GtkMenuShell
*ignored
,
1695 int index
= gtk_combo_box_get_active (state
->validation
.error
.action
);
1696 gboolean
const flag
= (index
> 0) &&
1697 (gtk_combo_box_get_active (state
->validation
.constraint_type
) > 0);
1699 gtk_widget_set_sensitive (GTK_WIDGET (state
->validation
.error
.title_label
), flag
);
1700 gtk_widget_set_sensitive (GTK_WIDGET (state
->validation
.error
.msg_label
), flag
);
1701 gtk_widget_set_sensitive (GTK_WIDGET (state
->validation
.error
.title
), flag
);
1702 gtk_widget_set_sensitive (GTK_WIDGET (state
->validation
.error
.msg
), flag
);
1705 char const *icon_name
= validation_error_actions
[index
].icon_name
;
1706 gtk_image_set_from_icon_name (state
->validation
.error
.image
,
1707 icon_name
, GTK_ICON_SIZE_DIALOG
);
1708 gtk_widget_show (GTK_WIDGET (state
->validation
.error
.image
));
1710 gtk_widget_hide (GTK_WIDGET (state
->validation
.error
.image
));
1712 validation_rebuild_validation (state
);
1716 cb_validation_sensitivity (G_GNUC_UNUSED GtkMenuShell
*ignored
,
1719 gboolean has_operators
= FALSE
;
1720 char const *msg0
= "";
1721 char const *msg1
= "";
1722 ValidationType
const type
= gtk_combo_box_get_active (
1723 state
->validation
.constraint_type
);
1726 case GNM_VALIDATION_TYPE_IN_LIST
: msg0
= _("Source"); break;
1727 case GNM_VALIDATION_TYPE_CUSTOM
: msg0
= _("Criteria"); break;
1729 case GNM_VALIDATION_TYPE_AS_INT
:
1730 case GNM_VALIDATION_TYPE_AS_NUMBER
:
1731 case GNM_VALIDATION_TYPE_AS_DATE
:
1732 case GNM_VALIDATION_TYPE_AS_TIME
:
1733 case GNM_VALIDATION_TYPE_TEXT_LENGTH
: {
1734 ValidationOp
const op
= gtk_combo_box_get_active (
1735 state
->validation
.op
);
1736 has_operators
= TRUE
;
1738 case GNM_VALIDATION_OP_NONE
:
1740 case GNM_VALIDATION_OP_BETWEEN
:
1741 case GNM_VALIDATION_OP_NOT_BETWEEN
:
1745 case GNM_VALIDATION_OP_EQUAL
:
1746 case GNM_VALIDATION_OP_NOT_EQUAL
:
1749 case GNM_VALIDATION_OP_GT
:
1750 case GNM_VALIDATION_OP_GTE
:
1753 case GNM_VALIDATION_OP_LT
:
1754 case GNM_VALIDATION_OP_LTE
:
1758 g_warning ("Unknown operator index %d", (int)op
);
1766 gtk_label_set_text (state
->validation
.expr0
.name
, msg0
);
1767 gtk_widget_set_sensitive (GTK_WIDGET (state
->validation
.expr0
.name
), *msg0
!= '\0');
1768 gtk_widget_set_sensitive (GTK_WIDGET (state
->validation
.expr0
.entry
), *msg0
!= '\0');
1770 gtk_label_set_text (state
->validation
.expr1
.name
, msg1
);
1771 gtk_widget_set_sensitive (GTK_WIDGET (state
->validation
.expr1
.name
), *msg1
!= '\0');
1772 gtk_widget_set_sensitive (GTK_WIDGET (state
->validation
.expr1
.entry
), *msg1
!= '\0');
1774 gtk_widget_set_sensitive (GTK_WIDGET (state
->validation
.op
),
1776 gtk_widget_set_sensitive (GTK_WIDGET (state
->validation
.operator_label
),
1779 gtk_widget_set_sensitive (GTK_WIDGET (state
->validation
.error
.action_label
),
1780 type
!= GNM_VALIDATION_TYPE_ANY
);
1781 gtk_widget_set_sensitive (GTK_WIDGET (state
->validation
.error
.action
),
1782 type
!= GNM_VALIDATION_TYPE_ANY
);
1783 gtk_widget_set_sensitive (GTK_WIDGET (state
->validation
.allow_blank
),
1784 type
!= GNM_VALIDATION_TYPE_ANY
);
1785 gtk_widget_set_sensitive (GTK_WIDGET (state
->validation
.use_dropdown
),
1786 type
== GNM_VALIDATION_TYPE_IN_LIST
);
1788 validation_rebuild_validation (state
);
1792 cb_validation_changed (G_GNUC_UNUSED GtkEntry
*ignored
,
1795 if (state
->enable_edit
)
1796 state
->validation
.changed
= TRUE
;
1800 fmt_dialog_init_validation_expr_entry (FormatState
*state
, ExprEntry
*entry
,
1801 char const *name
, int i
)
1803 entry
->name
= GTK_LABEL (go_gtk_builder_get_widget (state
->gui
, name
));
1804 entry
->entry
= gnm_expr_entry_new (state
->wbcg
, TRUE
);
1805 gtk_grid_attach (state
->validation
.criteria_grid
,
1806 GTK_WIDGET (entry
->entry
), 1, 3+i
, 3, 1);
1807 gtk_widget_show (GTK_WIDGET (entry
->entry
));
1808 gnm_editable_enters (
1809 GTK_WINDOW (state
->dialog
),
1810 GTK_WIDGET (entry
->entry
));
1811 gnm_expr_entry_set_flags (entry
->entry
, GNM_EE_FORCE_ABS_REF
| GNM_EE_SHEET_OPTIONAL
, GNM_EE_MASK
);
1812 g_signal_connect (G_OBJECT (entry
->entry
),
1814 G_CALLBACK (cb_validation_changed
), state
);
1818 cb_validation_rebuild (G_GNUC_UNUSED
void *ignored
,
1821 validation_rebuild_validation (state
);
1825 build_validation_error_combo (FormatState
*state
, GtkComboBox
*box
)
1827 GtkListStore
*store
;
1828 GtkCellRenderer
*renderer
;
1831 store
= gtk_list_store_new (2, GDK_TYPE_PIXBUF
, G_TYPE_STRING
);
1832 gtk_combo_box_set_model (box
, GTK_TREE_MODEL (store
));
1834 for (ui
= 0; ui
< G_N_ELEMENTS (validation_error_actions
); ui
++) {
1835 const char *icon_name
= validation_error_actions
[ui
].icon_name
;
1836 GdkPixbuf
*pixbuf
= icon_name
1837 ? go_gtk_widget_render_icon_pixbuf (GTK_WIDGET (wbcg_toplevel (state
->wbcg
)),
1838 icon_name
, GTK_ICON_SIZE_MENU
)
1842 gtk_list_store_append (store
, &iter
);
1843 gtk_list_store_set (store
, &iter
,
1845 1, _(validation_error_actions
[ui
].text
),
1848 g_object_unref (pixbuf
);
1851 renderer
= gtk_cell_renderer_pixbuf_new ();
1852 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (box
),
1855 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (box
), renderer
,
1859 renderer
= gtk_cell_renderer_text_new ();
1860 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (box
),
1863 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (box
), renderer
,
1867 g_object_unref (store
);
1871 fmt_dialog_init_validation_page (FormatState
*state
)
1873 GnmValidation
const *v
= NULL
;
1874 g_return_if_fail (state
!= NULL
);
1877 state
->validation
.changed
= FALSE
;
1878 state
->validation
.valid
= 1;
1879 state
->validation
.criteria_grid
= GTK_GRID (go_gtk_builder_get_widget (state
->gui
, "validation-grid"));
1880 state
->validation
.constraint_type
= GTK_COMBO_BOX (go_gtk_builder_get_widget (state
->gui
, "validation_constraint_type"));
1881 gtk_combo_box_set_active (state
->validation
.constraint_type
, 0);
1882 state
->validation
.operator_label
= GTK_LABEL (go_gtk_builder_get_widget (state
->gui
, "validation_operator_label"));
1883 state
->validation
.op
= GTK_COMBO_BOX (go_gtk_builder_get_widget (state
->gui
, "validation_operator"));
1884 gtk_combo_box_set_active (state
->validation
.op
, 0);
1885 state
->validation
.allow_blank
= GTK_TOGGLE_BUTTON(go_gtk_builder_get_widget (state
->gui
, "validation_ignore_blank"));
1886 state
->validation
.use_dropdown
= GTK_TOGGLE_BUTTON(go_gtk_builder_get_widget (state
->gui
, "validation_in_dropdown"));
1887 state
->validation
.error
.action_label
= GTK_LABEL (go_gtk_builder_get_widget (state
->gui
, "validation_error_action_label"));
1888 state
->validation
.error
.title_label
= GTK_LABEL (go_gtk_builder_get_widget (state
->gui
, "validation_error_title_label"));
1889 state
->validation
.error
.msg_label
= GTK_LABEL (go_gtk_builder_get_widget (state
->gui
, "validation_error_msg_label"));
1890 state
->validation
.error
.action
= GTK_COMBO_BOX (go_gtk_builder_get_widget (state
->gui
, "validation_error_action"));
1891 build_validation_error_combo (state
, state
->validation
.error
.action
);
1892 gtk_combo_box_set_active (state
->validation
.error
.action
, 0);
1893 state
->validation
.error
.title
= GTK_ENTRY (go_gtk_builder_get_widget (state
->gui
, "validation_error_title"));
1894 state
->validation
.error
.msg
= GTK_TEXT_VIEW (go_gtk_builder_get_widget (state
->gui
, "validation_error_msg"));
1895 state
->validation
.error
.image
= GTK_IMAGE (go_gtk_builder_get_widget (state
->gui
, "validation_error_image"));
1897 gnm_editable_enters (
1898 GTK_WINDOW (state
->dialog
),
1899 GTK_WIDGET (state
->validation
.error
.title
));
1901 g_signal_connect (state
->validation
.constraint_type
,
1903 G_CALLBACK (cb_validation_sensitivity
), state
);
1904 g_signal_connect (state
->validation
.op
,
1906 G_CALLBACK (cb_validation_sensitivity
), state
);
1907 g_signal_connect (state
->validation
.error
.action
,
1909 G_CALLBACK (cb_validation_error_action_changed
), state
);
1911 fmt_dialog_init_validation_expr_entry (state
, &state
->validation
.expr0
, "validation_expr0_name", 0);
1912 fmt_dialog_init_validation_expr_entry (state
, &state
->validation
.expr1
, "validation_expr1_name", 1);
1914 g_signal_connect (G_OBJECT (state
->validation
.allow_blank
),
1916 G_CALLBACK (cb_validation_rebuild
), state
);
1917 g_signal_connect (G_OBJECT (state
->validation
.use_dropdown
),
1919 G_CALLBACK (cb_validation_rebuild
), state
);
1920 g_signal_connect (G_OBJECT (state
->validation
.error
.title
),
1922 G_CALLBACK (cb_validation_rebuild
), state
);
1923 g_signal_connect (G_OBJECT (gtk_text_view_get_buffer (state
->validation
.error
.msg
)),
1925 G_CALLBACK (cb_validation_rebuild
), state
);
1928 if (0 == (state
->conflicts
& (1 << MSTYLE_VALIDATION
)))
1929 v
= gnm_style_get_validation (state
->style
);
1933 gtk_combo_box_set_active (state
->validation
.error
.action
, v
->style
);
1934 gtk_combo_box_set_active (state
->validation
.constraint_type
, v
->type
);
1935 gtk_combo_box_set_active (state
->validation
.op
, v
->op
);
1937 gtk_entry_set_text (GTK_ENTRY (state
->validation
.error
.title
),
1938 (v
->title
!= NULL
) ? v
->title
->str
: "");
1940 gnm_textview_set_text (GTK_TEXT_VIEW (state
->validation
.error
.msg
),
1942 gtk_toggle_button_set_active (state
->validation
.allow_blank
, v
->allow_blank
);
1943 gtk_toggle_button_set_active (state
->validation
.use_dropdown
, v
->use_dropdown
);
1945 parse_pos_init (&pp
, state
->sheet
->workbook
, state
->sheet
,
1946 state
->sv
->edit_pos
.col
, state
->sv
->edit_pos
.row
);
1947 gnm_expr_entry_load_from_expr (state
->validation
.expr0
.entry
,
1948 v
->deps
[0].texpr
, &pp
);
1949 gnm_expr_entry_load_from_expr (state
->validation
.expr1
.entry
,
1950 v
->deps
[1].texpr
, &pp
);
1953 cb_validation_sensitivity (NULL
, state
);
1954 cb_validation_error_action_changed (NULL
, state
);
1957 /*****************************************************************************/
1960 input_msg_rebuild_input_msg (FormatState
*state
)
1963 char *msg
= gnm_textview_get_text (state
->input_msg
.msg
);
1964 char const *title
= gtk_entry_get_text (state
->input_msg
.title
);
1966 im
= gnm_input_msg_new (msg
, title
);
1968 gnm_style_set_input_msg (state
->result
, im
);
1969 fmt_dialog_changed (state
);
1973 cb_input_msg_rebuild (G_GNUC_UNUSED
void *ignored
,
1976 input_msg_rebuild_input_msg (state
);
1980 cb_input_msg_flag_toggled (GtkToggleButton
*button
, FormatState
*state
)
1982 gboolean flag
= gtk_toggle_button_get_active (button
);
1984 gtk_widget_set_sensitive (GTK_WIDGET (state
->input_msg
.title_label
), flag
);
1985 gtk_widget_set_sensitive (GTK_WIDGET (state
->input_msg
.msg_label
), flag
);
1986 gtk_widget_set_sensitive (GTK_WIDGET (state
->input_msg
.title
), flag
);
1987 gtk_widget_set_sensitive (GTK_WIDGET (state
->input_msg
.msg
), flag
);
1989 if (state
->enable_edit
) {
1991 input_msg_rebuild_input_msg (state
);
1993 gnm_style_set_input_msg (state
->result
, NULL
);
1994 fmt_dialog_changed (state
);
1999 fmt_dialog_init_input_msg_page (FormatState
*state
)
2001 GnmInputMsg
const *im
= NULL
;
2003 g_return_if_fail (state
!= NULL
);
2006 state
->input_msg
.flag
= GTK_TOGGLE_BUTTON (go_gtk_builder_get_widget (state
->gui
, "input_msg_flag"));
2007 state
->input_msg
.title_label
= GTK_LABEL (go_gtk_builder_get_widget (state
->gui
, "input_msg_title_label"));
2008 state
->input_msg
.msg_label
= GTK_LABEL (go_gtk_builder_get_widget (state
->gui
, "input_msg_msg_label"));
2009 state
->input_msg
.title
= GTK_ENTRY (go_gtk_builder_get_widget (state
->gui
, "input_msg_title"));
2010 state
->input_msg
.msg
= GTK_TEXT_VIEW (go_gtk_builder_get_widget (state
->gui
, "input_msg_msg"));
2013 if (0 == (state
->conflicts
& (1 << MSTYLE_INPUT_MSG
)))
2014 im
= gnm_style_get_input_msg (state
->style
);
2016 gtk_entry_set_text (state
->input_msg
.title
,
2017 gnm_input_msg_get_title (im
));
2018 gnm_textview_set_text (state
->input_msg
.msg
,
2019 gnm_input_msg_get_msg (im
));
2021 gtk_toggle_button_set_active (state
->input_msg
.flag
, im
!= NULL
);
2023 gnm_editable_enters (
2024 GTK_WINDOW (state
->dialog
),
2025 GTK_WIDGET (state
->input_msg
.title
));
2027 g_signal_connect (G_OBJECT (state
->input_msg
.flag
),
2029 G_CALLBACK (cb_input_msg_flag_toggled
), state
);
2030 g_signal_connect (G_OBJECT (state
->input_msg
.title
),
2032 G_CALLBACK (cb_input_msg_rebuild
), state
);
2033 g_signal_connect (G_OBJECT (gtk_text_view_get_buffer (state
->input_msg
.msg
)),
2035 G_CALLBACK (cb_input_msg_rebuild
), state
);
2038 cb_input_msg_flag_toggled (state
->input_msg
.flag
, state
);
2041 /*****************************************************************************/
2043 /* button handlers */
2045 cb_fmt_dialog_dialog_buttons (GtkWidget
*btn
, FormatState
*state
)
2048 static GnmStyleBorderLocation
const bmap_ltr
[] = {
2049 GNM_STYLE_BORDER_TOP
, GNM_STYLE_BORDER_BOTTOM
,
2050 GNM_STYLE_BORDER_LEFT
, GNM_STYLE_BORDER_RIGHT
,
2051 GNM_STYLE_BORDER_REV_DIAG
, GNM_STYLE_BORDER_DIAG
,
2052 GNM_STYLE_BORDER_HORIZ
, GNM_STYLE_BORDER_VERT
2054 static GnmStyleBorderLocation
const bmap_rtl
[] = {
2055 GNM_STYLE_BORDER_TOP
, GNM_STYLE_BORDER_BOTTOM
,
2057 GNM_STYLE_BORDER_RIGHT
, GNM_STYLE_BORDER_LEFT
,
2059 GNM_STYLE_BORDER_DIAG
, GNM_STYLE_BORDER_REV_DIAG
,
2060 GNM_STYLE_BORDER_HORIZ
, GNM_STYLE_BORDER_VERT
2062 GnmStyleBorderLocation
const *bmap
= bmap_ltr
;
2064 if (NULL
!= state
->sheet
&& state
->sheet
->text_is_rtl
)
2068 if (btn
== state
->apply_button
|| btn
== state
->ok_button
) {
2071 /* We need to make sure the right sheet is active */
2072 /* since we are acting on the current selection */
2073 /* validation may have switched sheets. */
2075 wb_control_sheet_focus (GNM_WBC (state
->wbcg
),
2078 if (state
->validation
.changed
)
2079 validation_rebuild_validation (state
);
2081 if (state
->validation
.valid
< 0) {
2082 if (go_gtk_query_yes_no (
2083 GTK_WINDOW (state
->dialog
),
2085 _ ("The validation criteria are unusable. Disable validation?")))
2087 gtk_combo_box_set_active (state
->validation
.constraint_type
, 0);
2088 cb_validation_sensitivity (NULL
, state
);
2090 gtk_notebook_set_current_page (state
->notebook
, FD_VALIDATION
);
2092 if (state
->validation
.valid
== -1)
2093 gnm_expr_entry_grab_focus (state
->validation
.expr0
.entry
, FALSE
);
2095 gnm_expr_entry_grab_focus (state
->validation
.expr1
.entry
, FALSE
);
2100 if (state
->protection
.sheet_protected_changed
) {
2101 state
->sheet
->is_protected
= state
->protection
.sheet_protected_value
;
2102 state
->protection
.sheet_protected_changed
= FALSE
;
2106 if (state
->style_selector
.is_selector
) {
2107 GnmStyle
*style
= gnm_style_dup (state
->style
);
2108 for (i
= GNM_STYLE_BORDER_TOP
; i
<= GNM_STYLE_BORDER_DIAG
; i
++) {
2109 GnmBorder
*b
= border_get_mstyle (state
, i
);
2111 gnm_style_set_border
2114 (int)(i
- GNM_STYLE_BORDER_TOP
),
2117 gnm_style_merge (style
, state
->result
);
2118 dialog_cell_format_style_added
2119 (state
->style_selector
.closure
,
2121 gnm_style_unref (state
->result
);
2123 GnmBorder
*borders
[GNM_STYLE_BORDER_EDGE_MAX
];
2124 for (i
= GNM_STYLE_BORDER_TOP
; i
< GNM_STYLE_BORDER_EDGE_MAX
; i
++)
2125 borders
[i
] = border_get_mstyle (state
, i
);
2126 cmd_selection_format (GNM_WBC (state
->wbcg
),
2127 state
->result
, borders
, NULL
);
2129 /* state->result got absorbed. */
2130 /* Get a fresh style to accumulate results in */
2131 state
->result
= gnm_style_new ();
2132 sheet_update (state
->sheet
);
2135 gtk_widget_set_sensitive (state
->apply_button
, FALSE
);
2138 if (btn
!= state
->apply_button
)
2139 gtk_widget_destroy (GTK_WIDGET (state
->dialog
));
2142 /* Handler for destroy */
2144 cb_fmt_dialog_dialog_destroy (FormatState
*state
)
2146 gnm_style_unref (state
->back
.style
);
2147 gnm_style_unref (state
->style
);
2148 gnm_style_unref (state
->result
);
2149 g_object_unref (state
->gui
);
2153 /* Handler for expr-entry's focus.
2155 * NOTE: This will only become useful once the
2156 * cell format dialog is made non-modal
2159 cb_fmt_dialog_set_focus (G_GNUC_UNUSED GtkWidget
*window
,
2160 G_GNUC_UNUSED GtkWidget
*focus_widget
,
2163 if (state
->validation
.changed
)
2164 validation_rebuild_validation (state
);
2168 cb_dialog_destroy (GtkDialog
*dialog
)
2170 g_object_set_data (G_OBJECT (dialog
), "state", NULL
);
2174 /* Set initial focus */
2176 set_initial_focus (FormatState
*s
)
2178 GtkWidget
*focus_widget
= NULL
, *pagew
;
2181 pagew
= gtk_notebook_get_nth_page (s
->notebook
, fmt_dialog_page
);
2182 name
= gtk_widget_get_name (pagew
);
2184 if (strcmp (name
, "number_box") == 0) {
2185 go_format_sel_set_focus (GO_FORMAT_SEL (s
->format_sel
));
2187 } else if (strcmp (name
, "alignment_box") == 0)
2188 focus_widget
= go_gtk_builder_get_widget (s
->gui
, "halign_left");
2189 else if (strcmp (name
, "font_box") == 0)
2190 focus_widget
= GTK_WIDGET (s
->font
.selector
);
2191 else if (strcmp (name
, "border_box") == 0)
2192 focus_widget
= go_gtk_builder_get_widget (s
->gui
, "gnumeric-format-border-outline");
2193 else if (strcmp (name
, "background_box") == 0)
2194 focus_widget
= go_gtk_builder_get_widget (s
->gui
, "back_color_auto");
2195 else if (strcmp (name
, "protection_box") == 0)
2196 focus_widget
= GTK_WIDGET (s
->protection
.locked
);
2198 focus_widget
= NULL
;
2201 gtk_widget_get_can_focus (focus_widget
) &&
2202 gtk_widget_is_sensitive (focus_widget
))
2203 gtk_widget_grab_focus (focus_widget
);
2207 fmt_dialog_impl (FormatState
*state
, FormatDialogPosition_t pageno
, gint pages
)
2210 char const *const name
;
2211 GnmStyleBorderType
const pattern
;
2212 } const line_pattern_buttons
[] = {
2213 { "line_pattern_none", GNM_STYLE_BORDER_NONE
},
2214 { "line_pattern_medium_dash_dot_dot", GNM_STYLE_BORDER_MEDIUM_DASH_DOT_DOT
},
2216 { "line_pattern_hair", GNM_STYLE_BORDER_HAIR
},
2217 { "line_pattern_slant", GNM_STYLE_BORDER_SLANTED_DASH_DOT
},
2219 { "line_pattern_dotted", GNM_STYLE_BORDER_DOTTED
},
2220 { "line_pattern_medium_dash_dot", GNM_STYLE_BORDER_MEDIUM_DASH_DOT
},
2222 { "line_pattern_dash_dot_dot", GNM_STYLE_BORDER_DASH_DOT_DOT
},
2223 { "line_pattern_medium_dash", GNM_STYLE_BORDER_MEDIUM_DASH
},
2225 { "line_pattern_dash_dot", GNM_STYLE_BORDER_DASH_DOT
},
2226 { "line_pattern_medium", GNM_STYLE_BORDER_MEDIUM
},
2228 { "line_pattern_dashed", GNM_STYLE_BORDER_DASHED
},
2229 { "line_pattern_thick", GNM_STYLE_BORDER_THICK
},
2231 { "line_pattern_thin", GNM_STYLE_BORDER_THIN
},
2232 { "line_pattern_double", GNM_STYLE_BORDER_DOUBLE
},
2236 static char const *const pattern_buttons
[] = {
2237 "gnumeric-pattern-solid", "gnumeric-pattern-75grey", "gnumeric-pattern-50grey",
2238 "gnumeric-pattern-25grey", "gnumeric-pattern-125grey", "gnumeric-pattern-625grey",
2240 "gnumeric-pattern-horiz",
2241 "gnumeric-pattern-vert",
2242 "gnumeric-pattern-diag",
2243 "gnumeric-pattern-rev-diag",
2244 "gnumeric-pattern-diag-cross",
2245 "gnumeric-pattern-thick-diag-cross",
2247 "gnumeric-pattern-thin-horiz",
2248 "gnumeric-pattern-thin-vert",
2249 "gnumeric-pattern-thin-rev-diag",
2250 "gnumeric-pattern-thin-diag",
2251 "gnumeric-pattern-thin-horiz-cross",
2252 "gnumeric-pattern-thin-diag-cross",
2254 "gnumeric-pattern-small-circle",
2255 "gnumeric-pattern-semi-circle",
2256 "gnumeric-pattern-thatch",
2257 "gnumeric-pattern-large-circles",
2258 "gnumeric-pattern-bricks",
2259 "gnumeric-pattern-foreground-solid",
2264 /* The order corresponds to the BorderLocation enum */
2265 static char const *const border_buttons
[] = {
2266 "gnumeric-format-border-top", "gnumeric-format-border-bottom",
2267 "gnumeric-format-border-left", "gnumeric-format-border-right",
2268 "gnumeric-format-border-rev-diag", "gnumeric-format-border-diag",
2269 "gnumeric-format-border-inside-horiz", "gnumeric-format-border-inside-vert",
2273 /* The order corresponds to BorderPresets */
2274 static char const *const border_preset_buttons
[] = {
2275 "gnumeric-format-border-no", "gnumeric-format-border-outline", "gnumeric-format-border-inside",
2283 GOColor default_border_color
;
2284 int default_border_style
= GNM_STYLE_BORDER_THIN
;
2285 GtkStyleContext
*ctxt
;
2288 GtkWidget
*tmp
, *dialog
= go_gtk_builder_get_widget (state
->gui
, "CellFormat");
2289 g_return_if_fail (dialog
!= NULL
);
2291 gtk_window_set_title (GTK_WINDOW (dialog
), _("Format Cells"));
2294 state
->dialog
= GTK_DIALOG (dialog
);
2295 state
->notebook
= GTK_NOTEBOOK (go_gtk_builder_get_widget (state
->gui
, "notebook"));
2297 state
->enable_edit
= FALSE
; /* Enable below */
2299 state
->border
.canvas
= NULL
;
2300 state
->border
.pattern
.cur_index
= 0;
2302 state
->back
.canvas
= NULL
;
2303 state
->back
.grid
= NULL
;
2304 state
->back
.style
= gnm_style_new_default ();
2305 state
->back
.pattern
.cur_index
= 0;
2307 fmt_dialog_init_format_page (state
);
2308 fmt_dialog_init_align_page (state
);
2309 fmt_dialog_init_font_page (state
);
2310 fmt_dialog_init_background_page (state
);
2311 fmt_dialog_init_protection_page (state
);
2312 fmt_dialog_init_validation_page (state
);
2313 fmt_dialog_init_input_msg_page (state
);
2315 ctxt
= gtk_widget_get_style_context (GTK_WIDGET (state
->dialog
));
2316 gtk_style_context_get_border_color (ctxt
, GTK_STATE_FLAG_NORMAL
, &bc
);
2317 default_border_color
= GO_COLOR_FROM_GDK_RGBA (bc
);
2319 if (pageno
== FD_CURRENT
)
2320 pageno
= fmt_dialog_page
;
2321 gtk_notebook_set_current_page (state
->notebook
, pageno
);
2323 page_signal
= g_signal_connect (G_OBJECT (state
->notebook
),
2325 G_CALLBACK (cb_page_select
), NULL
);
2326 g_signal_connect (G_OBJECT (state
->notebook
),
2328 G_CALLBACK (cb_notebook_destroy
), GINT_TO_POINTER (page_signal
));
2330 /* Setup border line pattern buttons & select the 1st button */
2331 for (i
= MSTYLE_BORDER_TOP
; i
< MSTYLE_BORDER_DIAGONAL
; i
++) {
2332 GnmBorder
const *border
= gnm_style_get_border (state
->style
, i
);
2333 if (!gnm_style_border_is_blank (border
)) {
2334 default_border_color
= border
->color
->go_color
;
2335 default_border_style
= border
->line_type
;
2340 state
->border
.pattern
.draw_preview
= NULL
;
2341 state
->border
.pattern
.current_pattern
= NULL
;
2342 state
->border
.pattern
.state
= state
;
2343 state
->border
.rgba
= default_border_color
;
2344 for (i
= 0; (name
= line_pattern_buttons
[i
].name
) != NULL
; ++i
)
2345 setup_pattern_button (gtk_widget_get_screen (GTK_WIDGET (state
->dialog
)),
2346 state
->gui
, name
, &state
->border
.pattern
,
2347 i
!= 0, /* No image for None */
2349 line_pattern_buttons
[i
].pattern
,
2350 default_border_style
, 54);
2352 setup_color_pickers (state
, &state
->border
.color
, "border_color_group",
2353 "border_color_placeholder", "border_color_label",
2354 _("Automatic"), _("Border"),
2355 G_CALLBACK (cb_border_color
), MSTYLE_BORDER_TOP
, FALSE
);
2356 setup_color_pickers (state
, &state
->back
.back_color
, "back_color_group",
2357 "background_color_placeholder", "back_color_label",
2358 _("Clear Background"), _("Background"),
2359 G_CALLBACK (cb_back_preview_color
), MSTYLE_COLOR_BACK
, FALSE
);
2360 setup_color_pickers (state
, &state
->back
.pattern_color
, "pattern_color_group",
2361 "pattern_color_placeholder", "pattern_color_label",
2362 _("Automatic"), _("Pattern"),
2363 G_CALLBACK (cb_pattern_preview_color
), MSTYLE_COLOR_PATTERN
, FALSE
);
2365 /* Setup the border images */
2366 for (i
= 0; (name
= border_buttons
[i
]) != NULL
; ++i
) {
2367 GtkWidget
*tmp
= go_gtk_builder_get_widget (state
->gui
, name
);
2369 init_border_button (state
, i
, tmp
,
2371 gnm_style_border_unref (state
->borders
[i
]);
2372 state
->borders
[i
] = NULL
;
2376 /* Get the current background
2377 * A pattern of 0 is has no background.
2378 * A pattern of 1 is a solid background
2379 * All others have 2 colours and a stipple
2383 if (0 == (state
->conflicts
& (1 << MSTYLE_PATTERN
))) {
2384 selected
= gnm_style_get_pattern (state
->style
);
2385 has_back
= (selected
!= 0);
2388 /* Setup pattern buttons & select the current pattern (or the 1st
2389 * if none is selected)
2390 * NOTE: This must be done AFTER the colour has been setup to
2391 * avoid having it erased by initialization.
2393 state
->back
.pattern
.draw_preview
= &draw_pattern_selected
;
2394 state
->back
.pattern
.current_pattern
= NULL
;
2395 state
->back
.pattern
.state
= state
;
2396 for (i
= 0; (name
= pattern_buttons
[i
]) != NULL
; ++i
)
2397 setup_pattern_button (gtk_widget_get_screen (GTK_WIDGET (state
->dialog
)),
2399 &state
->back
.pattern
, TRUE
, TRUE
,
2400 i
+1, /* Pattern #s start at 1 */
2403 /* If the pattern is 0 indicating no background colour
2404 * Set background to No colour. This will set states correctly.
2407 go_combo_color_set_color_to_default (GO_COMBO_COLOR (state
->back
.back_color
.combo
));
2409 /* Setup the images in the border presets */
2410 for (i
= 0; (name
= border_preset_buttons
[i
]) != NULL
; ++i
) {
2411 GtkWidget
*tmp
= go_gtk_builder_get_widget (state
->gui
, name
);
2413 state
->border
.preset
[i
] = GTK_BUTTON (tmp
);
2414 g_signal_connect (G_OBJECT (tmp
),
2416 G_CALLBACK (cb_border_preset_clicked
), state
);
2417 if (i
== BORDER_PRESET_INSIDE
&& state
->selection_mask
!= 0x8)
2418 gtk_widget_hide (tmp
);
2422 draw_border_preview (state
);
2424 gnm_init_help_button (
2425 go_gtk_builder_get_widget (state
->gui
, "helpbutton"),
2426 GNUMERIC_HELP_LINK_CELL_FORMAT
);
2428 state
->ok_button
= go_gtk_builder_get_widget (state
->gui
, "okbutton");
2429 gtk_widget_set_sensitive (state
->ok_button
, FALSE
);
2430 g_signal_connect (G_OBJECT (state
->ok_button
),
2432 G_CALLBACK (cb_fmt_dialog_dialog_buttons
), state
);
2433 state
->apply_button
= go_gtk_builder_get_widget (state
->gui
, "applybutton");
2434 gtk_widget_set_sensitive (state
->apply_button
, FALSE
);
2435 g_signal_connect (G_OBJECT (state
->apply_button
),
2437 G_CALLBACK (cb_fmt_dialog_dialog_buttons
), state
);
2438 tmp
= go_gtk_builder_get_widget (state
->gui
, "cancelbutton");
2439 g_signal_connect (G_OBJECT (tmp
),
2441 G_CALLBACK (cb_fmt_dialog_dialog_buttons
), state
);
2443 set_initial_focus (state
);
2444 gtk_notebook_set_scrollable (state
->notebook
, TRUE
);
2446 gnm_dialog_setup_destroy_handlers (GTK_DIALOG (dialog
), state
->wbcg
,
2447 GNM_DIALOG_DESTROY_CURRENT_SHEET_REMOVED
);
2449 /* Ok, edit events from now on are real */
2450 state
->enable_edit
= TRUE
;
2452 g_signal_connect (G_OBJECT (dialog
),
2454 G_CALLBACK (cb_fmt_dialog_set_focus
), state
);
2455 /* We could now make it modeless, and arguably should do so. We must
2456 * then track the selection: styles should be applied to the current
2458 * There are some UI issues to discuss before we do this, though. Most
2460 * - will users be confused?
2461 * And on a different level:
2462 * - should the preselected style in the dialog change when another
2463 * cell is selected? May be, but then we can't first make a style,
2464 * then move around and apply it to different cells.
2467 g_object_set_data_full (G_OBJECT (state
->dialog
),
2468 "state", state
, (GDestroyNotify
)cb_fmt_dialog_dialog_destroy
);
2469 g_signal_connect (G_OBJECT (dialog
), "destroy",
2470 G_CALLBACK (cb_dialog_destroy
), NULL
);
2473 for (i
= 0; i
<= FD_LAST
; i
++) {
2474 GtkWidget
*widget
= gtk_notebook_get_nth_page
2475 (state
->notebook
, i
);
2476 if (widget
!= NULL
&& !((1<<i
) & pages
))
2477 gtk_widget_hide (widget
);
2481 gnm_restore_window_geometry (GTK_WINDOW (state
->dialog
),
2486 cb_check_cell_format (GnmCellIter
const *iter
, gpointer user
)
2488 FormatState
*state
= user
;
2489 GnmValue
const *value
= iter
->cell
->value
;
2490 GOFormat
const *common
= gnm_style_get_format (state
->style
);
2491 GOFormat
const *fmt
= value
? VALUE_FMT (value
) : NULL
;
2494 go_format_is_markup (fmt
) ||
2495 go_format_eq (common
, fmt
))
2498 if (go_format_is_general (common
)) {
2499 gnm_style_set_format (state
->style
, fmt
);
2502 state
->conflicts
|= MSTYLE_FORMAT
;
2503 return VALUE_TERMINATE
;
2508 fmt_dialog_selection_type (SheetView
*sv
,
2509 GnmRange
const *range
,
2512 FormatState
*state
= user_data
;
2513 GSList
*merged
= gnm_sheet_merge_get_overlap (sv
->sheet
, range
);
2514 GnmRange r
= *range
;
2515 gboolean allow_multi
=
2517 merged
->next
!= NULL
||
2518 !range_equal ((GnmRange
*)merged
->data
, range
);
2519 g_slist_free (merged
);
2521 /* allow_multi == FALSE && !is_singleton (range) means that we are in
2522 * an merge cell, use only the top left */
2523 if (r
.start
.col
!= r
.end
.col
)
2526 state
->selection_mask
|= 2;
2528 r
.end
.col
= r
.start
.col
;
2530 if (range
->start
.row
!= range
->end
.row
)
2533 state
->selection_mask
|= 1;
2535 r
.end
.row
= r
.start
.row
;
2538 state
->conflicts
= sheet_style_find_conflicts (state
->sheet
, &r
,
2539 &(state
->style
), state
->borders
);
2541 if ((state
->conflicts
& MSTYLE_FORMAT
) == 0 &&
2542 go_format_is_general (gnm_style_get_format (state
->style
))) {
2543 sheet_foreach_cell_in_range (state
->sheet
,
2544 CELL_ITER_IGNORE_BLANK
,
2546 cb_check_cell_format
,
2553 static FormatState
*
2554 dialog_cell_format_init (WBCGtk
*wbcg
)
2560 gui
= gnm_gtk_builder_load ("res:ui/cell-format.ui", NULL
, GO_CMD_CONTEXT (wbcg
));
2565 state
= g_new (FormatState
, 1);
2568 state
->sv
= wb_control_cur_sheet_view (GNM_WBC (wbcg
));
2569 state
->sheet
= sv_sheet (state
->sv
);
2571 edit_cell
= sheet_cell_get (state
->sheet
,
2572 state
->sv
->edit_pos
.col
,
2573 state
->sv
->edit_pos
.row
);
2575 state
->value
= (edit_cell
!= NULL
) ? edit_cell
->value
: NULL
;
2576 state
->style
= NULL
;
2577 state
->result
= gnm_style_new ();
2578 state
->selection_mask
= 0;
2580 (void) sv_selection_foreach (state
->sv
,
2581 fmt_dialog_selection_type
, state
);
2582 state
->selection_mask
= 1 << state
->selection_mask
;
2588 dialog_cell_format (WBCGtk
*wbcg
, FormatDialogPosition_t pageno
, gint pages
)
2592 g_return_if_fail (wbcg
!= NULL
);
2594 state
= dialog_cell_format_init (wbcg
);
2599 state
->style_selector
.is_selector
= FALSE
;
2600 state
->style_selector
.w
= NULL
;
2601 state
->style_selector
.closure
= NULL
;
2605 for (i
= FD_NUMBER
; i
<= FD_PROTECTION
; i
++)
2609 fmt_dialog_impl (state
, pageno
, pages
);
2611 wbc_gtk_attach_guru (state
->wbcg
, GTK_WIDGET (state
->dialog
));
2612 go_gtk_nonmodal_dialog (wbcg_toplevel (state
->wbcg
),
2613 GTK_WINDOW (state
->dialog
));
2614 gtk_widget_show (GTK_WIDGET (state
->dialog
));
2621 * - Add the 'text' elements in the preview
2624 * - Some undo capabilities in the dialog.
2625 * - How to distinguish between auto & custom colors on extraction from styles.
2629 dialog_cell_format_select_style (WBCGtk
*wbcg
, gint pages
,
2631 GnmStyle
*style
, gpointer closure
)
2635 g_return_val_if_fail (wbcg
!= NULL
, NULL
);
2636 state
= dialog_cell_format_init (wbcg
);
2637 g_return_val_if_fail (state
!= NULL
, NULL
);
2639 state
->style_selector
.is_selector
= TRUE
;
2640 state
->style_selector
.w
= w
;
2641 state
->style_selector
.closure
= closure
;
2642 state
->selection_mask
= 1;
2645 gnm_style_unref (state
->style
);
2646 state
->style
= style
;
2647 state
->conflicts
= 0;
2650 fmt_dialog_impl (state
, FD_BACKGROUND
, pages
);
2652 gtk_widget_hide (state
->apply_button
);
2654 go_gtk_nonmodal_dialog (w
, GTK_WINDOW (state
->dialog
));
2655 gtk_widget_show (GTK_WIDGET (state
->dialog
));
2657 return state
->dialog
;