Compilation: don't compile dialogs separately.
[gnumeric.git] / src / dialogs / dialog-cell-format.c
blob2d5f61844d819a19c52f166c55f130e9fa015961
1 /*
2 * dialog-cell-format.c: Implements a dialog to format cells.
4 * Authors:
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/>.
21 **/
23 #include <gnumeric-config.h>
24 #include <glib/gi18n-lib.h>
25 #include <gnumeric.h>
26 #include "dialogs.h"
27 #include "help.h"
29 #include <sheet.h>
30 #include <sheet-view.h>
31 #include <sheet-merge.h>
32 #include <sheet-style.h>
33 #include <style-color.h>
34 #include <gui-util.h>
35 #include <selection.h>
36 #include <ranges.h>
37 #include <cell.h>
38 #include <expr.h>
39 #include <value.h>
40 #include <gnm-format.h>
41 #include <pattern.h>
42 #include <position.h>
43 #include <mstyle.h>
44 #include <application.h>
45 #include <validation.h>
46 #include <input-msg.h>
47 #include <workbook.h>
48 #include <wbc-gtk.h>
49 #include <commands.h>
50 #include <mathfunc.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>
60 #include <gtk/gtk.h>
62 #include <string.h>
64 #define CELL_FORMAT_KEY "cell-format-dialog"
66 static struct {
67 char const *Cname;
68 GnmUnderline ut;
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 */
81 typedef enum
83 BORDER_PRESET_NONE,
84 BORDER_PRESET_OUTLINE,
85 BORDER_PRESET_INSIDE,
87 BORDER_PRESET_MAX
88 } BorderPresets;
90 struct _FormatState;
92 typedef struct {
93 struct _FormatState *state;
94 int cur_index;
95 GtkToggleButton *current_pattern;
96 GtkToggleButton *default_button;
97 void (*draw_preview) (struct _FormatState *);
98 } PatternPicker;
100 typedef struct {
101 struct _FormatState *state;
103 GtkWidget *combo;
104 GCallback preview_update;
105 } ColorPicker;
107 typedef struct {
108 struct _FormatState *state;
109 GtkToggleButton *button;
110 GnmStyleBorderType pattern_index;
111 gboolean is_selected; /* Is it selected */
112 GnmStyleBorderLocation index;
113 guint rgba;
114 gboolean is_auto_color;
115 gboolean is_set; /* Has the element been changed */
116 } BorderPicker;
118 typedef struct {
119 GtkLabel *name;
120 GnmExprEntry *entry;
121 } ExprEntry;
123 typedef struct _FormatState {
124 GtkBuilder *gui;
125 WBCGtk *wbcg;
126 GtkDialog *dialog;
127 GtkNotebook *notebook;
128 GtkWidget *apply_button;
129 GtkWidget *ok_button;
131 Sheet *sheet;
132 SheetView *sv;
133 GnmValue *value;
134 unsigned int conflicts;
135 GnmStyle *style, *result;
136 GnmBorder *borders[GNM_STYLE_BORDER_EDGE_MAX];
138 int selection_mask;
139 gboolean enable_edit;
141 GtkWidget * format_sel;
143 struct {
144 GtkCheckButton *wrap;
145 GtkSpinButton *indent_button;
146 GtkWidget *indent_label;
147 int indent;
148 GORotationSel *rotation;
149 } align;
150 struct {
151 GOFontSel *selector;
152 GtkWidget *underline_picker;
153 GnmUnderline underline;
154 } font;
155 struct {
156 GocCanvas *canvas;
157 GtkButton *preset[BORDER_PRESET_MAX];
158 GocItem *back;
159 GocItem *lines[20];
161 BorderPicker edge[GNM_STYLE_BORDER_EDGE_MAX];
162 ColorPicker color;
163 guint rgba;
164 gboolean is_auto_color;
165 PatternPicker pattern;
166 } border;
167 struct {
168 GocCanvas *canvas;
169 GnmPreviewGrid *grid;
170 GnmStyle *style;
172 ColorPicker back_color;
173 ColorPicker pattern_color;
174 PatternPicker pattern;
175 } back;
176 struct {
177 GtkCheckButton *hidden, *locked, *sheet_protected;
179 gboolean sheet_protected_changed;
180 gboolean sheet_protected_value;
181 } protection;
182 struct {
183 GtkGrid *criteria_grid;
184 GtkComboBox *constraint_type;
185 GtkLabel *operator_label;
186 GtkComboBox *op;
187 ExprEntry expr0, expr1;
188 GtkToggleButton *allow_blank;
189 GtkToggleButton *use_dropdown;
191 struct {
192 GtkLabel *action_label;
193 GtkLabel *title_label;
194 GtkLabel *msg_label;
195 GtkComboBox *action;
196 GtkEntry *title;
197 GtkTextView *msg;
198 GtkImage *image;
199 } error;
200 gboolean changed;
201 int valid;
202 } validation;
203 struct {
204 GtkToggleButton *flag;
206 GtkLabel *title_label;
207 GtkLabel *msg_label;
208 GtkEntry *title;
209 GtkTextView *msg;
210 } input_msg;
211 struct {
212 gboolean is_selector;
213 GtkWindow *w;
214 gpointer closure;
215 } style_selector;
216 } FormatState;
218 enum {
219 CONDITIONS_RANGE,
220 CONDITIONS_COND,
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.
233 static void
234 fmt_dialog_changed (FormatState *state)
236 GOFormatSel *gfs;
237 GOFormat const *fmt;
238 gboolean ok;
240 if (!state->enable_edit)
241 return;
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
252 * invocations */
253 static FormatDialogPosition_t fmt_dialog_page = FD_NUMBER;
255 #if 0
256 /* The last currency selected */
257 static int fmt_dialog_currency = 0;
258 #endif
261 * Callback routine to help remember which format tab was selected
262 * between dialog invocations.
264 static void
265 cb_page_select (G_GNUC_UNUSED GtkNotebook *notebook,
266 G_GNUC_UNUSED GtkWidget *page,
267 gint page_num,
268 G_GNUC_UNUSED gpointer user_data)
270 fmt_dialog_page = page_num;
273 static void
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.
283 static void
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.
300 static void
301 setup_pattern_button (GdkScreen *screen,
302 GtkBuilder *gui,
303 char const *const name,
304 PatternPicker *picker,
305 gboolean do_image,
306 gboolean from_icon,
307 int const index,
308 int const select_index,
309 unsigned size)
311 GtkWidget *tmp = go_gtk_builder_get_widget (gui, name);
312 if (tmp != NULL) {
313 GtkButton *button = GTK_BUTTON (tmp);
314 if (do_image) {
315 char *res = g_strconcat ("/org/gnumeric/gnumeric/images/", name, ".png", NULL);
316 GtkWidget *image;
317 if (from_icon)
318 image = gtk_image_new_from_icon_name (name, GTK_ICON_SIZE_DIALOG);
319 else {
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);
327 g_free (res);
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),
340 "toggled",
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),
349 TRUE);
351 } else
352 g_warning ("CellFormat: Unexpected missing widget");
355 static void
356 setup_color_pickers (FormatState *state,
357 ColorPicker *picker,
358 char const *color_group,
359 char const *placeholder,
360 char const *label,
361 char const *default_caption,
362 char const *caption,
363 GCallback preview_update,
364 GnmStyleElement e,
365 gboolean allow_alpha)
367 GtkWidget *combo, *w, *frame;
368 GOColorGroup *cg;
369 GnmColor *mcolor = NULL;
370 GnmColor *def_sc = NULL;
372 switch (e) {
373 case MSTYLE_COLOR_PATTERN:
374 if (0 == (state->conflicts & (1 << MSTYLE_COLOR_PATTERN)))
375 mcolor = gnm_style_get_pattern_color (state->style);
377 /* fallthrough */
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);
381 break;
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 ();
386 break;
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 ();
391 break;
392 default:
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);
398 g_object_unref (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),
403 "color_changed",
404 G_CALLBACK (preview_update), state);
406 if (mcolor && !mcolor->is_auto)
407 go_combo_color_set_color (GO_COMBO_COLOR (combo),
408 mcolor->go_color);
409 else
410 go_combo_color_set_color_to_default (GO_COMBO_COLOR (combo));
412 if (allow_alpha)
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 /*****************************************************************************/
435 static void
436 cb_number_format_changed (G_GNUC_UNUSED GtkWidget *widget,
437 const char *fmt,
438 FormatState *state)
440 gboolean changed = FALSE;
441 g_return_if_fail (state != NULL);
443 if (!state->enable_edit)
444 return;
446 if (fmt) {
447 GOFormat *format = go_format_new_from_XL (fmt);
448 gnm_style_set_format (state->result, format);
449 go_format_unref (format);
450 changed = TRUE;
453 if (changed)
454 fmt_dialog_changed (state);
457 static void
458 fmt_dialog_init_format_page (FormatState *state)
460 GOFormatSel *gfs;
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),
467 state->format_sel,
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 /*****************************************************************************/
484 static void
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);
499 static void
500 cb_align_h_toggle (GtkToggleButton *button, FormatState *state)
502 if (!gtk_toggle_button_get_active (button))
503 return;
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),
513 supports_indent);
514 gtk_widget_set_sensitive (GTK_WIDGET (state->align.indent_label),
515 supports_indent);
516 /* TODO: Should we 0 the indent ? */
517 fmt_dialog_changed (state);
521 static void
522 cb_align_v_toggle (GtkToggleButton *button, FormatState *state)
524 if (!gtk_toggle_button_get_active (button))
525 return;
527 if (state->enable_edit) {
528 gnm_style_set_align_v (
529 state->result,
530 GPOINTER_TO_INT (g_object_get_data (
531 G_OBJECT (button), "align")));
532 fmt_dialog_changed (state);
536 static void
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);
546 static void
547 fmt_dialog_init_align_radio (char const *const name,
548 int const val, int const target,
549 FormatState *state,
550 GCallback handler)
552 GtkWidget *tmp = go_gtk_builder_get_widget (state->gui, name);
553 if (tmp != NULL) {
554 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tmp),
555 val == target);
556 g_object_set_data (G_OBJECT (tmp), "align",
557 GINT_TO_POINTER (val));
558 g_signal_connect (G_OBJECT (tmp),
559 "toggled",
560 handler, state);
564 static void
565 cb_rotation_changed (G_GNUC_UNUSED GORotationSel *grs, int angle, FormatState *state)
567 if (angle < 0)
568 angle += 360;
569 gnm_style_set_rotation (state->result, angle);
570 fmt_dialog_changed (state);
573 static void
574 fmt_dialog_init_align_page (FormatState *state)
576 static struct {
577 char const *const name;
578 GnmHAlign align;
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 },
588 { NULL, 0}
590 static struct {
591 char const *const name;
592 GnmVAlign align;
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 },
599 { NULL, 0}
602 GtkWidget *w;
603 gboolean wrap = FALSE;
604 GnmHAlign h = GNM_HALIGN_GENERAL;
605 GnmVAlign v = GNM_VALIGN_CENTER;
606 char const *name;
607 int i, r;
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,
617 h, state,
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,
623 v, state,
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),
634 "toggled",
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;
640 else
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),
655 "value-changed",
656 G_CALLBACK (cb_indent_changed), state);
658 /* Catch <return> in the spin box */
659 gnm_editable_enters (
660 GTK_WINDOW (state->dialog),
661 GTK_WIDGET (w));
663 /* setup the rotation canvas */
664 if (0 == (state->conflicts & (1 << MSTYLE_ROTATION))) {
665 r = gnm_style_get_rotation (state->style);
666 if (r > 180)
667 r -= 360;
668 } else
669 r = 0;
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 /*****************************************************************************/
680 static void
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;
689 GnmColor *c;
691 gboolean changed = FALSE;
692 g_return_if_fail (state != NULL);
694 if (!state->enable_edit)
695 return;
697 aiter = pango_attr_list_get_iterator (attrs);
699 attr = pango_attr_iterator_get (aiter, PANGO_ATTR_FAMILY);
700 if (attr) {
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))) {
704 changed = TRUE;
705 gnm_style_set_font_name (res, s);
709 attr = pango_attr_iterator_get (aiter, PANGO_ATTR_SIZE);
710 if (attr) {
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)) {
715 changed = TRUE;
716 gnm_style_set_font_size (res, d);
720 attr = pango_attr_iterator_get (aiter, PANGO_ATTR_WEIGHT);
721 if (attr) {
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)) {
726 changed = TRUE;
727 gnm_style_set_font_bold (res, b);
731 attr = pango_attr_iterator_get (aiter, PANGO_ATTR_STYLE);
732 if (attr) {
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)) {
737 changed = TRUE;
738 gnm_style_set_font_italic (res, b);
742 attr = pango_attr_iterator_get (aiter, PANGO_ATTR_UNDERLINE);
743 if (attr) {
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)) {
748 changed = TRUE;
749 gnm_style_set_font_uline (res, u);
753 attr = pango_attr_iterator_get (aiter, PANGO_ATTR_STRIKETHROUGH);
754 if (attr) {
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)) {
759 changed = TRUE;
760 gnm_style_set_font_strike (res, b);
764 attr = pango_attr_iterator_get (aiter, go_pango_attr_subscript_get_attr_type ());
765 if (attr) {
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 ());
771 if (attr) {
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))) {
779 changed = TRUE;
780 gnm_style_set_font_script (res, script);
783 attr = pango_attr_iterator_get (aiter, PANGO_ATTR_FOREGROUND);
784 c = attr
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))) {
789 changed = TRUE;
790 gnm_style_set_font_color (res, c);
791 } else
792 style_color_unref (c);
794 pango_attr_iterator_destroy (aiter);
796 if (changed)
797 fmt_dialog_changed (state);
800 static void
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);
814 static void
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));
820 GList *children, *l;
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"));
832 if (u == uline)
833 go_option_menu_select_item (om, item);
835 g_list_free (children);
838 static void
839 cb_underline_changed (GOOptionMenu *om, FormatState *state)
841 GtkWidget *selected = go_option_menu_get_history (om);
842 GnmUnderline u;
844 if (!selected)
845 return;
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 */
853 static void
854 fmt_dialog_init_font_page (FormatState *state)
856 GOColorGroup *cg;
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;
862 GnmColor *def_sc;
863 GtkWidget *up;
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,
871 NULL);
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,
877 "show-style", TRUE,
878 "show-color", TRUE,
879 "color-unset-text", _("Automatic"),
880 "color-group", cg,
881 "color-default", def_sc->go_color,
882 "show-underline", TRUE,
883 "underline-picker", up,
884 "show-script", TRUE,
885 "show-strikethrough", TRUE,
886 "vexpand", TRUE,
887 "hexpand", TRUE,
888 NULL);
889 g_object_unref (cg);
890 style_color_unref (def_sc);
891 state->font.selector = GO_FONT_SEL (font_widget);
892 g_object_unref (up);
894 gtk_widget_show (font_widget);
895 gtk_container_add (GTK_CONTAINER (go_gtk_builder_get_widget (state->gui, "font_sel_placeholder")),
896 font_widget);
898 go_font_sel_editable_enters (state->font.selector,
899 GTK_WINDOW (state->dialog));
901 if (state->value) {
902 char *s = format_value (NULL, state->value, -1, date_conv);
903 go_font_sel_set_sample_text (state->font.selector, s);
904 g_free (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,
926 pts * PANGO_SCALE);
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)))
950 change_font_attr
951 (state,
952 go_color_to_pango (gnm_style_get_font_color (state->style)->go_color,
953 TRUE));
955 g_signal_connect (G_OBJECT (state->font.selector),
956 "font_changed",
957 G_CALLBACK (cb_font_changed), state);
960 /*****************************************************************************/
962 static void
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,
975 NULL);
979 static void
980 cb_back_preview_color (G_GNUC_UNUSED GOComboColor *combo,
981 GOColor c,
982 G_GNUC_UNUSED gboolean is_custom,
983 G_GNUC_UNUSED gboolean by_user,
984 gboolean is_default,
985 FormatState *state)
987 GnmColor *sc;
989 g_return_if_fail (c);
991 if (is_default) {
992 sc = style_color_auto_back ();
993 gnm_style_set_pattern (state->back.style, 0);
994 } else {
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);
1003 static void
1004 cb_pattern_preview_color (G_GNUC_UNUSED GOComboColor *combo,
1005 GOColor c,
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);
1019 static void
1020 draw_pattern_selected (FormatState *state)
1022 gnm_style_set_pattern (state->back.style, state->back.pattern.cur_index);
1023 back_style_changed (state);
1026 static void
1027 fmt_dialog_init_background_page (FormatState *state)
1029 GtkWidget *widget;
1030 int w = 120;
1031 int h = 60;
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,
1049 NULL));
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 */
1070 static struct
1072 double const points[4];
1073 int const states;
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
1083 /* 1, 2, 3, 4 */
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 }
1120 static GnmBorder *
1121 border_get_mstyle (FormatState const *state, GnmStyleBorderLocation const loc)
1123 BorderPicker const *edge = & state->border.edge[loc];
1124 GnmColor *color;
1125 /* Don't set borders that have not been changed */
1126 if (!edge->is_set)
1127 return NULL;
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);
1134 } else {
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.
1149 static gboolean
1150 border_format_has_changed (FormatState *state, BorderPicker *edge)
1152 int i;
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;
1159 changed = TRUE;
1161 } else if (edge->rgba != state->border.rgba)
1162 changed = TRUE;
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);
1184 changed = TRUE;
1187 return changed;
1191 * Map canvas x.y coords to a border type
1192 * Handle all of the various permutations of lines
1194 static gboolean
1195 border_event (GtkWidget *widget, GdkEventButton *event, FormatState *state)
1197 double x = event->x;
1198 double y = event->y;
1199 BorderPicker *edge;
1200 GnmStyleBorderLocation which;
1202 if (event->button != 1)
1203 return FALSE;
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);
1212 event->type = type;
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) {
1221 case 1:
1222 if ((x < V) == (y < H))
1223 which = GNM_STYLE_BORDER_REV_DIAG;
1224 else
1225 which = GNM_STYLE_BORDER_DIAG;
1226 break;
1227 case 2:
1228 if (H-5. < y && y < H+5.)
1229 which = GNM_STYLE_BORDER_HORIZ;
1230 else {
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;
1236 else
1237 which = GNM_STYLE_BORDER_DIAG;
1239 break;
1240 case 4:
1241 if (V-5. < x && x < V+5.)
1242 which = GNM_STYLE_BORDER_VERT;
1243 else {
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;
1249 else
1250 which = GNM_STYLE_BORDER_DIAG;
1252 break;
1253 case 8:
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;
1258 else {
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;
1265 else
1266 which = GNM_STYLE_BORDER_DIAG;
1268 break;
1270 default:
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);
1279 else
1280 fmt_dialog_changed (state);
1282 return TRUE;
1285 static void
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 }
1306 int i, j, k;
1308 /* The first time through lets initialize */
1309 if (state->border.canvas == NULL) {
1310 GocGroup *group;
1311 GocPoints *points;
1312 GOStyle *style;
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),
1317 150, 100);
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,
1327 GOC_TYPE_RECTANGLE,
1328 "x", L-10., "y", T-10.,
1329 "width", R-L+20., "height", B-T+20.,
1330 NULL);
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) {
1338 if (i >= 8) {
1339 if (!(state->selection_mask & 0xa))
1340 continue;
1341 } else if (i >= 4) {
1342 if (!(state->selection_mask & 0xc))
1343 continue;
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 (),
1355 "points", points,
1356 NULL)));
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],
1373 NULL);
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]),
1378 p->pattern_index);
1379 } else
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];
1386 int j;
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);
1399 static void
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;
1417 } else {
1418 g_warning ("Unknown border preset button");
1419 return;
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,
1427 FALSE);
1429 if (target_state)
1430 gtk_toggle_button_set_active (
1431 state->border.edge[i].button,
1432 TRUE);
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
1437 * the new colours.
1439 gtk_toggle_button_set_active (
1440 state->border.edge[i].button,
1441 FALSE);
1446 * Callback routine to update the border preview when a button is clicked
1448 static void
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);
1459 else
1460 /* Update the preview lines and enable/disable them */
1461 draw_border_preview (picker->state);
1464 static void
1465 cb_border_color (G_GNUC_UNUSED GOComboColor *combo,
1466 GOColor c,
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;
1475 #undef L
1476 #undef R
1477 #undef T
1478 #undef B
1479 #undef H
1480 #undef V
1482 typedef struct
1484 GnmStyleBorderLocation t;
1485 GnmBorder const *res;
1486 } check_border_closure_t;
1489 * Initialize the fields of a BorderPicker, connect signals and
1490 * hide if needed.
1492 static void
1493 init_border_button (FormatState *state, GnmStyleBorderLocation const i,
1494 GtkWidget *button,
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;
1502 } else {
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),
1521 "toggled",
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 /*****************************************************************************/
1531 static void
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);
1541 static void
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);
1551 static void
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);
1562 static void
1563 fmt_dialog_init_protection_page (FormatState *state)
1565 GtkWidget *w;
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),
1574 "toggled",
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),
1583 "toggled",
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),
1592 "toggled",
1593 G_CALLBACK (cb_protection_sheet_protected_toggle), state);
1596 /*****************************************************************************/
1598 static GnmExprTop const *
1599 validation_entry_to_expr (Sheet *sheet, GnmExprEntry *gee)
1601 GnmParsePos pp;
1602 parse_pos_init_sheet (&pp, sheet);
1603 return gnm_expr_entry_parse (gee, &pp, NULL, FALSE, GNM_EXPR_PARSE_DEFAULT);
1606 static void
1607 validation_rebuild_validation (FormatState *state)
1609 ValidationType type;
1611 if (!state->enable_edit)
1612 return;
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);
1635 if (texpr1 != NULL)
1636 state->validation.valid = 2;
1637 else {
1638 state->validation.valid = -2;
1639 gnm_expr_top_unref (texpr0);
1641 } else
1642 state->validation.valid = 1;
1643 } else
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
1650 (state->result,
1651 gnm_validation_new
1652 (style, type, op,
1653 state->sheet,
1654 title, msg,
1655 texpr0,
1656 texpr1,
1657 allow_blank,
1658 use_dropdown));
1661 g_free (msg);
1662 g_free (title);
1663 } else
1664 gnm_style_set_validation (state->result, NULL);
1665 fmt_dialog_changed (state);
1668 static struct {
1669 const char *text;
1670 const char *icon_name;
1671 } validation_error_actions[] = {
1673 N_("None (silently accept invalid input)"),
1674 NULL
1677 N_("Stop (never allow invalid input)"),
1678 "dialog-error"
1681 N_("Warning (accept/discard invalid input)"),
1682 "dialog-warning"
1685 N_("Information (allow invalid input)"),
1686 "dialog-information"
1690 static void
1691 cb_validation_error_action_changed (G_GNUC_UNUSED GtkMenuShell *ignored,
1692 FormatState *state)
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);
1703 if (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));
1708 } else
1709 gtk_widget_hide (GTK_WIDGET (state->validation.error.image));
1711 validation_rebuild_validation (state);
1714 static void
1715 cb_validation_sensitivity (G_GNUC_UNUSED GtkMenuShell *ignored,
1716 FormatState *state)
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);
1724 switch (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;
1736 switch (op) {
1737 case GNM_VALIDATION_OP_NONE:
1738 break;
1739 case GNM_VALIDATION_OP_BETWEEN:
1740 case GNM_VALIDATION_OP_NOT_BETWEEN:
1741 msg0 = _("Min:");
1742 msg1 = _("Max:");
1743 break;
1744 case GNM_VALIDATION_OP_EQUAL:
1745 case GNM_VALIDATION_OP_NOT_EQUAL:
1746 msg0 = _("Value:");
1747 break;
1748 case GNM_VALIDATION_OP_GT:
1749 case GNM_VALIDATION_OP_GTE:
1750 msg0 =_("Min:");
1751 break;
1752 case GNM_VALIDATION_OP_LT:
1753 case GNM_VALIDATION_OP_LTE:
1754 msg0 = _("Max:");
1755 break;
1756 default:
1757 g_warning ("Unknown operator index %d", (int)op);
1759 break;
1761 default:
1762 break;
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),
1774 has_operators);
1775 gtk_widget_set_sensitive (GTK_WIDGET (state->validation.operator_label),
1776 has_operators);
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);
1790 static void
1791 cb_validation_changed (G_GNUC_UNUSED GtkEntry *ignored,
1792 FormatState *state)
1794 if (state->enable_edit)
1795 state->validation.changed = TRUE;
1798 static void
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),
1812 "changed",
1813 G_CALLBACK (cb_validation_changed), state);
1816 static void
1817 cb_validation_rebuild (G_GNUC_UNUSED void *ignored,
1818 FormatState *state)
1820 validation_rebuild_validation (state);
1823 static void
1824 build_validation_error_combo (FormatState *state, GtkComboBox *box)
1826 GtkListStore *store;
1827 GtkCellRenderer *renderer;
1828 unsigned ui;
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)
1838 : NULL;
1839 GtkTreeIter iter;
1841 gtk_list_store_append (store, &iter);
1842 gtk_list_store_set (store, &iter,
1843 0, pixbuf,
1844 1, _(validation_error_actions[ui].text),
1845 -1);
1846 if (pixbuf)
1847 g_object_unref (pixbuf);
1850 renderer = gtk_cell_renderer_pixbuf_new ();
1851 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (box),
1852 renderer,
1853 FALSE);
1854 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (box), renderer,
1855 "pixbuf", 0,
1856 NULL);
1858 renderer = gtk_cell_renderer_text_new ();
1859 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (box),
1860 renderer,
1861 TRUE);
1862 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (box), renderer,
1863 "text", 1,
1864 NULL);
1866 g_object_unref (store);
1869 static void
1870 fmt_dialog_init_validation_page (FormatState *state)
1872 GnmValidation const *v = NULL;
1873 g_return_if_fail (state != NULL);
1875 /* Setup widgets */
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,
1901 "changed",
1902 G_CALLBACK (cb_validation_sensitivity), state);
1903 g_signal_connect (state->validation.op,
1904 "changed",
1905 G_CALLBACK (cb_validation_sensitivity), state);
1906 g_signal_connect (state->validation.error.action,
1907 "changed",
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),
1914 "toggled",
1915 G_CALLBACK (cb_validation_rebuild), state);
1916 g_signal_connect (G_OBJECT (state->validation.use_dropdown),
1917 "toggled",
1918 G_CALLBACK (cb_validation_rebuild), state);
1919 g_signal_connect (G_OBJECT (state->validation.error.title),
1920 "changed",
1921 G_CALLBACK (cb_validation_rebuild), state);
1922 g_signal_connect (G_OBJECT (gtk_text_view_get_buffer (state->validation.error.msg)),
1923 "changed",
1924 G_CALLBACK (cb_validation_rebuild), state);
1926 /* Initialize */
1927 if (0 == (state->conflicts & (1 << MSTYLE_VALIDATION)))
1928 v = gnm_style_get_validation (state->style);
1929 if (v != NULL) {
1930 GnmParsePos pp;
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 : "");
1938 if (v->msg != NULL)
1939 gnm_textview_set_text (GTK_TEXT_VIEW (state->validation.error.msg),
1940 v->msg->str);
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 /*****************************************************************************/
1958 static void
1959 input_msg_rebuild_input_msg (FormatState *state)
1961 GnmInputMsg *im;
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);
1966 g_free (msg);
1967 gnm_style_set_input_msg (state->result, im);
1968 fmt_dialog_changed (state);
1971 static void
1972 cb_input_msg_rebuild (G_GNUC_UNUSED void *ignored,
1973 FormatState *state)
1975 input_msg_rebuild_input_msg (state);
1978 static void
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) {
1989 if (flag)
1990 input_msg_rebuild_input_msg (state);
1991 else
1992 gnm_style_set_input_msg (state->result, NULL);
1993 fmt_dialog_changed (state);
1997 static void
1998 fmt_dialog_init_input_msg_page (FormatState *state)
2000 GnmInputMsg const *im = NULL;
2002 g_return_if_fail (state != NULL);
2004 /* Setup widgets */
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"));
2011 /* Initialize */
2012 if (0 == (state->conflicts & (1 << MSTYLE_INPUT_MSG)))
2013 im = gnm_style_get_input_msg (state->style);
2014 if (im) {
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),
2027 "toggled",
2028 G_CALLBACK (cb_input_msg_flag_toggled), state);
2029 g_signal_connect (G_OBJECT (state->input_msg.title),
2030 "changed",
2031 G_CALLBACK (cb_input_msg_rebuild), state);
2032 g_signal_connect (G_OBJECT (gtk_text_view_get_buffer (state->input_msg.msg)),
2033 "changed",
2034 G_CALLBACK (cb_input_msg_rebuild), state);
2036 /* Initialize */
2037 cb_input_msg_flag_toggled (state->input_msg.flag, state);
2040 /*****************************************************************************/
2042 /* button handlers */
2043 static void
2044 cb_fmt_dialog_dialog_buttons (GtkWidget *btn, FormatState *state)
2046 #if 0
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,
2055 /* reverse */
2056 GNM_STYLE_BORDER_RIGHT, GNM_STYLE_BORDER_LEFT,
2057 /* reverse */
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)
2064 bmap = bmap_rtl;
2065 #endif
2067 if (btn == state->apply_button || btn == state->ok_button) {
2068 int i;
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),
2075 state->sheet);
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),
2083 FALSE,
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);
2088 } else {
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);
2093 else
2094 gnm_expr_entry_grab_focus (state->validation.expr1.entry, FALSE);
2095 return;
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);
2109 if (b)
2110 gnm_style_set_border
2111 (state->result,
2112 MSTYLE_BORDER_TOP +
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,
2119 style);
2120 gnm_style_unref (state->result);
2121 } else {
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 */
2142 static void
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);
2149 g_free (state);
2152 /* Handler for expr-entry's focus.
2154 * NOTE: This will only become useful once the
2155 * cell format dialog is made non-modal
2157 static void
2158 cb_fmt_dialog_set_focus (G_GNUC_UNUSED GtkWidget *window,
2159 G_GNUC_UNUSED GtkWidget *focus_widget,
2160 FormatState *state)
2162 if (state->validation.changed)
2163 validation_rebuild_validation (state);
2166 static void
2167 cb_dialog_destroy (GtkDialog *dialog)
2169 g_object_set_data (G_OBJECT (dialog), "state", NULL);
2173 /* Set initial focus */
2174 static void
2175 set_initial_focus (FormatState *s)
2177 GtkWidget *focus_widget = NULL, *pagew;
2178 gchar const *name;
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));
2185 return;
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);
2196 else
2197 focus_widget = NULL;
2199 if (focus_widget &&
2200 gtk_widget_get_can_focus (focus_widget) &&
2201 gtk_widget_is_sensitive (focus_widget))
2202 gtk_widget_grab_focus (focus_widget);
2205 static void
2206 fmt_dialog_impl (FormatState *state, FormatDialogPosition_t pageno, gint pages)
2208 static struct {
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 },
2233 { NULL, 0}
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",
2260 NULL
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",
2269 NULL
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",
2275 NULL
2278 int page_signal;
2279 int i, selected;
2280 char const *name;
2281 gboolean has_back;
2282 GOColor default_border_color;
2283 int default_border_style = GNM_STYLE_BORDER_THIN;
2284 GtkStyleContext *ctxt;
2285 GdkRGBA bc;
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"));
2292 /* Initialize */
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),
2323 "switch_page",
2324 G_CALLBACK (cb_page_select), NULL);
2325 g_signal_connect (G_OBJECT (state->notebook),
2326 "destroy",
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;
2335 break;
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 */
2347 FALSE,
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);
2367 if (tmp != NULL) {
2368 init_border_button (state, i, tmp,
2369 state->borders[i]);
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
2380 has_back = FALSE;
2381 selected = 1;
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)),
2397 state->gui, name,
2398 &state->back.pattern, TRUE, TRUE,
2399 i+1, /* Pattern #s start at 1 */
2400 selected, 16);
2402 /* If the pattern is 0 indicating no background colour
2403 * Set background to No colour. This will set states correctly.
2405 if (!has_back)
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);
2411 if (tmp != NULL) {
2412 state->border.preset[i] = GTK_BUTTON (tmp);
2413 g_signal_connect (G_OBJECT (tmp),
2414 "clicked",
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),
2430 "clicked",
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),
2435 "clicked",
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),
2439 "clicked",
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),
2452 "set-focus",
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
2456 * selection.
2457 * There are some UI issues to discuss before we do this, though. Most
2458 * important:
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);
2471 if (pages > 0)
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),
2481 CELL_FORMAT_KEY);
2484 static GnmValue *
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;
2492 if (!fmt ||
2493 go_format_is_markup (fmt) ||
2494 go_format_eq (common, fmt))
2495 return NULL;
2497 if (go_format_is_general (common)) {
2498 gnm_style_set_format (state->style, fmt);
2499 return NULL;
2500 } else {
2501 state->conflicts |= MSTYLE_FORMAT;
2502 return VALUE_TERMINATE;
2506 static gboolean
2507 fmt_dialog_selection_type (SheetView *sv,
2508 GnmRange const *range,
2509 gpointer user_data)
2511 FormatState *state = user_data;
2512 GSList *merged = gnm_sheet_merge_get_overlap (sv->sheet, range);
2513 GnmRange r = *range;
2514 gboolean allow_multi =
2515 merged == NULL ||
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)
2524 if (allow_multi)
2525 state->selection_mask |= 2;
2526 else
2527 r.end.col = r.start.col;
2529 if (range->start.row != range->end.row)
2531 if (allow_multi)
2532 state->selection_mask |= 1;
2533 else
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,
2546 state);
2549 return TRUE;
2552 static FormatState *
2553 dialog_cell_format_init (WBCGtk *wbcg)
2555 GtkBuilder *gui;
2556 GnmCell *edit_cell;
2557 FormatState *state;
2559 gui = gnm_gtk_builder_load ("res:ui/cell-format.ui", NULL, GO_CMD_CONTEXT (wbcg));
2560 if (gui == NULL)
2561 return NULL;
2563 /* Initialize */
2564 state = g_new (FormatState, 1);
2565 state->wbcg = wbcg;
2566 state->gui = gui;
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;
2583 return state;
2586 void
2587 dialog_cell_format (WBCGtk *wbcg, FormatDialogPosition_t pageno, gint pages)
2589 FormatState *state;
2591 g_return_if_fail (wbcg != NULL);
2593 state = dialog_cell_format_init (wbcg);
2595 if (state == NULL)
2596 return;
2598 state->style_selector.is_selector = FALSE;
2599 state->style_selector.w = NULL;
2600 state->style_selector.closure = NULL;
2602 if (pages == 0) {
2603 int i;
2604 for (i = FD_NUMBER; i <= FD_PROTECTION; i++)
2605 pages |= (1 << 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));
2617 * TODO
2619 * Borders
2620 * - Add the 'text' elements in the preview
2622 * Wishlist
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.
2632 GtkDialog *
2633 dialog_cell_format_select_style (WBCGtk *wbcg, gint pages,
2634 GtkWindow *w,
2635 GnmStyle *style, gpointer closure)
2637 FormatState *state;
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;
2648 if (style) {
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;