2 * dialog-cell-format.c: Implements a dialog to format cells.
5 * Jody Goldberg <jody@gnome.org>
6 * Almer S. Tigelaar <almer@gnome.org>
7 * Andreas J. Guelzow <aguelzow@pyrshep.ca>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, see <https://www.gnu.org/licenses/>.
23 #include <gnumeric-config.h>
24 #include <glib/gi18n-lib.h>
30 #include <sheet-view.h>
31 #include <sheet-merge.h>
32 #include <sheet-style.h>
33 #include <style-color.h>
35 #include <selection.h>
40 #include <gnm-format.h>
44 #include <application.h>
45 #include <validation.h>
46 #include <input-msg.h>
51 #include <preview-grid.h>
52 #include <widgets/gnumeric-dashed-canvas-line.h>
53 #include <widgets/gnm-format-sel.h>
54 #include <style-conditions.h>
56 #include <goffice/goffice.h>
57 #include <goffice/canvas/goc-canvas.h>
58 #include <goffice/canvas/goc-item.h>
59 #include <goffice/canvas/goc-rectangle.h>
64 #define CELL_FORMAT_KEY "cell-format-dialog"
69 } const underline_types
[] = {
70 /* xgettext: This refers to a "none underline" */
71 { NC_("underline", "None"), UNDERLINE_NONE
},
72 { NC_("underline", "Single"), UNDERLINE_SINGLE
},
73 { NC_("underline", "Double"), UNDERLINE_DOUBLE
},
74 /* xgettext: This refers to a "single low underline" */
75 { NC_("underline", "Single Low"), UNDERLINE_SINGLE_LOW
},
76 /* xgettext: This refers to a "double low underline" */
77 { NC_("underline", "Double Low"), UNDERLINE_DOUBLE_LOW
}
80 /* The order corresponds to border_preset_buttons */
84 BORDER_PRESET_OUTLINE
,
93 struct _FormatState
*state
;
95 GtkToggleButton
*current_pattern
;
96 GtkToggleButton
*default_button
;
97 void (*draw_preview
) (struct _FormatState
*);
101 struct _FormatState
*state
;
104 GCallback preview_update
;
108 struct _FormatState
*state
;
109 GtkToggleButton
*button
;
110 GnmStyleBorderType pattern_index
;
111 gboolean is_selected
; /* Is it selected */
112 GnmStyleBorderLocation index
;
114 gboolean is_auto_color
;
115 gboolean is_set
; /* Has the element been changed */
123 typedef struct _FormatState
{
127 GtkNotebook
*notebook
;
128 GtkWidget
*apply_button
;
129 GtkWidget
*ok_button
;
134 unsigned int conflicts
;
135 GnmStyle
*style
, *result
;
136 GnmBorder
*borders
[GNM_STYLE_BORDER_EDGE_MAX
];
139 gboolean enable_edit
;
141 GtkWidget
* format_sel
;
144 GtkCheckButton
*wrap
;
145 GtkSpinButton
*indent_button
;
146 GtkWidget
*indent_label
;
148 GORotationSel
*rotation
;
152 GtkWidget
*underline_picker
;
153 GnmUnderline underline
;
157 GtkButton
*preset
[BORDER_PRESET_MAX
];
161 BorderPicker edge
[GNM_STYLE_BORDER_EDGE_MAX
];
164 gboolean is_auto_color
;
165 PatternPicker pattern
;
169 GnmPreviewGrid
*grid
;
172 ColorPicker back_color
;
173 ColorPicker pattern_color
;
174 PatternPicker pattern
;
177 GtkCheckButton
*hidden
, *locked
, *sheet_protected
;
179 gboolean sheet_protected_changed
;
180 gboolean sheet_protected_value
;
183 GtkGrid
*criteria_grid
;
184 GtkComboBox
*constraint_type
;
185 GtkLabel
*operator_label
;
187 ExprEntry expr0
, expr1
;
188 GtkToggleButton
*allow_blank
;
189 GtkToggleButton
*use_dropdown
;
192 GtkLabel
*action_label
;
193 GtkLabel
*title_label
;
204 GtkToggleButton
*flag
;
206 GtkLabel
*title_label
;
212 gboolean is_selector
;
221 CONDITIONS_NUM_COLUMNS
226 /*****************************************************************************/
227 /* Some utility routines shared by all pages */
230 * A utility routine to help mark the attributes as being changed
231 * VERY stupid for now.
234 fmt_dialog_changed (FormatState
*state
)
240 if (!state
->enable_edit
)
243 gfs
= GO_FORMAT_SEL (state
->format_sel
);
244 fmt
= go_format_sel_get_fmt (gfs
);
245 ok
= !go_format_is_invalid (fmt
);
247 gtk_widget_set_sensitive (state
->apply_button
, ok
);
248 gtk_widget_set_sensitive (state
->ok_button
, ok
);
251 /* Default to the 'Format' page but remember which page we were on between
253 static FormatDialogPosition_t fmt_dialog_page
= FD_NUMBER
;
256 /* The last currency selected */
257 static int fmt_dialog_currency
= 0;
261 * Callback routine to help remember which format tab was selected
262 * between dialog invocations.
265 cb_page_select (G_GNUC_UNUSED GtkNotebook
*notebook
,
266 G_GNUC_UNUSED GtkWidget
*page
,
268 G_GNUC_UNUSED gpointer user_data
)
270 fmt_dialog_page
= page_num
;
274 cb_notebook_destroy (GtkWidget
*nb
, gpointer page_sig_ptr
)
276 g_signal_handler_disconnect (nb
, GPOINTER_TO_UINT (page_sig_ptr
));
280 * Callback routine to give radio button like behaviour to the
281 * set of toggle buttons used for line & background patterns.
284 cb_toggle_changed (GtkToggleButton
*button
, PatternPicker
*picker
)
286 if (gtk_toggle_button_get_active (button
) &&
287 picker
->current_pattern
!= button
) {
288 gtk_toggle_button_set_active (picker
->current_pattern
, FALSE
);
289 picker
->current_pattern
= button
;
290 picker
->cur_index
= GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button
), "index"));
291 if (picker
->draw_preview
)
292 picker
->draw_preview (picker
->state
);
297 * Setup routine to associate images with toggle buttons
298 * and to adjust the relief so it looks nice.
301 setup_pattern_button (GdkScreen
*screen
,
303 char const *const name
,
304 PatternPicker
*picker
,
308 int const select_index
,
311 GtkWidget
*tmp
= go_gtk_builder_get_widget (gui
, name
);
313 GtkButton
*button
= GTK_BUTTON (tmp
);
315 char *res
= g_strconcat ("/org/gnumeric/gnumeric/images/", name
, ".png", NULL
);
318 image
= gtk_image_new_from_icon_name (name
, GTK_ICON_SIZE_DIALOG
);
320 /* gtk_image_new_from_resource() is unable to load pixdata with gdk-pixbuf >= 2.36.1
321 * because it uses the gdk_pixbuf_loader API and the pixdata module has been removed
322 * because of a security issue. See #776004. */
323 GdkPixbuf
*pixbuf
= gdk_pixbuf_new_from_resource (res
, NULL
);
324 image
= gtk_image_new_from_pixbuf (pixbuf
);
325 g_object_unref (pixbuf
);
328 gtk_widget_show (image
);
329 gtk_container_add (GTK_CONTAINER (tmp
), image
);
332 if (picker
->current_pattern
== NULL
) {
333 picker
->default_button
= GTK_TOGGLE_BUTTON (button
);
334 picker
->current_pattern
= picker
->default_button
;
335 picker
->cur_index
= index
;
338 gtk_button_set_relief (button
, GTK_RELIEF_NONE
);
339 g_signal_connect (G_OBJECT (button
),
341 G_CALLBACK (cb_toggle_changed
), picker
);
342 g_object_set_data (G_OBJECT (button
), "index",
343 GINT_TO_POINTER (index
));
345 /* Set the state AFTER the signal to get things redrawn correctly */
346 if (index
== select_index
) {
347 picker
->cur_index
= index
;
348 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button
),
352 g_warning ("CellFormat: Unexpected missing widget");
356 setup_color_pickers (FormatState
*state
,
358 char const *color_group
,
359 char const *placeholder
,
361 char const *default_caption
,
363 GCallback preview_update
,
365 gboolean allow_alpha
)
367 GtkWidget
*combo
, *w
, *frame
;
369 GnmColor
*mcolor
= NULL
;
370 GnmColor
*def_sc
= NULL
;
373 case MSTYLE_COLOR_PATTERN
:
374 if (0 == (state
->conflicts
& (1 << MSTYLE_COLOR_PATTERN
)))
375 mcolor
= gnm_style_get_pattern_color (state
->style
);
379 case MSTYLE_BORDER_TOP
: /* MSTYLE_BORDER_TOP is abused as representing all borders. */
380 def_sc
= sheet_style_get_auto_pattern_color (state
->sheet
);
382 case MSTYLE_FONT_COLOR
:
383 if (0 == (state
->conflicts
& (1 << MSTYLE_FONT_COLOR
)))
384 mcolor
= gnm_style_get_font_color (state
->style
);
385 def_sc
= style_color_auto_font ();
387 case MSTYLE_COLOR_BACK
:
388 if (0 == (state
->conflicts
& (1 << MSTYLE_COLOR_BACK
)))
389 mcolor
= gnm_style_get_back_color (state
->style
);
390 def_sc
= style_color_auto_back ();
393 g_warning ("Unhandled style element!");
395 cg
= go_color_group_fetch (color_group
, NULL
);
396 combo
= go_combo_color_new (NULL
, default_caption
,
397 def_sc
? def_sc
->go_color
: GO_COLOR_BLACK
, cg
);
399 go_combo_box_set_title (GO_COMBO_BOX (combo
), caption
);
401 /* Connect to the sample canvas and redraw it */
402 g_signal_connect (G_OBJECT (combo
),
404 G_CALLBACK (preview_update
), state
);
406 if (mcolor
&& !mcolor
->is_auto
)
407 go_combo_color_set_color (GO_COMBO_COLOR (combo
),
410 go_combo_color_set_color_to_default (GO_COMBO_COLOR (combo
));
413 go_combo_color_set_allow_alpha (GO_COMBO_COLOR (combo
), TRUE
);
414 frame
= gtk_frame_new (NULL
);
415 gtk_frame_set_shadow_type (GTK_FRAME (frame
), GTK_SHADOW_OUT
);
416 gtk_container_add (GTK_CONTAINER (frame
), combo
);
417 gtk_widget_show_all (frame
);
419 w
= go_gtk_builder_get_widget (state
->gui
, placeholder
);
420 go_gtk_widget_replace (w
, frame
);
422 w
= go_gtk_builder_get_widget (state
->gui
, label
);
423 gtk_label_set_mnemonic_widget (GTK_LABEL (w
), combo
);
425 style_color_unref (def_sc
);
427 if (picker
!= NULL
) {
428 picker
->combo
= combo
;
429 picker
->preview_update
= preview_update
;
433 /*****************************************************************************/
436 cb_number_format_changed (G_GNUC_UNUSED GtkWidget
*widget
,
440 gboolean changed
= FALSE
;
441 g_return_if_fail (state
!= NULL
);
443 if (!state
->enable_edit
)
447 GOFormat
*format
= go_format_new_from_XL (fmt
);
448 gnm_style_set_format (state
->result
, format
);
449 go_format_unref (format
);
454 fmt_dialog_changed (state
);
458 fmt_dialog_init_format_page (FormatState
*state
)
461 GODateConventions
const *date_conv
= sheet_date_conv (state
->sheet
);
463 state
->format_sel
= gnm_format_sel_new ();
464 gfs
= GO_FORMAT_SEL (state
->format_sel
);
466 gtk_notebook_prepend_page (GTK_NOTEBOOK (state
->notebook
),
468 gtk_label_new (_("Number")));
469 gtk_widget_show (GTK_WIDGET (gfs
));
471 if (0 == (state
->conflicts
& (1 << MSTYLE_FORMAT
))) {
472 GOFormat
const *fmt
= gnm_style_get_format (state
->style
);
473 go_format_sel_set_style_format (gfs
, fmt
);
475 go_format_sel_set_dateconv (gfs
, date_conv
);
476 go_format_sel_editable_enters (gfs
, GTK_WINDOW (state
->dialog
));
478 g_signal_connect (G_OBJECT (state
->format_sel
), "format_changed",
479 G_CALLBACK (cb_number_format_changed
), state
);
482 /*****************************************************************************/
485 cb_indent_changed (GtkEditable
*editable
, FormatState
*state
)
487 if (state
->enable_edit
) {
488 GtkSpinButton
*sb
= GTK_SPIN_BUTTON (editable
);
489 int val
= gtk_spin_button_get_value_as_int (sb
);
491 if (state
->align
.indent
!= val
) {
492 state
->align
.indent
= val
;
493 gnm_style_set_indent (state
->result
, val
);
494 fmt_dialog_changed (state
);
500 cb_align_h_toggle (GtkToggleButton
*button
, FormatState
*state
)
502 if (!gtk_toggle_button_get_active (button
))
505 if (state
->enable_edit
) {
506 GnmHAlign
const new_h
=
507 GPOINTER_TO_INT (g_object_get_data (
508 G_OBJECT (button
), "align"));
509 gboolean
const supports_indent
=
510 (new_h
== GNM_HALIGN_LEFT
|| new_h
== GNM_HALIGN_RIGHT
);
511 gnm_style_set_align_h (state
->result
, new_h
);
512 gtk_widget_set_sensitive (GTK_WIDGET (state
->align
.indent_button
),
514 gtk_widget_set_sensitive (GTK_WIDGET (state
->align
.indent_label
),
516 /* TODO: Should we 0 the indent ? */
517 fmt_dialog_changed (state
);
522 cb_align_v_toggle (GtkToggleButton
*button
, FormatState
*state
)
524 if (!gtk_toggle_button_get_active (button
))
527 if (state
->enable_edit
) {
528 gnm_style_set_align_v (
530 GPOINTER_TO_INT (g_object_get_data (
531 G_OBJECT (button
), "align")));
532 fmt_dialog_changed (state
);
537 cb_align_wrap_toggle (GtkToggleButton
*button
, FormatState
*state
)
539 if (state
->enable_edit
) {
540 gnm_style_set_wrap_text (state
->result
,
541 gtk_toggle_button_get_active (button
));
542 fmt_dialog_changed (state
);
547 fmt_dialog_init_align_radio (char const *const name
,
548 int const val
, int const target
,
552 GtkWidget
*tmp
= go_gtk_builder_get_widget (state
->gui
, name
);
554 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tmp
),
556 g_object_set_data (G_OBJECT (tmp
), "align",
557 GINT_TO_POINTER (val
));
558 g_signal_connect (G_OBJECT (tmp
),
565 cb_rotation_changed (G_GNUC_UNUSED GORotationSel
*grs
, int angle
, FormatState
*state
)
569 gnm_style_set_rotation (state
->result
, angle
);
570 fmt_dialog_changed (state
);
574 fmt_dialog_init_align_page (FormatState
*state
)
577 char const *const name
;
579 } const h_buttons
[] = {
580 { "halign_left", GNM_HALIGN_LEFT
},
581 { "halign_center", GNM_HALIGN_CENTER
},
582 { "halign_right", GNM_HALIGN_RIGHT
},
583 { "halign_general", GNM_HALIGN_GENERAL
},
584 { "halign_justify", GNM_HALIGN_JUSTIFY
},
585 { "halign_fill", GNM_HALIGN_FILL
},
586 { "halign_center_across_selection", GNM_HALIGN_CENTER_ACROSS_SELECTION
},
587 { "halign_distributed", GNM_HALIGN_DISTRIBUTED
},
591 char const *const name
;
593 } const v_buttons
[] = {
594 { "valign_top", GNM_VALIGN_TOP
},
595 { "valign_center", GNM_VALIGN_CENTER
},
596 { "valign_bottom", GNM_VALIGN_BOTTOM
},
597 { "valign_justify", GNM_VALIGN_JUSTIFY
},
598 { "valign_distributed", GNM_VALIGN_DISTRIBUTED
},
603 gboolean wrap
= FALSE
;
604 GnmHAlign h
= GNM_HALIGN_GENERAL
;
605 GnmVAlign v
= GNM_VALIGN_CENTER
;
609 if (0 == (state
->conflicts
& (1 << MSTYLE_ALIGN_H
)))
610 h
= gnm_style_get_align_h (state
->style
);
611 if (0 == (state
->conflicts
& (1 << MSTYLE_ALIGN_V
)))
612 v
= gnm_style_get_align_v (state
->style
);
614 /* Setup the horizontal buttons */
615 for (i
= 0; (name
= h_buttons
[i
].name
) != NULL
; ++i
)
616 fmt_dialog_init_align_radio (name
, h_buttons
[i
].align
,
618 G_CALLBACK (cb_align_h_toggle
));
620 /* Setup the vertical buttons */
621 for (i
= 0; (name
= v_buttons
[i
].name
) != NULL
; ++i
)
622 fmt_dialog_init_align_radio (name
, v_buttons
[i
].align
,
624 G_CALLBACK (cb_align_v_toggle
));
626 /* Setup the wrap button, and assign the current value */
627 if (0 == (state
->conflicts
& (1 << MSTYLE_WRAP_TEXT
)))
628 wrap
= gnm_style_get_wrap_text (state
->style
);
630 w
= go_gtk_builder_get_widget (state
->gui
, "align_wrap");
631 state
->align
.wrap
= GTK_CHECK_BUTTON (w
);
632 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w
), wrap
);
633 g_signal_connect (G_OBJECT (w
),
635 G_CALLBACK (cb_align_wrap_toggle
), state
);
637 if (0 != (state
->conflicts
& (1 << MSTYLE_INDENT
)) ||
638 (h
!= GNM_HALIGN_LEFT
&& h
!= GNM_HALIGN_RIGHT
))
639 state
->align
.indent
= 0;
641 state
->align
.indent
= gnm_style_get_indent (state
->style
);
643 state
->align
.indent_label
=
644 go_gtk_builder_get_widget (state
->gui
, "halign_indent_label");
645 w
= go_gtk_builder_get_widget (state
->gui
, "halign_indent");
646 state
->align
.indent_button
= GTK_SPIN_BUTTON (w
);
647 gtk_spin_button_set_value (state
->align
.indent_button
, state
->align
.indent
);
648 gtk_widget_set_sensitive (GTK_WIDGET (state
->align
.indent_button
),
649 (h
== GNM_HALIGN_LEFT
|| h
== GNM_HALIGN_RIGHT
));
650 gtk_widget_set_sensitive (GTK_WIDGET (state
->align
.indent_label
),
651 (h
== GNM_HALIGN_LEFT
|| h
== GNM_HALIGN_RIGHT
));
653 /* Catch changes to the spin box */
654 g_signal_connect (G_OBJECT (w
),
656 G_CALLBACK (cb_indent_changed
), state
);
658 /* Catch <return> in the spin box */
659 gnm_editable_enters (
660 GTK_WINDOW (state
->dialog
),
663 /* setup the rotation canvas */
664 if (0 == (state
->conflicts
& (1 << MSTYLE_ROTATION
))) {
665 r
= gnm_style_get_rotation (state
->style
);
670 state
->align
.rotation
= (GORotationSel
*) go_rotation_sel_new ();
671 go_rotation_sel_set_rotation (state
->align
.rotation
, r
);
672 g_signal_connect (G_OBJECT (state
->align
.rotation
), "rotation-changed",
673 G_CALLBACK (cb_rotation_changed
), state
);
674 go_gtk_widget_replace (go_gtk_builder_get_widget (state
->gui
, "rotation_placeholder"),
675 GTK_WIDGET (state
->align
.rotation
));
678 /*****************************************************************************/
681 cb_font_changed (G_GNUC_UNUSED GtkWidget
*widget
,
682 PangoAttrList
*attrs
, FormatState
*state
)
684 PangoAttrIterator
*aiter
;
685 const PangoAttribute
*attr
;
686 GnmStyle
*res
= state
->result
;
687 GOFontScript script
= GO_FONT_SCRIPT_STANDARD
;
688 gboolean has_script_attr
= FALSE
;
691 gboolean changed
= FALSE
;
692 g_return_if_fail (state
!= NULL
);
694 if (!state
->enable_edit
)
697 aiter
= pango_attr_list_get_iterator (attrs
);
699 attr
= pango_attr_iterator_get (aiter
, PANGO_ATTR_FAMILY
);
701 const char *s
= ((PangoAttrString
*)attr
)->value
;
702 if (!gnm_style_is_element_set (res
, MSTYLE_FONT_NAME
) ||
703 !g_str_equal (s
, gnm_style_get_font_name (res
))) {
705 gnm_style_set_font_name (res
, s
);
709 attr
= pango_attr_iterator_get (aiter
, PANGO_ATTR_SIZE
);
711 int i
= ((PangoAttrInt
*)attr
)->value
;
712 double d
= i
/ (double)PANGO_SCALE
;
713 if (!gnm_style_is_element_set (res
, MSTYLE_FONT_SIZE
) ||
714 d
!= gnm_style_get_font_size (res
)) {
716 gnm_style_set_font_size (res
, d
);
720 attr
= pango_attr_iterator_get (aiter
, PANGO_ATTR_WEIGHT
);
722 int i
= ((PangoAttrInt
*)attr
)->value
;
723 gboolean b
= (i
>= PANGO_WEIGHT_BOLD
);
724 if (!gnm_style_is_element_set (res
, MSTYLE_FONT_BOLD
) ||
725 b
!= gnm_style_get_font_bold (res
)) {
727 gnm_style_set_font_bold (res
, b
);
731 attr
= pango_attr_iterator_get (aiter
, PANGO_ATTR_STYLE
);
733 int i
= ((PangoAttrInt
*)attr
)->value
;
734 gboolean b
= (i
!= PANGO_STYLE_NORMAL
);
735 if (!gnm_style_is_element_set (res
, MSTYLE_FONT_ITALIC
) ||
736 b
!= gnm_style_get_font_italic (res
)) {
738 gnm_style_set_font_italic (res
, b
);
742 attr
= pango_attr_iterator_get (aiter
, PANGO_ATTR_UNDERLINE
);
744 /* Underline is special: we go beyond what pango has */
745 GnmUnderline u
= state
->font
.underline
;
746 if (!gnm_style_is_element_set (res
, MSTYLE_FONT_UNDERLINE
) ||
747 u
!= gnm_style_get_font_uline (res
)) {
749 gnm_style_set_font_uline (res
, u
);
753 attr
= pango_attr_iterator_get (aiter
, PANGO_ATTR_STRIKETHROUGH
);
755 int i
= ((PangoAttrInt
*)attr
)->value
;
756 gboolean b
= (i
!= 0);
757 if (!gnm_style_is_element_set (res
, MSTYLE_FONT_STRIKETHROUGH
) ||
758 b
!= gnm_style_get_font_strike (res
)) {
760 gnm_style_set_font_strike (res
, b
);
764 attr
= pango_attr_iterator_get (aiter
, go_pango_attr_subscript_get_attr_type ());
766 has_script_attr
= TRUE
;
767 if (((GOPangoAttrSubscript
*)attr
)->val
)
768 script
= GO_FONT_SCRIPT_SUB
;
770 attr
= pango_attr_iterator_get (aiter
, go_pango_attr_superscript_get_attr_type ());
772 has_script_attr
= TRUE
;
773 if (((GOPangoAttrSuperscript
*)attr
)->val
)
774 script
= GO_FONT_SCRIPT_SUPER
;
776 if (has_script_attr
&&
777 (!gnm_style_is_element_set (res
, MSTYLE_FONT_SCRIPT
) ||
778 script
!= gnm_style_get_font_script (res
))) {
780 gnm_style_set_font_script (res
, script
);
783 attr
= pango_attr_iterator_get (aiter
, PANGO_ATTR_FOREGROUND
);
785 ? gnm_color_new_pango (&((PangoAttrColor
*)attr
)->color
)
786 : style_color_auto_font ();
787 if (!gnm_style_is_element_set (res
, MSTYLE_FONT_COLOR
) ||
788 !style_color_equal (c
, gnm_style_get_font_color (res
))) {
790 gnm_style_set_font_color (res
, c
);
792 style_color_unref (c
);
794 pango_attr_iterator_destroy (aiter
);
797 fmt_dialog_changed (state
);
801 change_font_attr (FormatState
*state
, PangoAttribute
*attr
)
803 GOFontSel
*gfs
= state
->font
.selector
;
804 PangoAttrList
*attrs
= pango_attr_list_copy
805 (go_font_sel_get_sample_attributes (gfs
));
806 attr
->start_index
= 0;
807 attr
->end_index
= -1;
808 pango_attr_list_change (attrs
, attr
);
809 go_font_sel_set_sample_attributes (gfs
, attrs
);
810 cb_font_changed (NULL
, attrs
, state
);
811 pango_attr_list_unref (attrs
);
815 set_font_underline (FormatState
*state
, GnmUnderline uline
)
817 PangoUnderline pu
= gnm_translate_underline_to_pango (uline
);
818 GOOptionMenu
*om
= GO_OPTION_MENU (state
->font
.underline_picker
);
819 GtkMenuShell
*ms
= GTK_MENU_SHELL (go_option_menu_get_menu (om
));
822 if (uline
!= state
->font
.underline
) {
823 state
->font
.underline
= uline
;
824 change_font_attr (state
, pango_attr_underline_new (pu
));
827 children
= gtk_container_get_children (GTK_CONTAINER (ms
));
828 for (l
= children
; l
; l
= l
->next
) {
829 GtkMenuItem
*item
= GTK_MENU_ITEM (l
->data
);
830 GnmUnderline u
= GPOINTER_TO_INT
831 (g_object_get_data (G_OBJECT (item
), "value"));
833 go_option_menu_select_item (om
, item
);
835 g_list_free (children
);
839 cb_underline_changed (GOOptionMenu
*om
, FormatState
*state
)
841 GtkWidget
*selected
= go_option_menu_get_history (om
);
847 u
= GPOINTER_TO_INT (g_object_get_data (G_OBJECT (selected
), "value"));
848 set_font_underline (state
, u
);
852 /* Manually insert the font selector, and setup signals */
854 fmt_dialog_init_font_page (FormatState
*state
)
857 GtkWidget
*font_widget
;
858 gboolean strikethrough
= FALSE
;
859 GOFontScript script
= GO_FONT_SCRIPT_STANDARD
;
860 GODateConventions
const *date_conv
= sheet_date_conv (state
->sheet
);
861 GnmColor
*mcolor
= NULL
;
865 up
= state
->font
.underline_picker
= go_option_menu_build
866 (C_("underline", "None"), UNDERLINE_NONE
,
867 C_("underline", "Single"), UNDERLINE_SINGLE
,
868 C_("underline", "Double"), UNDERLINE_DOUBLE
,
869 C_("underline", "Single Low"), UNDERLINE_SINGLE_LOW
,
870 C_("underline", "Double Low"), UNDERLINE_DOUBLE_LOW
,
872 g_signal_connect (up
,
873 "changed", G_CALLBACK (cb_underline_changed
), state
);
874 def_sc
= style_color_auto_font ();
875 cg
= go_color_group_fetch ("fore_color_group", NULL
);
876 font_widget
= g_object_new (GO_TYPE_FONT_SEL
,
879 "color-unset-text", _("Automatic"),
881 "color-default", def_sc
->go_color
,
882 "show-underline", TRUE
,
883 "underline-picker", up
,
885 "show-strikethrough", TRUE
,
890 style_color_unref (def_sc
);
891 state
->font
.selector
= GO_FONT_SEL (font_widget
);
894 gtk_widget_show (font_widget
);
895 gtk_container_add (GTK_CONTAINER (go_gtk_builder_get_widget (state
->gui
, "font_sel_placeholder")),
898 go_font_sel_editable_enters (state
->font
.selector
,
899 GTK_WINDOW (state
->dialog
));
902 char *s
= format_value (NULL
, state
->value
, -1, date_conv
);
903 go_font_sel_set_sample_text (state
->font
.selector
, s
);
907 if (0 == (state
->conflicts
& (1 << MSTYLE_FONT_NAME
))) {
908 const char *family
= gnm_style_get_font_name (state
->style
);
909 go_font_sel_set_family (state
->font
.selector
, family
);
912 if (0 == (state
->conflicts
& (1 << MSTYLE_FONT_BOLD
)) &&
913 0 == (state
->conflicts
& (1 << MSTYLE_FONT_ITALIC
))) {
914 gboolean is_bold
= gnm_style_get_font_bold (state
->style
);
915 gboolean is_italic
= gnm_style_get_font_italic (state
->style
);
917 go_font_sel_set_style
918 (state
->font
.selector
,
919 is_bold
? PANGO_WEIGHT_BOLD
: PANGO_WEIGHT_NORMAL
,
920 is_italic
? PANGO_STYLE_ITALIC
: PANGO_STYLE_NORMAL
);
923 if (0 == (state
->conflicts
& (1 << MSTYLE_FONT_SIZE
))) {
924 double pts
= gnm_style_get_font_size (state
->style
);
925 go_font_sel_set_size (state
->font
.selector
,
929 state
->font
.underline
= UNDERLINE_NONE
;
930 if (0 == (state
->conflicts
& (1 << MSTYLE_FONT_UNDERLINE
))) {
931 GnmUnderline ut
= gnm_style_get_font_uline (state
->style
);
932 set_font_underline (state
, ut
);
935 if (0 == (state
->conflicts
& (1 << MSTYLE_FONT_COLOR
)))
936 mcolor
= gnm_style_get_font_color (state
->style
);
937 go_font_sel_set_color (state
->font
.selector
,
938 mcolor
? mcolor
->go_color
: GO_COLOR_BLACK
,
939 !mcolor
|| mcolor
->is_auto
);
941 if (0 == (state
->conflicts
& (1 << MSTYLE_FONT_STRIKETHROUGH
)))
942 strikethrough
= gnm_style_get_font_strike (state
->style
);
943 go_font_sel_set_strikethrough (state
->font
.selector
, strikethrough
);
945 if (0 == (state
->conflicts
& (1 << MSTYLE_FONT_SCRIPT
)))
946 script
= gnm_style_get_font_script (state
->style
);
947 go_font_sel_set_script (state
->font
.selector
, script
);
949 if (0 == (state
->conflicts
& (1 << MSTYLE_FONT_COLOR
)))
952 go_color_to_pango (gnm_style_get_font_color (state
->style
)->go_color
,
955 g_signal_connect (G_OBJECT (state
->font
.selector
),
957 G_CALLBACK (cb_font_changed
), state
);
960 /*****************************************************************************/
963 back_style_changed (FormatState
*state
)
965 g_return_if_fail (state
->back
.style
!= NULL
);
967 fmt_dialog_changed (state
);
969 if (state
->enable_edit
) {
970 gnm_style_merge_element (state
->result
, state
->back
.style
, MSTYLE_PATTERN
);
971 gnm_style_merge_element (state
->result
, state
->back
.style
, MSTYLE_COLOR_BACK
);
972 gnm_style_merge_element (state
->result
, state
->back
.style
, MSTYLE_COLOR_PATTERN
);
973 goc_item_set (GOC_ITEM (state
->back
.grid
),
974 "default-style", state
->back
.style
,
980 cb_back_preview_color (G_GNUC_UNUSED GOComboColor
*combo
,
982 G_GNUC_UNUSED gboolean is_custom
,
983 G_GNUC_UNUSED gboolean by_user
,
989 g_return_if_fail (c
);
992 sc
= style_color_auto_back ();
993 gnm_style_set_pattern (state
->back
.style
, 0);
995 sc
= gnm_color_new_go (c
);
996 gnm_style_set_pattern (state
->back
.style
, state
->back
.pattern
.cur_index
);
999 gnm_style_set_back_color (state
->back
.style
, sc
);
1000 back_style_changed (state
);
1004 cb_pattern_preview_color (G_GNUC_UNUSED GOComboColor
*combo
,
1006 G_GNUC_UNUSED gboolean is_custom
,
1007 G_GNUC_UNUSED gboolean by_user
,
1008 gboolean is_default
, FormatState
*state
)
1010 GnmColor
*col
= is_default
1011 ? sheet_style_get_auto_pattern_color (state
->sheet
)
1012 : gnm_color_new_go (c
);
1014 gnm_style_set_pattern_color (state
->back
.style
, col
);
1016 back_style_changed (state
);
1020 draw_pattern_selected (FormatState
*state
)
1022 gnm_style_set_pattern (state
->back
.style
, state
->back
.pattern
.cur_index
);
1023 back_style_changed (state
);
1027 fmt_dialog_init_background_page (FormatState
*state
)
1033 widget
= g_object_new (GOC_TYPE_CANVAS
, NULL
);
1034 state
->back
.canvas
= GOC_CANVAS (widget
);
1035 gtk_widget_set_size_request (widget
, w
, h
);
1037 widget
= go_gtk_builder_get_widget (state
->gui
, "back_sample_frame");
1038 gtk_container_add (GTK_CONTAINER (widget
),
1039 GTK_WIDGET (state
->back
.canvas
));
1040 gtk_widget_show_all (widget
);
1042 state
->back
.grid
= GNM_PREVIEW_GRID (goc_item_new (
1043 goc_canvas_get_root (state
->back
.canvas
),
1044 gnm_preview_grid_get_type (),
1045 "render-gridlines", FALSE
,
1046 "default-col-width", w
,
1047 "default-row-height", h
,
1048 "default-style", state
->back
.style
,
1052 /*****************************************************************************/
1055 * This is self-evident.
1056 * I stared at it for 15 minutes before realizing that it's self-evident,
1057 * but it is. - jon_kare
1059 * @points: x, y coordinates for the endpoints of the line segment.
1060 * @states: A bitmap of states the coordinates are valid for.
1061 * @location: Location.
1063 #define L 10. /* Left */
1064 #define R 140. /* Right */
1065 #define T 10. /* Top */
1066 #define B 90. /* Bottom */
1067 #define H 50. /* Horizontal Middle */
1068 #define V 75. /* Vertical Middle */
1072 double const points
[4];
1074 GnmStyleBorderLocation
const location
;
1075 } const line_info
[] = {
1077 state 1 = single cell;
1078 state 2 = multi vert, single horiz (A1:A2);
1079 state 3 = single vert, multi horiz (A1:B1);
1080 state 4 = multi vertical & multi horizontal
1084 { { L
, T
, R
, T
}, 0xf, GNM_STYLE_BORDER_TOP
},
1085 { { L
, B
, R
, B
}, 0xf, GNM_STYLE_BORDER_BOTTOM
},
1086 { { L
, T
, L
, B
}, 0xf, GNM_STYLE_BORDER_LEFT
},
1087 { { R
, T
, R
, B
}, 0xf, GNM_STYLE_BORDER_RIGHT
},
1089 /* Only for state 2 & 4 */
1090 { { L
, H
, R
, H
}, 0xa, GNM_STYLE_BORDER_HORIZ
},
1092 /* Only for state 3 & 4 */
1093 { { V
, T
, V
, B
}, 0xc, GNM_STYLE_BORDER_VERT
},
1095 /* Only for state 1 & 4 */
1096 { { L
, T
, R
, B
}, 0x9, GNM_STYLE_BORDER_REV_DIAG
},
1097 { { L
, B
, R
, T
}, 0x9, GNM_STYLE_BORDER_DIAG
},
1099 /* Only for state 2 */
1100 { { L
, T
, R
, H
}, 0x2, GNM_STYLE_BORDER_REV_DIAG
},
1101 { { L
, H
, R
, B
}, 0x2, GNM_STYLE_BORDER_REV_DIAG
},
1102 { { L
, H
, R
, T
}, 0x2, GNM_STYLE_BORDER_DIAG
},
1103 { { L
, B
, R
, H
}, 0x2, GNM_STYLE_BORDER_DIAG
},
1105 /* Only for state 3 */
1106 { { L
, T
, V
, B
}, 0x4, GNM_STYLE_BORDER_REV_DIAG
},
1107 { { V
, T
, R
, B
}, 0x4, GNM_STYLE_BORDER_REV_DIAG
},
1108 { { L
, B
, V
, T
}, 0x4, GNM_STYLE_BORDER_DIAG
},
1109 { { V
, B
, R
, T
}, 0x4, GNM_STYLE_BORDER_DIAG
},
1111 /* Only for state 4 */
1112 { { L
, H
, V
, B
}, 0x8, GNM_STYLE_BORDER_REV_DIAG
},
1113 { { V
, T
, R
, H
}, 0x8, GNM_STYLE_BORDER_REV_DIAG
},
1114 { { L
, H
, V
, T
}, 0x8, GNM_STYLE_BORDER_DIAG
},
1115 { { V
, B
, R
, H
}, 0x8, GNM_STYLE_BORDER_DIAG
},
1117 { { 0., 0., 0., 0. }, 0, 0 }
1121 border_get_mstyle (FormatState
const *state
, GnmStyleBorderLocation
const loc
)
1123 BorderPicker
const *edge
= & state
->border
.edge
[loc
];
1125 /* Don't set borders that have not been changed */
1129 if (!edge
->is_selected
)
1130 return gnm_style_border_ref (gnm_style_border_none ());
1132 if (edge
->is_auto_color
) {
1133 color
= sheet_style_get_auto_pattern_color (state
->sheet
);
1135 guint8
const r
= (guint8
) (edge
->rgba
>> 24);
1136 guint8
const g
= (guint8
) (edge
->rgba
>> 16);
1137 guint8
const b
= (guint8
) (edge
->rgba
>> 8);
1138 guint8
const a
= (guint8
) (edge
->rgba
>> 0);
1139 color
= gnm_color_new_rgba8 (r
, g
, b
, a
);
1141 return gnm_style_border_fetch
1142 (state
->border
.edge
[loc
].pattern_index
, color
,
1143 gnm_style_border_get_orientation (loc
));
1146 /* See if either the color or pattern for any segment has changed and
1147 * apply the change to all of the lines that make up the segment.
1150 border_format_has_changed (FormatState
*state
, BorderPicker
*edge
)
1153 gboolean changed
= FALSE
;
1155 edge
->is_set
= TRUE
;
1156 if (edge
->is_auto_color
) {
1157 if (!state
->border
.is_auto_color
) {
1158 edge
->is_auto_color
= state
->border
.is_auto_color
;
1161 } else if (edge
->rgba
!= state
->border
.rgba
)
1164 if (edge
->rgba
!= state
->border
.rgba
) {
1165 edge
->rgba
= state
->border
.rgba
;
1167 for (i
= 0; line_info
[i
].states
!= 0 ; ++i
) {
1168 if (line_info
[i
].location
== edge
->index
&&
1169 state
->border
.lines
[i
] != NULL
)
1170 go_styled_object_get_style (
1171 GO_STYLED_OBJECT (state
->border
.lines
[i
]))->line
.color
= edge
->rgba
;
1174 if ((int)edge
->pattern_index
!= state
->border
.pattern
.cur_index
) {
1175 edge
->pattern_index
= state
->border
.pattern
.cur_index
;
1176 for (i
= 0; line_info
[i
].states
!= 0 ; ++i
) {
1177 if (line_info
[i
].location
== edge
->index
&&
1178 state
->border
.lines
[i
] != NULL
) {
1179 gnumeric_dashed_canvas_line_set_dash_index (
1180 GNM_DASHED_CANVAS_LINE (state
->border
.lines
[i
]),
1181 edge
->pattern_index
);
1191 * Map canvas x.y coords to a border type
1192 * Handle all of the various permutations of lines
1195 border_event (GtkWidget
*widget
, GdkEventButton
*event
, FormatState
*state
)
1197 double x
= event
->x
;
1198 double y
= event
->y
;
1200 GnmStyleBorderLocation which
;
1202 if (event
->button
!= 1)
1205 /* If we receive a double or triple translate them into single clicks */
1206 if (event
->type
== GDK_2BUTTON_PRESS
|| event
->type
== GDK_3BUTTON_PRESS
) {
1207 GdkEventType type
= event
->type
;
1208 event
->type
= GDK_BUTTON_PRESS
;
1209 border_event (widget
, event
, state
);
1210 if (event
->type
== GDK_3BUTTON_PRESS
)
1211 border_event (widget
, event
, state
);
1215 /* The edges are always there */
1216 if (x
<= L
+5.) which
= GNM_STYLE_BORDER_LEFT
;
1217 else if (y
<= T
+5.) which
= GNM_STYLE_BORDER_TOP
;
1218 else if (y
>= B
-5.) which
= GNM_STYLE_BORDER_BOTTOM
;
1219 else if (x
>= R
-5.) which
= GNM_STYLE_BORDER_RIGHT
;
1220 else switch (state
->selection_mask
) {
1222 if ((x
< V
) == (y
< H
))
1223 which
= GNM_STYLE_BORDER_REV_DIAG
;
1225 which
= GNM_STYLE_BORDER_DIAG
;
1228 if (H
-5. < y
&& y
< H
+5.)
1229 which
= GNM_STYLE_BORDER_HORIZ
;
1231 /* Map everything back to the top */
1232 if (y
> H
) y
-= H
-10.;
1234 if ((x
< V
) == (y
< H
/2.))
1235 which
= GNM_STYLE_BORDER_REV_DIAG
;
1237 which
= GNM_STYLE_BORDER_DIAG
;
1241 if (V
-5. < x
&& x
< V
+5.)
1242 which
= GNM_STYLE_BORDER_VERT
;
1244 /* Map everything back to the left */
1245 if (x
> V
) x
-= V
-10.;
1247 if ((x
< V
/2.) == (y
< H
))
1248 which
= GNM_STYLE_BORDER_REV_DIAG
;
1250 which
= GNM_STYLE_BORDER_DIAG
;
1254 if (V
-5. < x
&& x
< V
+5.)
1255 which
= GNM_STYLE_BORDER_VERT
;
1256 else if (H
-5. < y
&& y
< H
+5.)
1257 which
= GNM_STYLE_BORDER_HORIZ
;
1259 /* Map everything back to the 1st quadrant */
1260 if (x
> V
) x
-= V
-10.;
1261 if (y
> H
) y
-= H
-10.;
1263 if ((x
< V
/2.) == (y
< H
/2.))
1264 which
= GNM_STYLE_BORDER_REV_DIAG
;
1266 which
= GNM_STYLE_BORDER_DIAG
;
1271 which
= GNM_STYLE_BORDER_LEFT
;
1272 g_assert_not_reached ();
1275 edge
= &state
->border
.edge
[which
];
1276 if (!border_format_has_changed (state
, edge
) || !edge
->is_selected
)
1277 gtk_toggle_button_set_active (edge
->button
,
1278 !edge
->is_selected
);
1280 fmt_dialog_changed (state
);
1286 draw_border_preview (FormatState
*state
)
1288 static double const corners
[12][6] = {
1289 { L
-5., T
, L
, T
, L
, T
-5. },
1290 { R
+5., T
, R
, T
, R
, T
-5 },
1291 { L
-5., B
, L
, B
, L
, B
+5. },
1292 { R
+5., B
, R
, B
, R
, B
+5. },
1294 { V
-5., T
-1., V
, T
-1., V
, T
-5. },
1295 { V
+5., T
-1., V
, T
-1., V
, T
-5. },
1297 { V
-5., B
+1., V
, B
+1., V
, B
+5. },
1298 { V
+5., B
+1., V
, B
+1., V
, B
+5. },
1300 { L
-1., H
-5., L
-1., H
, L
-5., H
},
1301 { L
-1., H
+5., L
-1., H
, L
-5., H
},
1303 { R
+1., H
-5., R
+1., H
, R
+5., H
},
1304 { R
+1., H
+5., R
+1., H
, R
+5., H
}
1308 /* The first time through lets initialize */
1309 if (state
->border
.canvas
== NULL
) {
1314 state
->border
.canvas
= GOC_CANVAS (g_object_new (GOC_TYPE_CANVAS
, NULL
));
1315 gtk_widget_show (GTK_WIDGET (state
->border
.canvas
));
1316 gtk_widget_set_size_request (GTK_WIDGET (state
->border
.canvas
),
1318 go_gtk_widget_replace (go_gtk_builder_get_widget (state
->gui
, "border_sample_placeholder"),
1319 GTK_WIDGET (state
->border
.canvas
));
1320 group
= GOC_GROUP (goc_canvas_get_root (state
->border
.canvas
));
1322 g_signal_connect (G_OBJECT (state
->border
.canvas
),
1323 "button-press-event",
1324 G_CALLBACK (border_event
), state
);
1326 state
->border
.back
= goc_item_new (group
,
1328 "x", L
-10., "y", T
-10.,
1329 "width", R
-L
+20., "height", B
-T
+20.,
1331 style
= go_styled_object_get_style (GO_STYLED_OBJECT (state
->border
.back
));
1332 style
->line
.dash_type
= GO_LINE_NONE
;
1334 /* Draw the corners */
1335 points
= goc_points_new (3);
1337 for (i
= 0; i
< 12 ; ++i
) {
1339 if (!(state
->selection_mask
& 0xa))
1341 } else if (i
>= 4) {
1342 if (!(state
->selection_mask
& 0xc))
1346 for (j
= 3, k
= 5 ; --j
>= 0 ;) {
1347 points
->points
[j
].y
= corners
[i
][k
--] + .5;
1348 points
->points
[j
].x
= corners
[i
][k
--] + .5;
1352 style
= go_styled_object_get_style (GO_STYLED_OBJECT (
1353 goc_item_new (group
,
1354 goc_polyline_get_type (),
1357 style
->line
.color
= GO_COLOR_FROM_RGBA (0xa1, 0xa1, 0xa1, 0xff); /* gray63 */
1358 style
->line
.width
= 0.;
1360 goc_points_unref (points
);
1362 for (i
= 0; line_info
[i
].states
!= 0 ; ++i
) {
1363 if (line_info
[i
].states
& state
->selection_mask
) {
1364 BorderPicker
const *p
=
1365 & state
->border
.edge
[line_info
[i
].location
];
1366 state
->border
.lines
[i
] =
1367 goc_item_new (group
,
1368 gnumeric_dashed_canvas_line_get_type (),
1369 "x0", line_info
[i
].points
[0],
1370 "y0", line_info
[i
].points
[1],
1371 "x1", line_info
[i
].points
[2],
1372 "y1", line_info
[i
].points
[3],
1374 style
= go_styled_object_get_style (GO_STYLED_OBJECT (state
->border
.lines
[i
]));
1375 style
->line
.color
= p
->rgba
;
1376 gnumeric_dashed_canvas_line_set_dash_index (
1377 GNM_DASHED_CANVAS_LINE (state
->border
.lines
[i
]),
1380 state
->border
.lines
[i
] = NULL
;
1384 for (i
= 0; i
< GNM_STYLE_BORDER_EDGE_MAX
; ++i
) {
1385 BorderPicker
const *border
= &state
->border
.edge
[i
];
1388 for (j
= 0; line_info
[j
].states
!= 0 ; ++j
) {
1389 if ((int)line_info
[j
].location
== i
&&
1390 state
->border
.lines
[j
] != NULL
)
1391 goc_item_set_visible (state
->border
.lines
[j
],
1392 border
->is_selected
);
1396 fmt_dialog_changed (state
);
1400 cb_border_preset_clicked (GtkButton
*btn
, FormatState
*state
)
1402 gboolean target_state
;
1403 GnmStyleBorderLocation i
, last
;
1405 if (state
->border
.preset
[BORDER_PRESET_NONE
] == btn
) {
1406 i
= GNM_STYLE_BORDER_TOP
;
1407 last
= GNM_STYLE_BORDER_VERT
;
1408 target_state
= FALSE
;
1409 } else if (state
->border
.preset
[BORDER_PRESET_OUTLINE
] == btn
) {
1410 i
= GNM_STYLE_BORDER_TOP
;
1411 last
= GNM_STYLE_BORDER_RIGHT
;
1412 target_state
= TRUE
;
1413 } else if (state
->border
.preset
[BORDER_PRESET_INSIDE
] == btn
) {
1414 i
= GNM_STYLE_BORDER_HORIZ
;
1415 last
= GNM_STYLE_BORDER_VERT
;
1416 target_state
= TRUE
;
1418 g_warning ("Unknown border preset button");
1422 /* If we are turning things on, TOGGLE the states to
1423 * capture the current pattern and color */
1424 for (; i
<= last
; ++i
) {
1425 gtk_toggle_button_set_active (
1426 state
->border
.edge
[i
].button
,
1430 gtk_toggle_button_set_active (
1431 state
->border
.edge
[i
].button
,
1433 else if (gtk_toggle_button_get_active (
1434 state
->border
.edge
[i
].button
))
1435 /* Turn off damn it !
1436 * we really want things off not just to pick up
1439 gtk_toggle_button_set_active (
1440 state
->border
.edge
[i
].button
,
1446 * Callback routine to update the border preview when a button is clicked
1449 cb_border_toggle (GtkToggleButton
*button
, BorderPicker
*picker
)
1451 picker
->is_selected
= gtk_toggle_button_get_active (button
);
1453 /* If the format has changed and we were just toggled off,
1454 * turn ourselves back on.
1456 if (border_format_has_changed (picker
->state
, picker
) &&
1457 !picker
->is_selected
)
1458 gtk_toggle_button_set_active (button
, TRUE
);
1460 /* Update the preview lines and enable/disable them */
1461 draw_border_preview (picker
->state
);
1465 cb_border_color (G_GNUC_UNUSED GOComboColor
*combo
,
1467 G_GNUC_UNUSED gboolean is_custom
,
1468 G_GNUC_UNUSED gboolean by_user
,
1469 gboolean is_default
, FormatState
*state
)
1471 state
->border
.rgba
= c
;
1472 state
->border
.is_auto_color
= is_default
;
1484 GnmStyleBorderLocation t
;
1485 GnmBorder
const *res
;
1486 } check_border_closure_t
;
1489 * Initialize the fields of a BorderPicker, connect signals and
1493 init_border_button (FormatState
*state
, GnmStyleBorderLocation
const i
,
1495 GnmBorder
const * const border
)
1497 if (border
== NULL
) {
1498 state
->border
.edge
[i
].rgba
= 0;
1499 state
->border
.edge
[i
].is_auto_color
= TRUE
;
1500 state
->border
.edge
[i
].pattern_index
= GNM_STYLE_BORDER_INCONSISTENT
;
1501 state
->border
.edge
[i
].is_selected
= TRUE
;
1503 GnmColor
const *c
= border
->color
;
1504 state
->border
.edge
[i
].rgba
= c
->go_color
;
1505 state
->border
.edge
[i
].is_auto_color
= c
->is_auto
;
1506 state
->border
.edge
[i
].pattern_index
= border
->line_type
;
1507 state
->border
.edge
[i
].is_selected
= (border
->line_type
!= GNM_STYLE_BORDER_NONE
);
1510 state
->border
.edge
[i
].state
= state
;
1511 state
->border
.edge
[i
].index
= i
;
1512 state
->border
.edge
[i
].button
= GTK_TOGGLE_BUTTON (button
);
1513 state
->border
.edge
[i
].is_set
= FALSE
;
1515 g_return_if_fail (button
!= NULL
);
1517 gtk_toggle_button_set_active (state
->border
.edge
[i
].button
,
1518 state
->border
.edge
[i
].is_selected
);
1520 g_signal_connect (G_OBJECT (button
),
1522 G_CALLBACK (cb_border_toggle
), &state
->border
.edge
[i
]);
1524 if ((i
== GNM_STYLE_BORDER_HORIZ
&& !(state
->selection_mask
& 0xa)) ||
1525 (i
== GNM_STYLE_BORDER_VERT
&& !(state
->selection_mask
& 0xc)))
1526 gtk_widget_hide (button
);
1529 /*****************************************************************************/
1532 cb_protection_locked_toggle (GtkToggleButton
*button
, FormatState
*state
)
1534 if (state
->enable_edit
) {
1535 gnm_style_set_contents_locked (state
->result
,
1536 gtk_toggle_button_get_active (button
));
1537 fmt_dialog_changed (state
);
1542 cb_protection_hidden_toggle (GtkToggleButton
*button
, FormatState
*state
)
1544 if (state
->enable_edit
) {
1545 gnm_style_set_contents_hidden (state
->result
,
1546 gtk_toggle_button_get_active (button
));
1547 fmt_dialog_changed (state
);
1552 cb_protection_sheet_protected_toggle (GtkToggleButton
*button
, FormatState
*state
)
1554 if (state
->enable_edit
) {
1555 state
->protection
.sheet_protected_value
=
1556 gtk_toggle_button_get_active (button
);
1557 state
->protection
.sheet_protected_changed
= TRUE
;
1558 fmt_dialog_changed (state
);
1563 fmt_dialog_init_protection_page (FormatState
*state
)
1566 gboolean flag
= FALSE
;
1568 flag
= (state
->conflicts
& (1 << MSTYLE_CONTENTS_LOCKED
))
1569 ? FALSE
: gnm_style_get_contents_locked (state
->style
);
1570 w
= go_gtk_builder_get_widget (state
->gui
, "protection_locked");
1571 state
->protection
.locked
= GTK_CHECK_BUTTON (w
);
1572 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w
), flag
);
1573 g_signal_connect (G_OBJECT (w
),
1575 G_CALLBACK (cb_protection_locked_toggle
), state
);
1577 flag
= (state
->conflicts
& (1 << MSTYLE_CONTENTS_HIDDEN
))
1578 ? FALSE
: gnm_style_get_contents_hidden (state
->style
);
1579 w
= go_gtk_builder_get_widget (state
->gui
, "protection_hidden");
1580 state
->protection
.hidden
= GTK_CHECK_BUTTON (w
);
1581 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w
), flag
);
1582 g_signal_connect (G_OBJECT (w
),
1584 G_CALLBACK (cb_protection_hidden_toggle
), state
);
1586 state
->protection
.sheet_protected_changed
= FALSE
;
1587 flag
= state
->sheet
->is_protected
;
1588 w
= go_gtk_builder_get_widget (state
->gui
, "protection_sheet_protected");
1589 state
->protection
.sheet_protected
= GTK_CHECK_BUTTON (w
);
1590 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w
), flag
);
1591 g_signal_connect (G_OBJECT (w
),
1593 G_CALLBACK (cb_protection_sheet_protected_toggle
), state
);
1596 /*****************************************************************************/
1598 static GnmExprTop
const *
1599 validation_entry_to_expr (Sheet
*sheet
, GnmExprEntry
*gee
)
1602 parse_pos_init_sheet (&pp
, sheet
);
1603 return gnm_expr_entry_parse (gee
, &pp
, NULL
, FALSE
, GNM_EXPR_PARSE_DEFAULT
);
1607 validation_rebuild_validation (FormatState
*state
)
1609 ValidationType type
;
1611 if (!state
->enable_edit
)
1614 state
->validation
.changed
= FALSE
;
1615 type
= gtk_combo_box_get_active (
1616 state
->validation
.constraint_type
);
1618 if (type
!= GNM_VALIDATION_TYPE_ANY
) {
1619 ValidationStyle style
= gtk_combo_box_get_active (state
->validation
.error
.action
);
1620 ValidationOp op
= gtk_combo_box_get_active (state
->validation
.op
);
1621 char *title
= gtk_editable_get_chars (GTK_EDITABLE (state
->validation
.error
.title
), 0, -1);
1622 char *msg
= gnm_textview_get_text (state
->validation
.error
.msg
);
1623 GnmExprTop
const *texpr0
=
1624 validation_entry_to_expr (state
->sheet
,
1625 state
->validation
.expr0
.entry
);
1626 GnmExprTop
const *texpr1
= NULL
;
1628 if (texpr0
!= NULL
) {
1629 if (type
== GNM_VALIDATION_TYPE_CUSTOM
|| type
== GNM_VALIDATION_TYPE_IN_LIST
) {
1630 state
->validation
.valid
= 1;
1631 op
= GNM_VALIDATION_OP_NONE
;
1632 } else if (op
== GNM_VALIDATION_OP_BETWEEN
|| op
== GNM_VALIDATION_OP_NOT_BETWEEN
) {
1633 texpr1
= validation_entry_to_expr (state
->sheet
,
1634 state
->validation
.expr1
.entry
);
1636 state
->validation
.valid
= 2;
1638 state
->validation
.valid
= -2;
1639 gnm_expr_top_unref (texpr0
);
1642 state
->validation
.valid
= 1;
1644 state
->validation
.valid
= -1;
1646 if (state
->validation
.valid
> 0) {
1647 gboolean allow_blank
= gtk_toggle_button_get_active (state
->validation
.allow_blank
);
1648 gboolean use_dropdown
= gtk_toggle_button_get_active (state
->validation
.use_dropdown
);
1649 gnm_style_set_validation
1664 gnm_style_set_validation (state
->result
, NULL
);
1665 fmt_dialog_changed (state
);
1670 const char *icon_name
;
1671 } validation_error_actions
[] = {
1673 N_("None (silently accept invalid input)"),
1677 N_("Stop (never allow invalid input)"),
1681 N_("Warning (accept/discard invalid input)"),
1685 N_("Information (allow invalid input)"),
1686 "dialog-information"
1691 cb_validation_error_action_changed (G_GNUC_UNUSED GtkMenuShell
*ignored
,
1694 int index
= gtk_combo_box_get_active (state
->validation
.error
.action
);
1695 gboolean
const flag
= (index
> 0) &&
1696 (gtk_combo_box_get_active (state
->validation
.constraint_type
) > 0);
1698 gtk_widget_set_sensitive (GTK_WIDGET (state
->validation
.error
.title_label
), flag
);
1699 gtk_widget_set_sensitive (GTK_WIDGET (state
->validation
.error
.msg_label
), flag
);
1700 gtk_widget_set_sensitive (GTK_WIDGET (state
->validation
.error
.title
), flag
);
1701 gtk_widget_set_sensitive (GTK_WIDGET (state
->validation
.error
.msg
), flag
);
1704 char const *icon_name
= validation_error_actions
[index
].icon_name
;
1705 gtk_image_set_from_icon_name (state
->validation
.error
.image
,
1706 icon_name
, GTK_ICON_SIZE_DIALOG
);
1707 gtk_widget_show (GTK_WIDGET (state
->validation
.error
.image
));
1709 gtk_widget_hide (GTK_WIDGET (state
->validation
.error
.image
));
1711 validation_rebuild_validation (state
);
1715 cb_validation_sensitivity (G_GNUC_UNUSED GtkMenuShell
*ignored
,
1718 gboolean has_operators
= FALSE
;
1719 char const *msg0
= "";
1720 char const *msg1
= "";
1721 ValidationType
const type
= gtk_combo_box_get_active (
1722 state
->validation
.constraint_type
);
1725 case GNM_VALIDATION_TYPE_IN_LIST
: msg0
= _("Source"); break;
1726 case GNM_VALIDATION_TYPE_CUSTOM
: msg0
= _("Criteria"); break;
1728 case GNM_VALIDATION_TYPE_AS_INT
:
1729 case GNM_VALIDATION_TYPE_AS_NUMBER
:
1730 case GNM_VALIDATION_TYPE_AS_DATE
:
1731 case GNM_VALIDATION_TYPE_AS_TIME
:
1732 case GNM_VALIDATION_TYPE_TEXT_LENGTH
: {
1733 ValidationOp
const op
= gtk_combo_box_get_active (
1734 state
->validation
.op
);
1735 has_operators
= TRUE
;
1737 case GNM_VALIDATION_OP_NONE
:
1739 case GNM_VALIDATION_OP_BETWEEN
:
1740 case GNM_VALIDATION_OP_NOT_BETWEEN
:
1744 case GNM_VALIDATION_OP_EQUAL
:
1745 case GNM_VALIDATION_OP_NOT_EQUAL
:
1748 case GNM_VALIDATION_OP_GT
:
1749 case GNM_VALIDATION_OP_GTE
:
1752 case GNM_VALIDATION_OP_LT
:
1753 case GNM_VALIDATION_OP_LTE
:
1757 g_warning ("Unknown operator index %d", (int)op
);
1765 gtk_label_set_text (state
->validation
.expr0
.name
, msg0
);
1766 gtk_widget_set_sensitive (GTK_WIDGET (state
->validation
.expr0
.name
), *msg0
!= '\0');
1767 gtk_widget_set_sensitive (GTK_WIDGET (state
->validation
.expr0
.entry
), *msg0
!= '\0');
1769 gtk_label_set_text (state
->validation
.expr1
.name
, msg1
);
1770 gtk_widget_set_sensitive (GTK_WIDGET (state
->validation
.expr1
.name
), *msg1
!= '\0');
1771 gtk_widget_set_sensitive (GTK_WIDGET (state
->validation
.expr1
.entry
), *msg1
!= '\0');
1773 gtk_widget_set_sensitive (GTK_WIDGET (state
->validation
.op
),
1775 gtk_widget_set_sensitive (GTK_WIDGET (state
->validation
.operator_label
),
1778 gtk_widget_set_sensitive (GTK_WIDGET (state
->validation
.error
.action_label
),
1779 type
!= GNM_VALIDATION_TYPE_ANY
);
1780 gtk_widget_set_sensitive (GTK_WIDGET (state
->validation
.error
.action
),
1781 type
!= GNM_VALIDATION_TYPE_ANY
);
1782 gtk_widget_set_sensitive (GTK_WIDGET (state
->validation
.allow_blank
),
1783 type
!= GNM_VALIDATION_TYPE_ANY
);
1784 gtk_widget_set_sensitive (GTK_WIDGET (state
->validation
.use_dropdown
),
1785 type
== GNM_VALIDATION_TYPE_IN_LIST
);
1787 validation_rebuild_validation (state
);
1791 cb_validation_changed (G_GNUC_UNUSED GtkEntry
*ignored
,
1794 if (state
->enable_edit
)
1795 state
->validation
.changed
= TRUE
;
1799 fmt_dialog_init_validation_expr_entry (FormatState
*state
, ExprEntry
*entry
,
1800 char const *name
, int i
)
1802 entry
->name
= GTK_LABEL (go_gtk_builder_get_widget (state
->gui
, name
));
1803 entry
->entry
= gnm_expr_entry_new (state
->wbcg
, TRUE
);
1804 gtk_grid_attach (state
->validation
.criteria_grid
,
1805 GTK_WIDGET (entry
->entry
), 1, 3+i
, 3, 1);
1806 gtk_widget_show (GTK_WIDGET (entry
->entry
));
1807 gnm_editable_enters (
1808 GTK_WINDOW (state
->dialog
),
1809 GTK_WIDGET (entry
->entry
));
1810 gnm_expr_entry_set_flags (entry
->entry
, GNM_EE_FORCE_ABS_REF
| GNM_EE_SHEET_OPTIONAL
, GNM_EE_MASK
);
1811 g_signal_connect (G_OBJECT (entry
->entry
),
1813 G_CALLBACK (cb_validation_changed
), state
);
1817 cb_validation_rebuild (G_GNUC_UNUSED
void *ignored
,
1820 validation_rebuild_validation (state
);
1824 build_validation_error_combo (FormatState
*state
, GtkComboBox
*box
)
1826 GtkListStore
*store
;
1827 GtkCellRenderer
*renderer
;
1830 store
= gtk_list_store_new (2, GDK_TYPE_PIXBUF
, G_TYPE_STRING
);
1831 gtk_combo_box_set_model (box
, GTK_TREE_MODEL (store
));
1833 for (ui
= 0; ui
< G_N_ELEMENTS (validation_error_actions
); ui
++) {
1834 const char *icon_name
= validation_error_actions
[ui
].icon_name
;
1835 GdkPixbuf
*pixbuf
= icon_name
1836 ? go_gtk_widget_render_icon_pixbuf (GTK_WIDGET (wbcg_toplevel (state
->wbcg
)),
1837 icon_name
, GTK_ICON_SIZE_MENU
)
1841 gtk_list_store_append (store
, &iter
);
1842 gtk_list_store_set (store
, &iter
,
1844 1, _(validation_error_actions
[ui
].text
),
1847 g_object_unref (pixbuf
);
1850 renderer
= gtk_cell_renderer_pixbuf_new ();
1851 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (box
),
1854 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (box
), renderer
,
1858 renderer
= gtk_cell_renderer_text_new ();
1859 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (box
),
1862 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (box
), renderer
,
1866 g_object_unref (store
);
1870 fmt_dialog_init_validation_page (FormatState
*state
)
1872 GnmValidation
const *v
= NULL
;
1873 g_return_if_fail (state
!= NULL
);
1876 state
->validation
.changed
= FALSE
;
1877 state
->validation
.valid
= 1;
1878 state
->validation
.criteria_grid
= GTK_GRID (go_gtk_builder_get_widget (state
->gui
, "validation-grid"));
1879 state
->validation
.constraint_type
= GTK_COMBO_BOX (go_gtk_builder_get_widget (state
->gui
, "validation_constraint_type"));
1880 gtk_combo_box_set_active (state
->validation
.constraint_type
, 0);
1881 state
->validation
.operator_label
= GTK_LABEL (go_gtk_builder_get_widget (state
->gui
, "validation_operator_label"));
1882 state
->validation
.op
= GTK_COMBO_BOX (go_gtk_builder_get_widget (state
->gui
, "validation_operator"));
1883 gtk_combo_box_set_active (state
->validation
.op
, 0);
1884 state
->validation
.allow_blank
= GTK_TOGGLE_BUTTON(go_gtk_builder_get_widget (state
->gui
, "validation_ignore_blank"));
1885 state
->validation
.use_dropdown
= GTK_TOGGLE_BUTTON(go_gtk_builder_get_widget (state
->gui
, "validation_in_dropdown"));
1886 state
->validation
.error
.action_label
= GTK_LABEL (go_gtk_builder_get_widget (state
->gui
, "validation_error_action_label"));
1887 state
->validation
.error
.title_label
= GTK_LABEL (go_gtk_builder_get_widget (state
->gui
, "validation_error_title_label"));
1888 state
->validation
.error
.msg_label
= GTK_LABEL (go_gtk_builder_get_widget (state
->gui
, "validation_error_msg_label"));
1889 state
->validation
.error
.action
= GTK_COMBO_BOX (go_gtk_builder_get_widget (state
->gui
, "validation_error_action"));
1890 build_validation_error_combo (state
, state
->validation
.error
.action
);
1891 gtk_combo_box_set_active (state
->validation
.error
.action
, 0);
1892 state
->validation
.error
.title
= GTK_ENTRY (go_gtk_builder_get_widget (state
->gui
, "validation_error_title"));
1893 state
->validation
.error
.msg
= GTK_TEXT_VIEW (go_gtk_builder_get_widget (state
->gui
, "validation_error_msg"));
1894 state
->validation
.error
.image
= GTK_IMAGE (go_gtk_builder_get_widget (state
->gui
, "validation_error_image"));
1896 gnm_editable_enters (
1897 GTK_WINDOW (state
->dialog
),
1898 GTK_WIDGET (state
->validation
.error
.title
));
1900 g_signal_connect (state
->validation
.constraint_type
,
1902 G_CALLBACK (cb_validation_sensitivity
), state
);
1903 g_signal_connect (state
->validation
.op
,
1905 G_CALLBACK (cb_validation_sensitivity
), state
);
1906 g_signal_connect (state
->validation
.error
.action
,
1908 G_CALLBACK (cb_validation_error_action_changed
), state
);
1910 fmt_dialog_init_validation_expr_entry (state
, &state
->validation
.expr0
, "validation_expr0_name", 0);
1911 fmt_dialog_init_validation_expr_entry (state
, &state
->validation
.expr1
, "validation_expr1_name", 1);
1913 g_signal_connect (G_OBJECT (state
->validation
.allow_blank
),
1915 G_CALLBACK (cb_validation_rebuild
), state
);
1916 g_signal_connect (G_OBJECT (state
->validation
.use_dropdown
),
1918 G_CALLBACK (cb_validation_rebuild
), state
);
1919 g_signal_connect (G_OBJECT (state
->validation
.error
.title
),
1921 G_CALLBACK (cb_validation_rebuild
), state
);
1922 g_signal_connect (G_OBJECT (gtk_text_view_get_buffer (state
->validation
.error
.msg
)),
1924 G_CALLBACK (cb_validation_rebuild
), state
);
1927 if (0 == (state
->conflicts
& (1 << MSTYLE_VALIDATION
)))
1928 v
= gnm_style_get_validation (state
->style
);
1932 gtk_combo_box_set_active (state
->validation
.error
.action
, v
->style
);
1933 gtk_combo_box_set_active (state
->validation
.constraint_type
, v
->type
);
1934 gtk_combo_box_set_active (state
->validation
.op
, v
->op
);
1936 gtk_entry_set_text (GTK_ENTRY (state
->validation
.error
.title
),
1937 (v
->title
!= NULL
) ? v
->title
->str
: "");
1939 gnm_textview_set_text (GTK_TEXT_VIEW (state
->validation
.error
.msg
),
1941 gtk_toggle_button_set_active (state
->validation
.allow_blank
, v
->allow_blank
);
1942 gtk_toggle_button_set_active (state
->validation
.use_dropdown
, v
->use_dropdown
);
1944 parse_pos_init (&pp
, state
->sheet
->workbook
, state
->sheet
,
1945 state
->sv
->edit_pos
.col
, state
->sv
->edit_pos
.row
);
1946 gnm_expr_entry_load_from_expr (state
->validation
.expr0
.entry
,
1947 v
->deps
[0].texpr
, &pp
);
1948 gnm_expr_entry_load_from_expr (state
->validation
.expr1
.entry
,
1949 v
->deps
[1].texpr
, &pp
);
1952 cb_validation_sensitivity (NULL
, state
);
1953 cb_validation_error_action_changed (NULL
, state
);
1956 /*****************************************************************************/
1959 input_msg_rebuild_input_msg (FormatState
*state
)
1962 char *msg
= gnm_textview_get_text (state
->input_msg
.msg
);
1963 char const *title
= gtk_entry_get_text (state
->input_msg
.title
);
1965 im
= gnm_input_msg_new (msg
, title
);
1967 gnm_style_set_input_msg (state
->result
, im
);
1968 fmt_dialog_changed (state
);
1972 cb_input_msg_rebuild (G_GNUC_UNUSED
void *ignored
,
1975 input_msg_rebuild_input_msg (state
);
1979 cb_input_msg_flag_toggled (GtkToggleButton
*button
, FormatState
*state
)
1981 gboolean flag
= gtk_toggle_button_get_active (button
);
1983 gtk_widget_set_sensitive (GTK_WIDGET (state
->input_msg
.title_label
), flag
);
1984 gtk_widget_set_sensitive (GTK_WIDGET (state
->input_msg
.msg_label
), flag
);
1985 gtk_widget_set_sensitive (GTK_WIDGET (state
->input_msg
.title
), flag
);
1986 gtk_widget_set_sensitive (GTK_WIDGET (state
->input_msg
.msg
), flag
);
1988 if (state
->enable_edit
) {
1990 input_msg_rebuild_input_msg (state
);
1992 gnm_style_set_input_msg (state
->result
, NULL
);
1993 fmt_dialog_changed (state
);
1998 fmt_dialog_init_input_msg_page (FormatState
*state
)
2000 GnmInputMsg
const *im
= NULL
;
2002 g_return_if_fail (state
!= NULL
);
2005 state
->input_msg
.flag
= GTK_TOGGLE_BUTTON (go_gtk_builder_get_widget (state
->gui
, "input_msg_flag"));
2006 state
->input_msg
.title_label
= GTK_LABEL (go_gtk_builder_get_widget (state
->gui
, "input_msg_title_label"));
2007 state
->input_msg
.msg_label
= GTK_LABEL (go_gtk_builder_get_widget (state
->gui
, "input_msg_msg_label"));
2008 state
->input_msg
.title
= GTK_ENTRY (go_gtk_builder_get_widget (state
->gui
, "input_msg_title"));
2009 state
->input_msg
.msg
= GTK_TEXT_VIEW (go_gtk_builder_get_widget (state
->gui
, "input_msg_msg"));
2012 if (0 == (state
->conflicts
& (1 << MSTYLE_INPUT_MSG
)))
2013 im
= gnm_style_get_input_msg (state
->style
);
2015 gtk_entry_set_text (state
->input_msg
.title
,
2016 gnm_input_msg_get_title (im
));
2017 gnm_textview_set_text (state
->input_msg
.msg
,
2018 gnm_input_msg_get_msg (im
));
2020 gtk_toggle_button_set_active (state
->input_msg
.flag
, im
!= NULL
);
2022 gnm_editable_enters (
2023 GTK_WINDOW (state
->dialog
),
2024 GTK_WIDGET (state
->input_msg
.title
));
2026 g_signal_connect (G_OBJECT (state
->input_msg
.flag
),
2028 G_CALLBACK (cb_input_msg_flag_toggled
), state
);
2029 g_signal_connect (G_OBJECT (state
->input_msg
.title
),
2031 G_CALLBACK (cb_input_msg_rebuild
), state
);
2032 g_signal_connect (G_OBJECT (gtk_text_view_get_buffer (state
->input_msg
.msg
)),
2034 G_CALLBACK (cb_input_msg_rebuild
), state
);
2037 cb_input_msg_flag_toggled (state
->input_msg
.flag
, state
);
2040 /*****************************************************************************/
2042 /* button handlers */
2044 cb_fmt_dialog_dialog_buttons (GtkWidget
*btn
, FormatState
*state
)
2047 static GnmStyleBorderLocation
const bmap_ltr
[] = {
2048 GNM_STYLE_BORDER_TOP
, GNM_STYLE_BORDER_BOTTOM
,
2049 GNM_STYLE_BORDER_LEFT
, GNM_STYLE_BORDER_RIGHT
,
2050 GNM_STYLE_BORDER_REV_DIAG
, GNM_STYLE_BORDER_DIAG
,
2051 GNM_STYLE_BORDER_HORIZ
, GNM_STYLE_BORDER_VERT
2053 static GnmStyleBorderLocation
const bmap_rtl
[] = {
2054 GNM_STYLE_BORDER_TOP
, GNM_STYLE_BORDER_BOTTOM
,
2056 GNM_STYLE_BORDER_RIGHT
, GNM_STYLE_BORDER_LEFT
,
2058 GNM_STYLE_BORDER_DIAG
, GNM_STYLE_BORDER_REV_DIAG
,
2059 GNM_STYLE_BORDER_HORIZ
, GNM_STYLE_BORDER_VERT
2061 GnmStyleBorderLocation
const *bmap
= bmap_ltr
;
2063 if (NULL
!= state
->sheet
&& state
->sheet
->text_is_rtl
)
2067 if (btn
== state
->apply_button
|| btn
== state
->ok_button
) {
2070 /* We need to make sure the right sheet is active */
2071 /* since we are acting on the current selection */
2072 /* validation may have switched sheets. */
2074 wb_control_sheet_focus (GNM_WBC (state
->wbcg
),
2077 if (state
->validation
.changed
)
2078 validation_rebuild_validation (state
);
2080 if (state
->validation
.valid
< 0) {
2081 if (go_gtk_query_yes_no (
2082 GTK_WINDOW (state
->dialog
),
2084 _ ("The validation criteria are unusable. Disable validation?")))
2086 gtk_combo_box_set_active (state
->validation
.constraint_type
, 0);
2087 cb_validation_sensitivity (NULL
, state
);
2089 gtk_notebook_set_current_page (state
->notebook
, FD_VALIDATION
);
2091 if (state
->validation
.valid
== -1)
2092 gnm_expr_entry_grab_focus (state
->validation
.expr0
.entry
, FALSE
);
2094 gnm_expr_entry_grab_focus (state
->validation
.expr1
.entry
, FALSE
);
2099 if (state
->protection
.sheet_protected_changed
) {
2100 state
->sheet
->is_protected
= state
->protection
.sheet_protected_value
;
2101 state
->protection
.sheet_protected_changed
= FALSE
;
2105 if (state
->style_selector
.is_selector
) {
2106 GnmStyle
*style
= gnm_style_dup (state
->style
);
2107 for (i
= GNM_STYLE_BORDER_TOP
; i
<= GNM_STYLE_BORDER_DIAG
; i
++) {
2108 GnmBorder
*b
= border_get_mstyle (state
, i
);
2110 gnm_style_set_border
2113 (int)(i
- GNM_STYLE_BORDER_TOP
),
2116 gnm_style_merge (style
, state
->result
);
2117 dialog_cell_format_style_added
2118 (state
->style_selector
.closure
,
2120 gnm_style_unref (state
->result
);
2122 GnmBorder
*borders
[GNM_STYLE_BORDER_EDGE_MAX
];
2123 for (i
= GNM_STYLE_BORDER_TOP
; i
< GNM_STYLE_BORDER_EDGE_MAX
; i
++)
2124 borders
[i
] = border_get_mstyle (state
, i
);
2125 cmd_selection_format (GNM_WBC (state
->wbcg
),
2126 state
->result
, borders
, NULL
);
2128 /* state->result got absorbed. */
2129 /* Get a fresh style to accumulate results in */
2130 state
->result
= gnm_style_new ();
2131 sheet_update (state
->sheet
);
2134 gtk_widget_set_sensitive (state
->apply_button
, FALSE
);
2137 if (btn
!= state
->apply_button
)
2138 gtk_widget_destroy (GTK_WIDGET (state
->dialog
));
2141 /* Handler for destroy */
2143 cb_fmt_dialog_dialog_destroy (FormatState
*state
)
2145 gnm_style_unref (state
->back
.style
);
2146 gnm_style_unref (state
->style
);
2147 gnm_style_unref (state
->result
);
2148 g_object_unref (state
->gui
);
2152 /* Handler for expr-entry's focus.
2154 * NOTE: This will only become useful once the
2155 * cell format dialog is made non-modal
2158 cb_fmt_dialog_set_focus (G_GNUC_UNUSED GtkWidget
*window
,
2159 G_GNUC_UNUSED GtkWidget
*focus_widget
,
2162 if (state
->validation
.changed
)
2163 validation_rebuild_validation (state
);
2167 cb_dialog_destroy (GtkDialog
*dialog
)
2169 g_object_set_data (G_OBJECT (dialog
), "state", NULL
);
2173 /* Set initial focus */
2175 set_initial_focus (FormatState
*s
)
2177 GtkWidget
*focus_widget
= NULL
, *pagew
;
2180 pagew
= gtk_notebook_get_nth_page (s
->notebook
, fmt_dialog_page
);
2181 name
= gtk_widget_get_name (pagew
);
2183 if (strcmp (name
, "number_box") == 0) {
2184 go_format_sel_set_focus (GO_FORMAT_SEL (s
->format_sel
));
2186 } else if (strcmp (name
, "alignment_box") == 0)
2187 focus_widget
= go_gtk_builder_get_widget (s
->gui
, "halign_left");
2188 else if (strcmp (name
, "font_box") == 0)
2189 focus_widget
= GTK_WIDGET (s
->font
.selector
);
2190 else if (strcmp (name
, "border_box") == 0)
2191 focus_widget
= go_gtk_builder_get_widget (s
->gui
, "gnumeric-format-border-outline");
2192 else if (strcmp (name
, "background_box") == 0)
2193 focus_widget
= go_gtk_builder_get_widget (s
->gui
, "back_color_auto");
2194 else if (strcmp (name
, "protection_box") == 0)
2195 focus_widget
= GTK_WIDGET (s
->protection
.locked
);
2197 focus_widget
= NULL
;
2200 gtk_widget_get_can_focus (focus_widget
) &&
2201 gtk_widget_is_sensitive (focus_widget
))
2202 gtk_widget_grab_focus (focus_widget
);
2206 fmt_dialog_impl (FormatState
*state
, FormatDialogPosition_t pageno
, gint pages
)
2209 char const *const name
;
2210 GnmStyleBorderType
const pattern
;
2211 } const line_pattern_buttons
[] = {
2212 { "line_pattern_none", GNM_STYLE_BORDER_NONE
},
2213 { "line_pattern_medium_dash_dot_dot", GNM_STYLE_BORDER_MEDIUM_DASH_DOT_DOT
},
2215 { "line_pattern_hair", GNM_STYLE_BORDER_HAIR
},
2216 { "line_pattern_slant", GNM_STYLE_BORDER_SLANTED_DASH_DOT
},
2218 { "line_pattern_dotted", GNM_STYLE_BORDER_DOTTED
},
2219 { "line_pattern_medium_dash_dot", GNM_STYLE_BORDER_MEDIUM_DASH_DOT
},
2221 { "line_pattern_dash_dot_dot", GNM_STYLE_BORDER_DASH_DOT_DOT
},
2222 { "line_pattern_medium_dash", GNM_STYLE_BORDER_MEDIUM_DASH
},
2224 { "line_pattern_dash_dot", GNM_STYLE_BORDER_DASH_DOT
},
2225 { "line_pattern_medium", GNM_STYLE_BORDER_MEDIUM
},
2227 { "line_pattern_dashed", GNM_STYLE_BORDER_DASHED
},
2228 { "line_pattern_thick", GNM_STYLE_BORDER_THICK
},
2230 { "line_pattern_thin", GNM_STYLE_BORDER_THIN
},
2231 { "line_pattern_double", GNM_STYLE_BORDER_DOUBLE
},
2235 static char const *const pattern_buttons
[] = {
2236 "gnumeric-pattern-solid", "gnumeric-pattern-75grey", "gnumeric-pattern-50grey",
2237 "gnumeric-pattern-25grey", "gnumeric-pattern-125grey", "gnumeric-pattern-625grey",
2239 "gnumeric-pattern-horiz",
2240 "gnumeric-pattern-vert",
2241 "gnumeric-pattern-diag",
2242 "gnumeric-pattern-rev-diag",
2243 "gnumeric-pattern-diag-cross",
2244 "gnumeric-pattern-thick-diag-cross",
2246 "gnumeric-pattern-thin-horiz",
2247 "gnumeric-pattern-thin-vert",
2248 "gnumeric-pattern-thin-rev-diag",
2249 "gnumeric-pattern-thin-diag",
2250 "gnumeric-pattern-thin-horiz-cross",
2251 "gnumeric-pattern-thin-diag-cross",
2253 "gnumeric-pattern-small-circle",
2254 "gnumeric-pattern-semi-circle",
2255 "gnumeric-pattern-thatch",
2256 "gnumeric-pattern-large-circles",
2257 "gnumeric-pattern-bricks",
2258 "gnumeric-pattern-foreground-solid",
2263 /* The order corresponds to the BorderLocation enum */
2264 static char const *const border_buttons
[] = {
2265 "gnumeric-format-border-top", "gnumeric-format-border-bottom",
2266 "gnumeric-format-border-left", "gnumeric-format-border-right",
2267 "gnumeric-format-border-rev-diag", "gnumeric-format-border-diag",
2268 "gnumeric-format-border-inside-horiz", "gnumeric-format-border-inside-vert",
2272 /* The order corresponds to BorderPresets */
2273 static char const *const border_preset_buttons
[] = {
2274 "gnumeric-format-border-no", "gnumeric-format-border-outline", "gnumeric-format-border-inside",
2282 GOColor default_border_color
;
2283 int default_border_style
= GNM_STYLE_BORDER_THIN
;
2284 GtkStyleContext
*ctxt
;
2287 GtkWidget
*tmp
, *dialog
= go_gtk_builder_get_widget (state
->gui
, "CellFormat");
2288 g_return_if_fail (dialog
!= NULL
);
2290 gtk_window_set_title (GTK_WINDOW (dialog
), _("Format Cells"));
2293 state
->dialog
= GTK_DIALOG (dialog
);
2294 state
->notebook
= GTK_NOTEBOOK (go_gtk_builder_get_widget (state
->gui
, "notebook"));
2296 state
->enable_edit
= FALSE
; /* Enable below */
2298 state
->border
.canvas
= NULL
;
2299 state
->border
.pattern
.cur_index
= 0;
2301 state
->back
.canvas
= NULL
;
2302 state
->back
.grid
= NULL
;
2303 state
->back
.style
= gnm_style_new_default ();
2304 state
->back
.pattern
.cur_index
= 0;
2306 fmt_dialog_init_format_page (state
);
2307 fmt_dialog_init_align_page (state
);
2308 fmt_dialog_init_font_page (state
);
2309 fmt_dialog_init_background_page (state
);
2310 fmt_dialog_init_protection_page (state
);
2311 fmt_dialog_init_validation_page (state
);
2312 fmt_dialog_init_input_msg_page (state
);
2314 ctxt
= gtk_widget_get_style_context (GTK_WIDGET (state
->dialog
));
2315 gtk_style_context_get_border_color (ctxt
, GTK_STATE_FLAG_NORMAL
, &bc
);
2316 default_border_color
= GO_COLOR_FROM_GDK_RGBA (bc
);
2318 if (pageno
== FD_CURRENT
)
2319 pageno
= fmt_dialog_page
;
2320 gtk_notebook_set_current_page (state
->notebook
, pageno
);
2322 page_signal
= g_signal_connect (G_OBJECT (state
->notebook
),
2324 G_CALLBACK (cb_page_select
), NULL
);
2325 g_signal_connect (G_OBJECT (state
->notebook
),
2327 G_CALLBACK (cb_notebook_destroy
), GINT_TO_POINTER (page_signal
));
2329 /* Setup border line pattern buttons & select the 1st button */
2330 for (i
= MSTYLE_BORDER_TOP
; i
< MSTYLE_BORDER_DIAGONAL
; i
++) {
2331 GnmBorder
const *border
= gnm_style_get_border (state
->style
, i
);
2332 if (!gnm_style_border_is_blank (border
)) {
2333 default_border_color
= border
->color
->go_color
;
2334 default_border_style
= border
->line_type
;
2339 state
->border
.pattern
.draw_preview
= NULL
;
2340 state
->border
.pattern
.current_pattern
= NULL
;
2341 state
->border
.pattern
.state
= state
;
2342 state
->border
.rgba
= default_border_color
;
2343 for (i
= 0; (name
= line_pattern_buttons
[i
].name
) != NULL
; ++i
)
2344 setup_pattern_button (gtk_widget_get_screen (GTK_WIDGET (state
->dialog
)),
2345 state
->gui
, name
, &state
->border
.pattern
,
2346 i
!= 0, /* No image for None */
2348 line_pattern_buttons
[i
].pattern
,
2349 default_border_style
, 54);
2351 setup_color_pickers (state
, &state
->border
.color
, "border_color_group",
2352 "border_color_placeholder", "border_color_label",
2353 _("Automatic"), _("Border"),
2354 G_CALLBACK (cb_border_color
), MSTYLE_BORDER_TOP
, FALSE
);
2355 setup_color_pickers (state
, &state
->back
.back_color
, "back_color_group",
2356 "background_color_placeholder", "back_color_label",
2357 _("Clear Background"), _("Background"),
2358 G_CALLBACK (cb_back_preview_color
), MSTYLE_COLOR_BACK
, FALSE
);
2359 setup_color_pickers (state
, &state
->back
.pattern_color
, "pattern_color_group",
2360 "pattern_color_placeholder", "pattern_color_label",
2361 _("Automatic"), _("Pattern"),
2362 G_CALLBACK (cb_pattern_preview_color
), MSTYLE_COLOR_PATTERN
, FALSE
);
2364 /* Setup the border images */
2365 for (i
= 0; (name
= border_buttons
[i
]) != NULL
; ++i
) {
2366 GtkWidget
*tmp
= go_gtk_builder_get_widget (state
->gui
, name
);
2368 init_border_button (state
, i
, tmp
,
2370 gnm_style_border_unref (state
->borders
[i
]);
2371 state
->borders
[i
] = NULL
;
2375 /* Get the current background
2376 * A pattern of 0 is has no background.
2377 * A pattern of 1 is a solid background
2378 * All others have 2 colours and a stipple
2382 if (0 == (state
->conflicts
& (1 << MSTYLE_PATTERN
))) {
2383 selected
= gnm_style_get_pattern (state
->style
);
2384 has_back
= (selected
!= 0);
2387 /* Setup pattern buttons & select the current pattern (or the 1st
2388 * if none is selected)
2389 * NOTE: This must be done AFTER the colour has been setup to
2390 * avoid having it erased by initialization.
2392 state
->back
.pattern
.draw_preview
= &draw_pattern_selected
;
2393 state
->back
.pattern
.current_pattern
= NULL
;
2394 state
->back
.pattern
.state
= state
;
2395 for (i
= 0; (name
= pattern_buttons
[i
]) != NULL
; ++i
)
2396 setup_pattern_button (gtk_widget_get_screen (GTK_WIDGET (state
->dialog
)),
2398 &state
->back
.pattern
, TRUE
, TRUE
,
2399 i
+1, /* Pattern #s start at 1 */
2402 /* If the pattern is 0 indicating no background colour
2403 * Set background to No colour. This will set states correctly.
2406 go_combo_color_set_color_to_default (GO_COMBO_COLOR (state
->back
.back_color
.combo
));
2408 /* Setup the images in the border presets */
2409 for (i
= 0; (name
= border_preset_buttons
[i
]) != NULL
; ++i
) {
2410 GtkWidget
*tmp
= go_gtk_builder_get_widget (state
->gui
, name
);
2412 state
->border
.preset
[i
] = GTK_BUTTON (tmp
);
2413 g_signal_connect (G_OBJECT (tmp
),
2415 G_CALLBACK (cb_border_preset_clicked
), state
);
2416 if (i
== BORDER_PRESET_INSIDE
&& state
->selection_mask
!= 0x8)
2417 gtk_widget_hide (tmp
);
2421 draw_border_preview (state
);
2423 gnm_init_help_button (
2424 go_gtk_builder_get_widget (state
->gui
, "helpbutton"),
2425 GNUMERIC_HELP_LINK_CELL_FORMAT
);
2427 state
->ok_button
= go_gtk_builder_get_widget (state
->gui
, "okbutton");
2428 gtk_widget_set_sensitive (state
->ok_button
, FALSE
);
2429 g_signal_connect (G_OBJECT (state
->ok_button
),
2431 G_CALLBACK (cb_fmt_dialog_dialog_buttons
), state
);
2432 state
->apply_button
= go_gtk_builder_get_widget (state
->gui
, "applybutton");
2433 gtk_widget_set_sensitive (state
->apply_button
, FALSE
);
2434 g_signal_connect (G_OBJECT (state
->apply_button
),
2436 G_CALLBACK (cb_fmt_dialog_dialog_buttons
), state
);
2437 tmp
= go_gtk_builder_get_widget (state
->gui
, "cancelbutton");
2438 g_signal_connect (G_OBJECT (tmp
),
2440 G_CALLBACK (cb_fmt_dialog_dialog_buttons
), state
);
2442 set_initial_focus (state
);
2443 gtk_notebook_set_scrollable (state
->notebook
, TRUE
);
2445 gnm_dialog_setup_destroy_handlers (GTK_DIALOG (dialog
), state
->wbcg
,
2446 GNM_DIALOG_DESTROY_CURRENT_SHEET_REMOVED
);
2448 /* Ok, edit events from now on are real */
2449 state
->enable_edit
= TRUE
;
2451 g_signal_connect (G_OBJECT (dialog
),
2453 G_CALLBACK (cb_fmt_dialog_set_focus
), state
);
2454 /* We could now make it modeless, and arguably should do so. We must
2455 * then track the selection: styles should be applied to the current
2457 * There are some UI issues to discuss before we do this, though. Most
2459 * - will users be confused?
2460 * And on a different level:
2461 * - should the preselected style in the dialog change when another
2462 * cell is selected? May be, but then we can't first make a style,
2463 * then move around and apply it to different cells.
2466 g_object_set_data_full (G_OBJECT (state
->dialog
),
2467 "state", state
, (GDestroyNotify
)cb_fmt_dialog_dialog_destroy
);
2468 g_signal_connect (G_OBJECT (dialog
), "destroy",
2469 G_CALLBACK (cb_dialog_destroy
), NULL
);
2472 for (i
= 0; i
<= FD_LAST
; i
++) {
2473 GtkWidget
*widget
= gtk_notebook_get_nth_page
2474 (state
->notebook
, i
);
2475 if (widget
!= NULL
&& !((1<<i
) & pages
))
2476 gtk_widget_hide (widget
);
2480 gnm_restore_window_geometry (GTK_WINDOW (state
->dialog
),
2485 cb_check_cell_format (GnmCellIter
const *iter
, gpointer user
)
2487 FormatState
*state
= user
;
2488 GnmValue
const *value
= iter
->cell
->value
;
2489 GOFormat
const *common
= gnm_style_get_format (state
->style
);
2490 GOFormat
const *fmt
= value
? VALUE_FMT (value
) : NULL
;
2493 go_format_is_markup (fmt
) ||
2494 go_format_eq (common
, fmt
))
2497 if (go_format_is_general (common
)) {
2498 gnm_style_set_format (state
->style
, fmt
);
2501 state
->conflicts
|= MSTYLE_FORMAT
;
2502 return VALUE_TERMINATE
;
2507 fmt_dialog_selection_type (SheetView
*sv
,
2508 GnmRange
const *range
,
2511 FormatState
*state
= user_data
;
2512 GSList
*merged
= gnm_sheet_merge_get_overlap (sv
->sheet
, range
);
2513 GnmRange r
= *range
;
2514 gboolean allow_multi
=
2516 merged
->next
!= NULL
||
2517 !range_equal ((GnmRange
*)merged
->data
, range
);
2518 g_slist_free (merged
);
2520 /* allow_multi == FALSE && !is_singleton (range) means that we are in
2521 * an merge cell, use only the top left */
2522 if (r
.start
.col
!= r
.end
.col
)
2525 state
->selection_mask
|= 2;
2527 r
.end
.col
= r
.start
.col
;
2529 if (range
->start
.row
!= range
->end
.row
)
2532 state
->selection_mask
|= 1;
2534 r
.end
.row
= r
.start
.row
;
2537 state
->conflicts
= sheet_style_find_conflicts (state
->sheet
, &r
,
2538 &(state
->style
), state
->borders
);
2540 if ((state
->conflicts
& MSTYLE_FORMAT
) == 0 &&
2541 go_format_is_general (gnm_style_get_format (state
->style
))) {
2542 sheet_foreach_cell_in_range (state
->sheet
,
2543 CELL_ITER_IGNORE_BLANK
,
2545 cb_check_cell_format
,
2552 static FormatState
*
2553 dialog_cell_format_init (WBCGtk
*wbcg
)
2559 gui
= gnm_gtk_builder_load ("res:ui/cell-format.ui", NULL
, GO_CMD_CONTEXT (wbcg
));
2564 state
= g_new (FormatState
, 1);
2567 state
->sv
= wb_control_cur_sheet_view (GNM_WBC (wbcg
));
2568 state
->sheet
= sv_sheet (state
->sv
);
2570 edit_cell
= sheet_cell_get (state
->sheet
,
2571 state
->sv
->edit_pos
.col
,
2572 state
->sv
->edit_pos
.row
);
2574 state
->value
= (edit_cell
!= NULL
) ? edit_cell
->value
: NULL
;
2575 state
->style
= NULL
;
2576 state
->result
= gnm_style_new ();
2577 state
->selection_mask
= 0;
2579 (void) sv_selection_foreach (state
->sv
,
2580 fmt_dialog_selection_type
, state
);
2581 state
->selection_mask
= 1 << state
->selection_mask
;
2587 dialog_cell_format (WBCGtk
*wbcg
, FormatDialogPosition_t pageno
, gint pages
)
2591 g_return_if_fail (wbcg
!= NULL
);
2593 state
= dialog_cell_format_init (wbcg
);
2598 state
->style_selector
.is_selector
= FALSE
;
2599 state
->style_selector
.w
= NULL
;
2600 state
->style_selector
.closure
= NULL
;
2604 for (i
= FD_NUMBER
; i
<= FD_PROTECTION
; i
++)
2608 fmt_dialog_impl (state
, pageno
, pages
);
2610 wbc_gtk_attach_guru (state
->wbcg
, GTK_WIDGET (state
->dialog
));
2611 go_gtk_nonmodal_dialog (wbcg_toplevel (state
->wbcg
),
2612 GTK_WINDOW (state
->dialog
));
2613 gtk_widget_show (GTK_WIDGET (state
->dialog
));
2620 * - Add the 'text' elements in the preview
2623 * - Some undo capabilities in the dialog.
2624 * - How to distinguish between auto & custom colors on extraction from styles.
2628 * dialog_cell_format_select_style:
2630 * Returns: (transfer floating): a #GtkDialog.
2633 dialog_cell_format_select_style (WBCGtk
*wbcg
, gint pages
,
2635 GnmStyle
*style
, gpointer closure
)
2639 g_return_val_if_fail (wbcg
!= NULL
, NULL
);
2640 state
= dialog_cell_format_init (wbcg
);
2641 g_return_val_if_fail (state
!= NULL
, NULL
);
2643 state
->style_selector
.is_selector
= TRUE
;
2644 state
->style_selector
.w
= w
;
2645 state
->style_selector
.closure
= closure
;
2646 state
->selection_mask
= 1;
2649 gnm_style_unref (state
->style
);
2650 state
->style
= style
;
2651 state
->conflicts
= 0;
2654 fmt_dialog_impl (state
, FD_BACKGROUND
, pages
);
2656 gtk_widget_hide (state
->apply_button
);
2658 go_gtk_nonmodal_dialog (w
, GTK_WINDOW (state
->dialog
));
2659 gtk_widget_show (GTK_WIDGET (state
->dialog
));
2661 return state
->dialog
;