1.12.42
[gnumeric.git] / src / dialogs / dialog-cell-format-cond.c
blob86931e4fc4976b75f6d934a9a9e027b93312ef93
1 /*
2 * dialog-cell-format-cond.c: Implements a dialog to format cells.
4 * (c) Copyright 2010-2011 Andreas J. Guelzow <aguelzow@pyrshep.ca>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <https://www.gnu.org/licenses/>.
18 **/
20 #include <gnumeric-config.h>
21 #include <glib/gi18n-lib.h>
22 #include <gnumeric.h>
23 #include <dialogs/dialogs.h>
24 #include <dialogs/help.h>
26 #include <sheet.h>
27 #include <sheet-view.h>
28 #include <sheet-merge.h>
29 #include <sheet-style.h>
30 #include <gui-util.h>
31 #include <selection.h>
32 #include <ranges.h>
33 #include <cell.h>
34 #include <expr.h>
35 #include <value.h>
36 #include <position.h>
37 #include <mstyle.h>
38 #include <application.h>
39 #include <validation.h>
40 #include <workbook.h>
41 #include <wbc-gtk.h>
42 #include <commands.h>
43 #include <mathfunc.h>
44 #include <style-conditions.h>
47 #include <string.h>
49 #define CELL_FORMAT_KEY "cell-format-cond-dialog"
50 #define CELL_FORMAT_DEF_KEY "cell-format-cond-def-dialog"
52 typedef struct _CFormatState {
53 GtkBuilder *gui;
54 WBCGtk *wbcg;
55 GtkDialog *dialog;
56 GtkWidget *close_button;
58 Sheet *sheet;
59 SheetView *sv;
60 unsigned int conflicts;
61 gboolean homogeneous;
62 GnmStyle *style;
64 GtkButton *remove;
65 GtkButton *clear;
66 GtkButton *expand;
67 GtkLabel *label;
68 GtkTreeView *treeview;
69 GtkTreeStore *model;
70 GtkTreeSelection *selection;
72 struct {
73 GOUndo *undo;
74 GOUndo *redo;
75 int size;
76 GnmStyle *new_style;
77 GnmStyle *old_style;
78 gboolean existing_conds_only;
79 } action;
80 struct {
81 GtkWidget *edit_style_button;
82 GtkWidget *add_button;
83 GtkWidget *replace_button;
84 GtkWidget *copy_button;
85 GtkWidget *combo;
86 GtkWidget *expr_x;
87 GtkWidget *expr_y;
88 GtkListStore *typestore;
89 GnmStyle *style;
90 GtkWidget *style_label;
91 GtkDialog *dialog;
92 } editor;
93 } CFormatState;
95 enum {
96 CONDITIONS_RANGE,
97 CONDITIONS_COND,
98 CONDITIONS_REFERENCE,
99 CONDITIONS_NUM_COLUMNS
104 /*****************************************************************************/
105 static void c_fmt_dialog_load (CFormatState *state);
106 static void c_fmt_dialog_apply_add_choice (CFormatState *state, GnmStyleCond *cond, gboolean add);
108 /*****************************************************************************/
110 /* button handlers */
111 static void
112 cb_c_fmt_dialog_dialog_buttons (G_GNUC_UNUSED GtkWidget *btn, CFormatState *state)
114 /* users may accidentally click on 'close' before adding the formatting style see #733352 */
115 if (!gtk_widget_get_sensitive (GTK_WIDGET (state->editor.add_button)) ||
116 gtk_widget_get_sensitive (GTK_WIDGET (state->clear)) ||
117 go_gtk_query_yes_no (GTK_WINDOW (state->dialog), FALSE,
118 _("You did not add the defined conditional format."
119 " Do you really want to close the conditional formatting dialog?")))
120 gtk_widget_destroy (GTK_WIDGET (state->dialog));
123 /* Handler for destroy */
124 static void
125 cb_c_fmt_dialog_dialog_destroy (CFormatState *state)
127 if (state->editor.dialog) {
128 gtk_widget_destroy (GTK_WIDGET (state->editor.dialog));
129 state->editor.dialog = NULL;
131 if (state->editor.style)
132 gnm_style_unref (state->editor.style);
133 if (state->style)
134 gnm_style_unref (state->style);
135 g_object_unref (state->gui);
136 g_free (state);
139 static void
140 cb_dialog_destroy (GtkDialog *dialog)
142 g_object_set_data (G_OBJECT (dialog), "state", NULL);
145 /*****************************************************************************/
147 static void
148 c_fmt_dialog_set_sensitive (CFormatState *state)
150 gboolean ok = (state->editor.style != NULL && state->homogeneous);
151 GnmParsePos pp;
152 GtkTreeIter iter;
153 gboolean not_empty, selected;
155 not_empty = gtk_tree_model_get_iter_first
156 (GTK_TREE_MODEL (state->model), &iter);
157 selected = gtk_tree_selection_get_selected
158 (state->selection, NULL, NULL);
160 gtk_widget_set_sensitive (GTK_WIDGET (state->clear), not_empty);
162 gtk_widget_set_sensitive (GTK_WIDGET (state->remove),
163 state->homogeneous && selected);
164 gtk_widget_set_sensitive (GTK_WIDGET (state->expand),
165 (!state->homogeneous) && selected);
167 parse_pos_init_editpos (&pp, state->sv);
169 if (ok && gtk_widget_get_sensitive (state->editor.expr_x)) {
170 GnmExprTop const *texpr = gnm_expr_entry_parse (GNM_EXPR_ENTRY (state->editor.expr_x), &pp,
171 NULL, FALSE,
172 GNM_EXPR_PARSE_UNKNOWN_NAMES_ARE_STRINGS);
173 ok = (texpr != NULL);
174 if (texpr)
175 gnm_expr_top_unref (texpr);
177 if (ok && gtk_widget_get_sensitive (state->editor.expr_y)) {
178 GnmExprTop const *texpr = gnm_expr_entry_parse (GNM_EXPR_ENTRY (state->editor.expr_y), &pp,
179 NULL, FALSE,
180 GNM_EXPR_PARSE_UNKNOWN_NAMES_ARE_STRINGS);
181 ok = (texpr != NULL);
182 if (texpr)
183 gnm_expr_top_unref (texpr);
186 gtk_widget_set_sensitive (state->editor.add_button, ok);
187 gtk_widget_set_sensitive (state->editor.replace_button, ok && selected);
188 gtk_widget_set_sensitive (state->editor.copy_button, selected && state->homogeneous);
191 static void
192 c_fmt_dialog_set_expr_sensitive (CFormatState *state)
194 GtkTreeIter iter;
195 gint n_expr = 0;
197 if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (state->editor.combo), &iter))
198 gtk_tree_model_get (GTK_TREE_MODEL (state->editor.typestore),
199 &iter,
200 2, &n_expr,
201 -1);
202 if (n_expr < 1) {
203 gtk_widget_set_sensitive (state->editor.expr_x, FALSE);
204 gtk_entry_set_text (gnm_expr_entry_get_entry (GNM_EXPR_ENTRY (state->editor.expr_x)), "");
205 } else
206 gtk_widget_set_sensitive (state->editor.expr_x, TRUE);
207 if (n_expr < 2) {
208 gtk_widget_set_sensitive (state->editor.expr_y, FALSE);
209 gtk_entry_set_text (gnm_expr_entry_get_entry (GNM_EXPR_ENTRY (state->editor.expr_y)), "");
210 } else
211 gtk_widget_set_sensitive (state->editor.expr_y, TRUE);
214 static void
215 cb_c_fmt_dialog_chooser_type_changed (G_GNUC_UNUSED GtkComboBox *widget, CFormatState *state)
217 c_fmt_dialog_set_expr_sensitive (state);
218 c_fmt_dialog_set_sensitive (state);
221 static gboolean
222 cb_c_fmt_dialog_chooser_entry_changed (G_GNUC_UNUSED GnmExprEntry *widget, G_GNUC_UNUSED GdkEvent *event,
223 CFormatState *state)
225 c_fmt_dialog_set_sensitive (state);
226 return FALSE;
229 void
230 dialog_cell_format_style_added (gpointer closure, GnmStyle *style)
232 CFormatState *state = closure;
234 if (state->editor.style)
235 gnm_style_unref (state->editor.style);
236 state->editor.style = style;
237 gtk_label_set_text (GTK_LABEL (state->editor.style_label),
238 style ? _("(defined)") : _("(undefined)"));
239 c_fmt_dialog_set_sensitive (state);
242 static void
243 c_fmt_dialog_set_component (CFormatState *state, GnmStyle *overlay, gchar const *name,
244 GnmStyleElement elem, gboolean uncheck)
246 GtkWidget *w = go_gtk_builder_get_widget (state->gui, name);
248 if (gnm_style_is_element_set (overlay, elem))
249 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), TRUE);
250 else if (uncheck)
251 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), FALSE);
255 static gint
256 cb_c_fmt_dialog_chooser_check_page (CFormatState *state, gchar const *name,
257 gint page)
259 GtkWidget *w = go_gtk_builder_get_widget (state->gui, name);
261 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w)))
262 return (1 << page);
263 else
264 return 0;
267 static void
268 editor_destroy_cb (G_GNUC_UNUSED GObject *obj, CFormatState *state)
270 state->editor.dialog = NULL;
273 static void
274 c_fmt_dialog_select_style (CFormatState *state, int pages)
276 if (state->editor.dialog)
277 gtk_widget_destroy (GTK_WIDGET (state->editor.dialog));
278 state->editor.dialog = dialog_cell_format_select_style
279 (state->wbcg, pages,
280 GTK_WINDOW (state->dialog),
281 state->editor.style, state);
282 if (state->editor.dialog)
283 g_signal_connect
284 (G_OBJECT (state->editor.dialog),
285 "destroy", G_CALLBACK (editor_destroy_cb), state);
288 static void
289 cb_c_fmt_dialog_edit_style_button (G_GNUC_UNUSED GtkWidget *btn, CFormatState *state)
291 int pages = 0;
292 pages |= cb_c_fmt_dialog_chooser_check_page
293 (state, "check-background", FD_BACKGROUND);
294 pages |= cb_c_fmt_dialog_chooser_check_page
295 (state, "check-number", FD_NUMBER);
296 pages |= cb_c_fmt_dialog_chooser_check_page
297 (state, "check-align", FD_ALIGNMENT);
298 pages |= cb_c_fmt_dialog_chooser_check_page
299 (state, "check-font", FD_FONT);
300 pages |= cb_c_fmt_dialog_chooser_check_page
301 (state, "check-border", FD_BORDER);
302 pages |= cb_c_fmt_dialog_chooser_check_page
303 (state, "check-protection", FD_PROTECTION);
304 pages |= cb_c_fmt_dialog_chooser_check_page
305 (state, "check-validation", FD_VALIDATION);
307 if (state->editor.style != NULL)
308 gnm_style_ref (state->editor.style);
309 c_fmt_dialog_select_style (state, pages);
312 static GnmStyleCond *
313 c_fmt_dialog_get_condition (CFormatState *state)
315 GnmStyleCondOp op;
316 GnmStyleCond *cond;
317 GtkTreeIter iter;
318 gint n_expr = 0;
319 GnmParsePos pp;
320 GnmStyle *overlay;
322 parse_pos_init_editpos (&pp, state->sv);
324 overlay = gnm_style_new ();
325 if (state->editor.style) {
326 if (cb_c_fmt_dialog_chooser_check_page
327 (state, "check-background", FD_BACKGROUND)) {
328 gnm_style_merge_element (overlay, state->editor.style,
329 MSTYLE_COLOR_BACK);
330 gnm_style_merge_element (overlay, state->editor.style,
331 MSTYLE_COLOR_PATTERN);
332 gnm_style_merge_element (overlay, state->editor.style,
333 MSTYLE_PATTERN);
335 if (cb_c_fmt_dialog_chooser_check_page
336 (state, "check-number", FD_NUMBER)) {
337 gnm_style_merge_element (overlay, state->editor.style,
338 MSTYLE_FORMAT);
340 if (cb_c_fmt_dialog_chooser_check_page
341 (state, "check-align", FD_ALIGNMENT)) {
342 gnm_style_merge_element (overlay, state->editor.style,
343 MSTYLE_ALIGN_V);
344 gnm_style_merge_element (overlay, state->editor.style,
345 MSTYLE_ALIGN_H);
346 gnm_style_merge_element (overlay, state->editor.style,
347 MSTYLE_INDENT);
348 gnm_style_merge_element (overlay, state->editor.style,
349 MSTYLE_ROTATION);
350 gnm_style_merge_element (overlay, state->editor.style,
351 MSTYLE_TEXT_DIR);
352 gnm_style_merge_element (overlay, state->editor.style,
353 MSTYLE_WRAP_TEXT);
354 gnm_style_merge_element (overlay, state->editor.style,
355 MSTYLE_SHRINK_TO_FIT);
357 if (cb_c_fmt_dialog_chooser_check_page
358 (state, "check-font", FD_FONT)) {
359 gnm_style_merge_element (overlay, state->editor.style,
360 MSTYLE_FONT_COLOR);
361 gnm_style_merge_element (overlay, state->editor.style,
362 MSTYLE_FONT_NAME);
363 gnm_style_merge_element (overlay, state->editor.style,
364 MSTYLE_FONT_BOLD);
365 gnm_style_merge_element (overlay, state->editor.style,
366 MSTYLE_FONT_ITALIC);
367 gnm_style_merge_element (overlay, state->editor.style,
368 MSTYLE_FONT_UNDERLINE);
369 gnm_style_merge_element (overlay, state->editor.style,
370 MSTYLE_FONT_STRIKETHROUGH);
371 gnm_style_merge_element (overlay, state->editor.style,
372 MSTYLE_FONT_SCRIPT);
373 gnm_style_merge_element (overlay, state->editor.style,
374 MSTYLE_FONT_SIZE);
376 if (cb_c_fmt_dialog_chooser_check_page
377 (state, "check-border", FD_BORDER)) {
378 gnm_style_merge_element (overlay, state->editor.style,
379 MSTYLE_BORDER_TOP);
380 gnm_style_merge_element (overlay, state->editor.style,
381 MSTYLE_BORDER_BOTTOM);
382 gnm_style_merge_element (overlay, state->editor.style,
383 MSTYLE_BORDER_LEFT);
384 gnm_style_merge_element (overlay, state->editor.style,
385 MSTYLE_BORDER_RIGHT);
386 gnm_style_merge_element (overlay, state->editor.style,
387 MSTYLE_BORDER_REV_DIAGONAL);
388 gnm_style_merge_element (overlay, state->editor.style,
389 MSTYLE_BORDER_DIAGONAL);
391 if (cb_c_fmt_dialog_chooser_check_page
392 (state, "check-protection", FD_PROTECTION)) {
395 if (cb_c_fmt_dialog_chooser_check_page
396 (state, "check-validation", FD_VALIDATION)) {
400 if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (state->editor.combo), &iter))
401 gtk_tree_model_get (GTK_TREE_MODEL (state->editor.typestore),
402 &iter,
403 1, &op,
404 2, &n_expr,
405 -1);
406 else
407 op = GNM_STYLE_COND_CONTAINS_ERR;
409 cond = gnm_style_cond_new (op, state->sheet);
410 gnm_style_cond_set_overlay (cond, overlay);
411 gnm_style_unref (overlay);
413 if (n_expr > 0) {
414 GnmExprTop const *texpr = gnm_expr_entry_parse (GNM_EXPR_ENTRY (state->editor.expr_x), &pp,
415 NULL, FALSE,
416 GNM_EXPR_PARSE_UNKNOWN_NAMES_ARE_STRINGS);
417 gnm_style_cond_set_expr (cond, texpr, 0);
418 gnm_expr_top_unref (texpr);
420 if (n_expr > 1) {
421 GnmExprTop const *texpr = gnm_expr_entry_parse (GNM_EXPR_ENTRY (state->editor.expr_y), &pp,
422 NULL, FALSE,
423 GNM_EXPR_PARSE_UNKNOWN_NAMES_ARE_STRINGS);
424 gnm_style_cond_set_expr (cond, texpr, 1);
425 gnm_expr_top_unref (texpr);
427 return cond;
430 static void
431 cb_c_fmt_dialog_add_button (G_GNUC_UNUSED GtkWidget *btn, CFormatState *state)
433 GnmStyleCond *cond = c_fmt_dialog_get_condition (state);
434 c_fmt_dialog_apply_add_choice (state, cond, TRUE);
435 gnm_style_cond_free (cond);
438 static void
439 cb_c_fmt_dialog_replace_button (G_GNUC_UNUSED GtkWidget *btn, CFormatState *state)
441 GnmStyleCond *cond = c_fmt_dialog_get_condition (state);
442 c_fmt_dialog_apply_add_choice (state, cond, FALSE);
443 gnm_style_cond_free (cond);
446 static void
447 cb_c_fmt_dialog_copy_button (G_GNUC_UNUSED GtkWidget *btn, CFormatState *state)
449 GnmStyleConditions *sc;
450 GtkTreeIter iter;
451 sc = gnm_style_get_conditions (state->style);
452 if (sc != NULL && gtk_tree_selection_get_selected (state->selection, NULL, &iter)) {
453 GtkTreePath *path = gtk_tree_model_get_path
454 (GTK_TREE_MODEL (state->model), &iter);
455 gint *pind = gtk_tree_path_get_indices (path);
456 GPtrArray const *conds = gnm_style_conditions_details (sc);
457 if (pind && conds) {
458 gint ind = *pind;
459 GnmStyleCond *gsc = g_ptr_array_index (conds, ind);
460 GtkTreeIter iter;
461 GnmParsePos pp;
462 GnmStyle *style;
463 GnmStyleConditions *conds;
465 /* Set the condition op */
466 if (gtk_tree_model_get_iter_first
467 (GTK_TREE_MODEL (state->editor.typestore), &iter)) {
468 do {
469 guint op;
470 gtk_tree_model_get (GTK_TREE_MODEL (state->editor.typestore),
471 &iter,
472 1, &op,
473 -1);
474 if (op == gsc->op) {
475 gtk_combo_box_set_active_iter
476 (GTK_COMBO_BOX (state->editor.combo), &iter);
477 break;
479 } while (gtk_tree_model_iter_next
480 (GTK_TREE_MODEL (state->editor.typestore), &iter));
482 /* Set the expressions */
483 parse_pos_init_editpos (&pp, state->sv);
484 if (gnm_style_cond_get_expr (gsc, 0))
485 gnm_expr_entry_load_from_expr (GNM_EXPR_ENTRY (state->editor.expr_x),
486 gnm_style_cond_get_expr (gsc, 0),
487 &pp);
488 else
489 gnm_expr_entry_load_from_text (GNM_EXPR_ENTRY (state->editor.expr_x),
490 "");
491 if (gnm_style_cond_get_expr (gsc, 1))
492 gnm_expr_entry_load_from_expr (GNM_EXPR_ENTRY (state->editor.expr_y),
493 gnm_style_cond_get_expr (gsc, 1),
494 &pp);
495 else
496 gnm_expr_entry_load_from_text (GNM_EXPR_ENTRY (state->editor.expr_y),
497 "");
498 /* Set the style */
499 conds = state->style
500 ? gnm_style_get_conditions (state->style)
501 : NULL;
502 if (conds)
503 style = gnm_style_dup
504 (gnm_style_get_cond_style (state->style, ind));
505 else {
506 style = gnm_style_new_default ();
507 gnm_style_merge (style, gsc->overlay);
509 dialog_cell_format_style_added (state, style);
510 /* Set the appl. style components */
511 c_fmt_dialog_set_component (state, gsc->overlay, "check-background",
512 MSTYLE_COLOR_BACK, TRUE);
513 c_fmt_dialog_set_component (state, gsc->overlay, "check-background",
514 MSTYLE_COLOR_PATTERN, FALSE);
515 c_fmt_dialog_set_component (state, gsc->overlay, "check-background",
516 MSTYLE_PATTERN, FALSE);
517 c_fmt_dialog_set_component (state, gsc->overlay, "check-number",
518 MSTYLE_FORMAT, TRUE);
519 c_fmt_dialog_set_component (state, gsc->overlay, "check-align",
520 MSTYLE_ALIGN_V, TRUE);
521 c_fmt_dialog_set_component (state, gsc->overlay, "check-align",
522 MSTYLE_ALIGN_H, FALSE);
523 c_fmt_dialog_set_component (state, gsc->overlay, "check-align",
524 MSTYLE_ROTATION, FALSE);
525 c_fmt_dialog_set_component (state, gsc->overlay, "check-align",
526 MSTYLE_INDENT, FALSE);
527 c_fmt_dialog_set_component (state, gsc->overlay, "check-align",
528 MSTYLE_TEXT_DIR, FALSE);
529 c_fmt_dialog_set_component (state, gsc->overlay, "check-align",
530 MSTYLE_WRAP_TEXT, FALSE);
531 c_fmt_dialog_set_component (state, gsc->overlay, "check-align",
532 MSTYLE_SHRINK_TO_FIT, FALSE);
533 c_fmt_dialog_set_component (state, gsc->overlay, "check-font",
534 MSTYLE_FONT_COLOR, TRUE);
535 c_fmt_dialog_set_component (state, gsc->overlay, "check-font",
536 MSTYLE_FONT_NAME, FALSE);
537 c_fmt_dialog_set_component (state, gsc->overlay, "check-font",
538 MSTYLE_FONT_BOLD, FALSE);
539 c_fmt_dialog_set_component (state, gsc->overlay, "check-font",
540 MSTYLE_FONT_ITALIC, FALSE);
541 c_fmt_dialog_set_component (state, gsc->overlay, "check-font",
542 MSTYLE_FONT_UNDERLINE, FALSE);
543 c_fmt_dialog_set_component (state, gsc->overlay, "check-font",
544 MSTYLE_FONT_STRIKETHROUGH, FALSE);
545 c_fmt_dialog_set_component (state, gsc->overlay, "check-font",
546 MSTYLE_FONT_SCRIPT, FALSE);
547 c_fmt_dialog_set_component (state, gsc->overlay, "check-font",
548 MSTYLE_FONT_SIZE, FALSE);
549 c_fmt_dialog_set_component (state, gsc->overlay, "check-border",
550 MSTYLE_BORDER_TOP, TRUE);
551 c_fmt_dialog_set_component (state, gsc->overlay, "check-border",
552 MSTYLE_BORDER_BOTTOM, FALSE);
553 c_fmt_dialog_set_component (state, gsc->overlay, "check-border",
554 MSTYLE_BORDER_LEFT, FALSE);
555 c_fmt_dialog_set_component (state, gsc->overlay, "check-border",
556 MSTYLE_BORDER_RIGHT, FALSE);
557 c_fmt_dialog_set_component (state, gsc->overlay, "check-border",
558 MSTYLE_BORDER_REV_DIAGONAL, FALSE);
559 c_fmt_dialog_set_component (state, gsc->overlay, "check-border",
560 MSTYLE_BORDER_DIAGONAL, FALSE);
562 gtk_tree_path_free (path);
566 static void
567 c_fmt_dialog_chooser_load_combo (CFormatState *state)
569 static struct {
570 char const *label;
571 gint type;
572 gint n_expressions;
573 } cond_types[] = {
574 /* without any expression */
575 { N_("Cell contains an error value."), GNM_STYLE_COND_CONTAINS_ERR, 0},
576 { N_("Cell does not contain an error value."), GNM_STYLE_COND_NOT_CONTAINS_ERR, 0},
577 { N_("Cell contains whitespace."), GNM_STYLE_COND_CONTAINS_BLANKS, 0},
578 { N_("Cell does not contain whitespace."), GNM_STYLE_COND_NOT_CONTAINS_BLANKS, 0},
579 /* with one expression */
580 { N_("Cell value is = x."), GNM_STYLE_COND_EQUAL, 1},
581 { N_("Cell value is \xe2\x89\xa0 x."), GNM_STYLE_COND_NOT_EQUAL, 1},
582 { N_("Cell value is > x."), GNM_STYLE_COND_GT, 1},
583 { N_("Cell value is < x."), GNM_STYLE_COND_LT, 1},
584 { N_("Cell value is \xe2\x89\xa7 x."), GNM_STYLE_COND_GTE, 1},
585 { N_("Cell value is \xe2\x89\xa6 x."), GNM_STYLE_COND_LTE, 1},
586 { N_("Expression x evaluates to TRUE."), GNM_STYLE_COND_CUSTOM, 1},
587 { N_("Cell contains the string x."), GNM_STYLE_COND_CONTAINS_STR, 1},
588 { N_("Cell does not contain the string x."), GNM_STYLE_COND_NOT_CONTAINS_STR, 1},
589 { N_("Cell value begins with the string x."), GNM_STYLE_COND_BEGINS_WITH_STR, 1},
590 { N_("Cell value does not begin with the string x."), GNM_STYLE_COND_NOT_BEGINS_WITH_STR, 1},
591 { N_("Cell value ends with the string x."), GNM_STYLE_COND_ENDS_WITH_STR, 1},
592 { N_("Cell value does not end with the string x."), GNM_STYLE_COND_NOT_ENDS_WITH_STR, 1},
593 /* with two expressions */
594 { N_("Cell value is between x and y (incl.)."), GNM_STYLE_COND_BETWEEN, 2},
595 { N_("Cell value is not between x and y (incl.)."), GNM_STYLE_COND_NOT_BETWEEN, 2}
597 guint i;
598 GtkCellRenderer *cell;
599 GtkTreeIter iter;
601 for (i = 0; i < G_N_ELEMENTS (cond_types); i++)
602 gtk_list_store_insert_with_values (state->editor.typestore,
603 NULL, G_MAXINT,
604 0, _(cond_types[i].label),
605 1, cond_types[i].type,
606 2, cond_types[i].n_expressions,
607 -1);
608 cell = gtk_cell_renderer_text_new();
609 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(state->editor.combo), cell, TRUE);
610 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(state->editor.combo), cell, "text", 0, NULL);
611 if (gtk_tree_model_get_iter_first
612 (GTK_TREE_MODEL (state->editor.typestore), &iter))
613 gtk_combo_box_set_active_iter (GTK_COMBO_BOX (state->editor.combo), &iter);
617 /*****************************************************************************/
618 /*****************************************************************************/
619 static gboolean
620 c_fmt_dialog_condition_setter (SheetView *sv, GnmRange const *range, CFormatState *state)
622 GnmSheetRange *sr = g_new (GnmSheetRange, 1);
623 sr->range = *range;
624 sr->sheet = sv->sheet;
625 state->action.redo = go_undo_combine
626 (state->action.redo,
627 sheet_apply_style_undo (sr, state->action.new_style));
628 sr = g_new (GnmSheetRange, 1);
629 sr->range = *range;
630 sr->sheet = sv->sheet;
631 state->action.undo = go_undo_combine
632 (sheet_apply_style_undo (sr, state->action.old_style),
633 state->action.undo);
634 state->action.size++;
635 return TRUE;
638 static gboolean
639 c_fmt_dialog_condition_setter_tiled (G_GNUC_UNUSED SheetView *sv, GnmRange const *range,
640 CFormatState *state)
642 GnmStyleList *l, *list;
643 if (state->action.existing_conds_only)
644 list = sheet_style_collect_conditions (state->sheet, range);
645 else
646 list = sheet_style_get_range (state->sheet, range);
647 for (l = list; l != NULL; l = l->next) {
648 GnmStyleConditions *old_cond;
649 GnmStyleRegion const *sr = l->data;
650 GnmRange r = *((GnmRange *) l->data);
652 r.start.row += range->start.row;
653 r.end.row += range->start.row;
654 r.start.col += range->start.col;
655 r.end.col += range->start.col;
656 state->action.old_style = gnm_style_new ();
657 if (gnm_style_is_element_set (sr->style, MSTYLE_CONDITIONS) &&
658 NULL != (old_cond = gnm_style_get_conditions (sr->style)))
659 gnm_style_set_conditions (state->action.old_style,
660 g_object_ref (old_cond));
661 else
662 gnm_style_set_conditions (state->action.old_style, NULL);
663 c_fmt_dialog_condition_setter (state->sv, &r, state);
664 gnm_style_unref (state->action.old_style);
665 state->action.old_style = NULL;
667 style_list_free (list);
668 return TRUE;
671 static void
672 c_fmt_dialog_set_conditions (CFormatState *state, char const *cmd_label)
674 GnmStyleConditions *old_cond;
676 state->action.undo = NULL;
677 state->action.redo = NULL;
678 state->action.size = 0;
680 if (state->homogeneous) {
681 state->action.old_style = gnm_style_new ();
682 old_cond = gnm_style_get_conditions (state->style);
683 gnm_style_set_conditions (state->action.old_style,
684 old_cond ? g_object_ref (old_cond) : NULL);
686 sv_selection_foreach (state->sv,
687 (GnmSelectionFunc)c_fmt_dialog_condition_setter,
688 state);
689 } else {
690 sv_selection_foreach (state->sv,
691 (GnmSelectionFunc)c_fmt_dialog_condition_setter_tiled,
692 state);
694 cmd_generic_with_size (GNM_WBC (state->wbcg), cmd_label,
695 state->action.size, state->action.undo, state->action.redo);
697 state->action.undo = NULL;
698 state->action.redo = NULL;
699 if (state->action.old_style) {
700 gnm_style_unref (state->action.old_style);
701 state->action.old_style = NULL;
705 static void
706 c_fmt_dialog_apply_add_choice (CFormatState *state, GnmStyleCond *cond, gboolean add)
708 if (cond != NULL) {
709 GnmStyleConditions *sc;
710 int index = -1;
711 sc = gnm_style_conditions_dup (gnm_style_get_conditions (state->style));
712 if (sc == NULL)
713 sc = gnm_style_conditions_new (state->sheet);
714 if (!add) {
715 GtkTreeIter iter;
716 if (gtk_tree_selection_get_selected (state->selection, NULL, &iter)) {
717 GtkTreePath *path = gtk_tree_model_get_path
718 (GTK_TREE_MODEL (state->model), &iter);
719 gint *ind = gtk_tree_path_get_indices (path);
720 if (ind) {
721 gnm_style_conditions_delete (sc, *ind);
722 index = *ind;
724 gtk_tree_path_free (path);
727 gnm_style_conditions_insert (sc, cond, index);
728 state->action.new_style = gnm_style_new ();
729 gnm_style_set_conditions (state->action.new_style, sc);
730 state->action.existing_conds_only = FALSE;
732 c_fmt_dialog_set_conditions (state, _("Set conditional formatting"));
734 gnm_style_unref (state->action.new_style);
735 state->action.new_style = NULL;
737 c_fmt_dialog_load (state);
741 static void
742 cb_c_fmt_dialog_clear_clicked (G_GNUC_UNUSED GtkButton *button, CFormatState *state)
744 state->action.new_style = gnm_style_new ();
745 gnm_style_set_conditions (state->action.new_style, NULL);
746 state->action.existing_conds_only = TRUE;
748 c_fmt_dialog_set_conditions (state, _("Clear conditional formatting"));
750 gnm_style_unref (state->action.new_style);
751 state->action.new_style = NULL;
753 c_fmt_dialog_load (state);
756 static void
757 cb_c_fmt_dialog_remove_clicked (GtkButton *button, CFormatState *state)
759 if (1 == gtk_tree_model_iter_n_children (GTK_TREE_MODEL (state->model), NULL))
760 cb_c_fmt_dialog_clear_clicked (button, state);
761 else {
762 GtkTreeIter iter;
763 if (gtk_tree_selection_get_selected (state->selection, NULL, &iter)) {
764 GtkTreePath *path = gtk_tree_model_get_path
765 (GTK_TREE_MODEL (state->model), &iter);
766 gint *ind = gtk_tree_path_get_indices (path);
767 if (ind) {
768 GnmStyleConditions *sc;
769 sc = gnm_style_conditions_dup
770 (gnm_style_get_conditions (state->style));
771 if (sc != NULL) {
772 gnm_style_conditions_delete (sc, *ind);
773 state->action.new_style = gnm_style_new ();
774 gnm_style_set_conditions
775 (state->action.new_style, sc);
776 state->action.existing_conds_only = TRUE;
778 c_fmt_dialog_set_conditions
779 (state,
780 _("Remove condition from conditional "
781 "formatting"));
783 gnm_style_unref (state->action.new_style);
784 state->action.new_style = NULL;
786 c_fmt_dialog_load (state);
789 gtk_tree_path_free (path);
794 static void
795 cb_c_fmt_dialog_expand_clicked (G_GNUC_UNUSED GtkButton *button, CFormatState *state)
797 GtkTreeIter iter;
798 if (!state->homogeneous && gtk_tree_selection_get_selected (state->selection, NULL, &iter)) {
799 GnmStyleConditions *sc;
800 gtk_tree_model_get (GTK_TREE_MODEL (state->model),
801 &iter,
802 CONDITIONS_REFERENCE, &sc,
803 -1);
804 if (sc != NULL) {
805 state->action.new_style = gnm_style_new ();
806 gnm_style_set_conditions
807 (state->action.new_style, sc);
808 state->action.existing_conds_only = FALSE;
810 c_fmt_dialog_set_conditions
811 (state,
812 _("Expand conditional formatting"));
814 gnm_style_unref (state->action.new_style);
815 state->action.new_style = NULL;
817 c_fmt_dialog_load (state);
822 static void
823 c_fmt_dialog_conditions_page_load_cond_single_f (CFormatState *state,
824 GnmExprTop const *texpr, GtkTreeIter *iter1)
826 char *formula;
827 GnmParsePos pp;
828 GtkTreeIter iter2;
830 gtk_tree_store_append (state->model, &iter2, iter1);
832 parse_pos_init_editpos (&pp, state->sv);
834 formula = gnm_expr_top_as_string (texpr, &pp, gnm_conventions_default);
835 gtk_tree_store_set (state->model, &iter2, CONDITIONS_RANGE, NULL,
836 CONDITIONS_COND, formula, CONDITIONS_REFERENCE, NULL, -1);
837 g_free (formula);
841 static void
842 c_fmt_dialog_conditions_page_load_cond_double_f (CFormatState *state,
843 GnmStyleCond const *cond, GtkTreeIter *iter1)
845 c_fmt_dialog_conditions_page_load_cond_single_f (state, gnm_style_cond_get_expr (cond, 0), iter1);
846 c_fmt_dialog_conditions_page_load_cond_single_f (state, gnm_style_cond_get_expr (cond, 1), iter1);
849 static void
850 c_fmt_dialog_conditions_page_load_cond (CFormatState *state, GnmStyleCond const *cond,
851 GtkTreeIter *iter)
853 GtkTreeIter iter1;
855 gtk_tree_store_append (state->model, &iter1, iter);
857 switch (cond->op) {
858 case GNM_STYLE_COND_BETWEEN:
859 gtk_tree_store_set (state->model, &iter1, CONDITIONS_RANGE, NULL,
860 CONDITIONS_COND,
861 _("If the cell content is between these "
862 "two values, a special style is used."),
863 CONDITIONS_REFERENCE, NULL, -1);
864 c_fmt_dialog_conditions_page_load_cond_double_f (state, cond, &iter1);
865 break;
866 case GNM_STYLE_COND_NOT_BETWEEN:
867 gtk_tree_store_set (state->model, &iter1, CONDITIONS_RANGE, NULL,
868 CONDITIONS_COND,
869 _("If the cell content is not between these"
870 " two values, a special style is used."),
871 CONDITIONS_REFERENCE, NULL, -1);
872 c_fmt_dialog_conditions_page_load_cond_double_f (state, cond, &iter1);
873 break;
874 case GNM_STYLE_COND_EQUAL:
875 gtk_tree_store_set (state->model, &iter1, CONDITIONS_RANGE, NULL,
876 CONDITIONS_COND,
877 _("If the cell content is equal to this value"
878 ", a special style is used."),
879 CONDITIONS_REFERENCE, NULL, -1);
880 c_fmt_dialog_conditions_page_load_cond_single_f (state, gnm_style_cond_get_expr (cond, 0), &iter1);
881 break;
882 case GNM_STYLE_COND_NOT_EQUAL:
883 gtk_tree_store_set (state->model, &iter1, CONDITIONS_RANGE, NULL,
884 CONDITIONS_COND,
885 _("If the cell content is not equal to this value"
886 ", a special style is used."),
887 CONDITIONS_REFERENCE, NULL, -1);
888 c_fmt_dialog_conditions_page_load_cond_single_f (state, gnm_style_cond_get_expr (cond, 0), &iter1);
889 break;
890 case GNM_STYLE_COND_GT:
891 gtk_tree_store_set (state->model, &iter1, CONDITIONS_RANGE, NULL,
892 CONDITIONS_COND,
893 _("If the cell content is > this value, a "
894 "special style is used."), -1);
895 c_fmt_dialog_conditions_page_load_cond_single_f (state, gnm_style_cond_get_expr (cond, 0), &iter1);
896 break;
897 case GNM_STYLE_COND_LT:
898 gtk_tree_store_set (state->model, &iter1, CONDITIONS_RANGE, NULL,
899 CONDITIONS_COND,
900 _("If the cell content is < this value, a "
901 "special style is used."),
902 CONDITIONS_REFERENCE, NULL, -1);
903 c_fmt_dialog_conditions_page_load_cond_single_f (state, gnm_style_cond_get_expr (cond, 0), &iter1);
904 break;
905 case GNM_STYLE_COND_GTE:
906 gtk_tree_store_set (state->model, &iter1, CONDITIONS_RANGE, NULL,
907 CONDITIONS_COND,
908 _("If the cell content is \xe2\x89\xa7 this "
909 "value, a special style is used."),
910 CONDITIONS_REFERENCE, NULL, -1);
912 c_fmt_dialog_conditions_page_load_cond_single_f (state, gnm_style_cond_get_expr (cond, 0), &iter1);
913 break;
914 case GNM_STYLE_COND_LTE:
915 gtk_tree_store_set (state->model, &iter1, CONDITIONS_RANGE, NULL,
916 CONDITIONS_COND,
917 _("If the cell content is \xe2\x89\xa6 this "
918 "value, a special style is used."),
919 CONDITIONS_REFERENCE, NULL, -1);
920 c_fmt_dialog_conditions_page_load_cond_single_f (state, gnm_style_cond_get_expr (cond, 0), &iter1);
921 break;
923 case GNM_STYLE_COND_CUSTOM:
924 gtk_tree_store_set (state->model, &iter1, CONDITIONS_RANGE, NULL,
925 CONDITIONS_COND,
926 _("If this formula evaluates to TRUE, a special style is used."),
927 CONDITIONS_REFERENCE, NULL, -1);
928 c_fmt_dialog_conditions_page_load_cond_single_f (state, gnm_style_cond_get_expr (cond, 0), &iter1);
929 break;
930 case GNM_STYLE_COND_CONTAINS_STR:
931 gtk_tree_store_set (state->model, &iter1, CONDITIONS_RANGE, NULL,
932 CONDITIONS_COND,
933 _("If the cell content contains this string"
934 ", a special style is used."),
935 CONDITIONS_REFERENCE, NULL, -1);
936 c_fmt_dialog_conditions_page_load_cond_single_f (state, gnm_style_cond_get_expr (cond, 0), &iter1);
937 break;
938 case GNM_STYLE_COND_NOT_CONTAINS_STR:
939 gtk_tree_store_set (state->model, &iter1, CONDITIONS_RANGE, NULL,
940 CONDITIONS_COND,
941 _("If the cell content does not contain this string"
942 ", a special style is used."),
943 CONDITIONS_REFERENCE, NULL, -1);
944 c_fmt_dialog_conditions_page_load_cond_single_f (state, gnm_style_cond_get_expr (cond, 0), &iter1);
945 break;
946 case GNM_STYLE_COND_BEGINS_WITH_STR:
947 gtk_tree_store_set (state->model, &iter1, CONDITIONS_RANGE, NULL,
948 CONDITIONS_COND,
949 _("If the cell content begins with this string"
950 ", a special style is used."),
951 CONDITIONS_REFERENCE, NULL, -1);
952 c_fmt_dialog_conditions_page_load_cond_single_f (state, gnm_style_cond_get_expr (cond, 0), &iter1);
953 break;
954 case GNM_STYLE_COND_NOT_BEGINS_WITH_STR:
955 gtk_tree_store_set (state->model, &iter1, CONDITIONS_RANGE, NULL,
956 CONDITIONS_COND,
957 _("If the cell content does not begin with this string,"
958 " a special style is used."), -1);
959 c_fmt_dialog_conditions_page_load_cond_single_f (state, gnm_style_cond_get_expr (cond, 0), &iter1);
960 break;
961 case GNM_STYLE_COND_ENDS_WITH_STR:
962 gtk_tree_store_set (state->model, &iter1, CONDITIONS_RANGE, NULL,
963 CONDITIONS_COND,
964 _("If the cell content ends with this string"
965 ", a special style is used."),
966 CONDITIONS_REFERENCE, NULL, -1);
967 c_fmt_dialog_conditions_page_load_cond_single_f (state, gnm_style_cond_get_expr (cond, 0), &iter1);
968 break;
969 case GNM_STYLE_COND_NOT_ENDS_WITH_STR:
970 gtk_tree_store_set (state->model, &iter1, CONDITIONS_RANGE, NULL,
971 CONDITIONS_COND,
972 _("If the cell content does not end "
973 "with this string, a special style is used."),
974 CONDITIONS_REFERENCE, NULL, -1);
975 c_fmt_dialog_conditions_page_load_cond_single_f (state, gnm_style_cond_get_expr (cond, 0), &iter1);
976 break;
977 case GNM_STYLE_COND_CONTAINS_ERR:
978 gtk_tree_store_set (state->model, &iter1, CONDITIONS_RANGE, NULL,
979 CONDITIONS_COND,
980 _("If the cell contains an error "
981 "value, a special style is used."), -1);
982 break;
983 case GNM_STYLE_COND_NOT_CONTAINS_ERR:
984 gtk_tree_store_set (state->model, &iter1, CONDITIONS_RANGE, NULL,
985 CONDITIONS_COND,
986 _("If the cell does not contain an error value"
987 ", a special style is used."),
988 CONDITIONS_REFERENCE, NULL, -1);
989 break;
990 case GNM_STYLE_COND_CONTAINS_BLANKS:
991 gtk_tree_store_set (state->model, &iter1, CONDITIONS_RANGE, NULL,
992 CONDITIONS_COND,
993 _("If the cell content "
994 "contains blanks, a special style is used."),
995 CONDITIONS_REFERENCE, NULL, -1);
996 break;
997 case GNM_STYLE_COND_NOT_CONTAINS_BLANKS:
998 gtk_tree_store_set (state->model, &iter1, CONDITIONS_RANGE, NULL,
999 CONDITIONS_COND,
1000 _("If the cell content does not contain blanks"
1001 ", a special style is used."),
1002 CONDITIONS_REFERENCE, NULL, -1);
1003 break;
1004 default:
1005 gtk_tree_store_set (state->model, &iter1, CONDITIONS_RANGE, NULL,
1006 CONDITIONS_COND,
1007 _("This is an unknown condition type."),
1008 CONDITIONS_REFERENCE, NULL, -1);
1009 return;
1013 static void
1014 c_fmt_dialog_conditions_page_load_conditions (GnmStyle *style, char const *range, CFormatState *state)
1016 GnmStyleConditions const *sc;
1017 GPtrArray const *conds;
1018 guint i;
1019 GtkTreeIter iter1, *iter;
1021 if (gnm_style_is_element_set (style, MSTYLE_CONDITIONS) &&
1022 NULL != (sc = gnm_style_get_conditions (style)) &&
1023 NULL != (conds = gnm_style_conditions_details (sc))) {
1024 if (range == NULL)
1025 iter = NULL;
1026 else {
1027 iter = &iter1;
1028 gtk_tree_store_append (state->model, iter, NULL);
1029 gtk_tree_store_set (state->model, iter, CONDITIONS_RANGE, range,
1030 CONDITIONS_COND, NULL,
1031 CONDITIONS_REFERENCE, sc, -1);
1033 for (i = 0 ; i < conds->len ; i++)
1034 c_fmt_dialog_conditions_page_load_cond
1035 (state, g_ptr_array_index (conds, i), iter);
1040 static gboolean
1041 c_fmt_dialog_condition_collector (G_GNUC_UNUSED SheetView *sv, GnmRange const *range,
1042 gpointer user_data)
1044 CFormatState *state = user_data;
1045 GnmStyleList *l, *list = sheet_style_collect_conditions (state->sheet, range);
1047 for (l = list; l != NULL; l = l->next) {
1048 GnmStyleRegion const *sr = l->data;
1049 GnmRange r = *((GnmRange *) l->data);
1050 r.start.row += range->start.row;
1051 r.end.row += range->start.row;
1052 r.start.col += range->start.col;
1053 r.end.col += range->start.col;
1054 c_fmt_dialog_conditions_page_load_conditions
1055 (sr->style, range_as_string (&r), state);
1058 style_list_free (list);
1059 return TRUE;
1063 static gboolean
1064 c_fmt_dialog_selection_type (SheetView *sv,
1065 GnmRange const *range,
1066 gpointer user_data)
1068 GnmBorder *borders[GNM_STYLE_BORDER_EDGE_MAX] = {NULL};
1069 CFormatState *state = user_data;
1070 int i;
1071 GSList *merged = gnm_sheet_merge_get_overlap (sv->sheet, range);
1072 GnmRange r = *range;
1073 gboolean allow_multi =
1074 merged == NULL ||
1075 merged->next != NULL ||
1076 !range_equal ((GnmRange *)merged->data, range);
1079 g_slist_free (merged);
1081 /* allow_multi == FALSE && !is_singleton (range) means that we are in
1082 * an merge cell, use only the top left */
1083 if (!allow_multi) {
1084 if (r.start.col != r.end.col)
1085 r.end.col = r.start.col;
1086 if (range->start.row != range->end.row)
1087 r.end.row = r.start.row;
1090 state->conflicts = sheet_style_find_conflicts (state->sheet, &r,
1091 &(state->style), borders);
1093 for (i = GNM_STYLE_BORDER_TOP ; i < GNM_STYLE_BORDER_EDGE_MAX ; i++) {
1094 gnm_style_border_unref (borders[i]);
1097 return TRUE;
1100 static void
1101 c_fmt_dialog_load (CFormatState *state)
1103 gtk_tree_store_clear (state->model);
1104 if (state->style)
1105 gnm_style_unref (state->style);
1106 state->style = NULL;
1108 (void) sv_selection_foreach (state->sv,
1109 c_fmt_dialog_selection_type, state);
1111 state->homogeneous = !(state->conflicts & (1 << MSTYLE_CONDITIONS));
1113 if (state->homogeneous) {
1114 gtk_label_set_markup (state->label,
1115 _("The selection is homogeneous with "
1116 "respect to conditions."));
1117 if (state->style != NULL)
1118 c_fmt_dialog_conditions_page_load_conditions
1119 (state->style, NULL, state);
1120 gtk_tree_view_expand_all (state->treeview);
1121 } else {
1122 gtk_label_set_markup (state->label,
1123 _("The selection is <b>not</b> "
1124 "homogeneous "
1125 "with respect to conditions!"));
1126 (void) sv_selection_foreach (state->sv,
1127 c_fmt_dialog_condition_collector, state);
1129 gtk_tree_view_column_queue_resize
1130 (gtk_tree_view_get_column (state->treeview, CONDITIONS_RANGE));
1131 c_fmt_dialog_set_sensitive (state);
1134 static void
1135 cb_selection_changed (G_GNUC_UNUSED GtkTreeSelection *treeselection, CFormatState *state)
1137 c_fmt_dialog_set_sensitive (state);
1140 static gboolean
1141 cb_can_select (G_GNUC_UNUSED GtkTreeSelection *selection,
1142 G_GNUC_UNUSED GtkTreeModel *model,
1143 GtkTreePath *path,
1144 gboolean path_currently_selected,
1145 G_GNUC_UNUSED CFormatState *state)
1147 if (path_currently_selected)
1148 return TRUE;
1150 return (gtk_tree_path_get_depth (path) == 1);
1153 static gboolean
1154 cb_c_format_dialog_range (G_GNUC_UNUSED SheetView *sv, GnmRange const *range, GString *str)
1156 g_string_append (str, range_as_string (range));
1157 g_string_append (str, ", ");
1158 return TRUE;
1161 static void
1162 c_fmt_dialog_init_editor_page (CFormatState *state)
1164 GtkGrid *grid;
1166 state->editor.add_button = go_gtk_builder_get_widget (state->gui, "add-button");
1167 state->editor.replace_button = go_gtk_builder_get_widget (state->gui, "replace-button");
1168 state->editor.copy_button = go_gtk_builder_get_widget (state->gui, "copy-button");
1169 state->editor.edit_style_button = go_gtk_builder_get_widget (state->gui, "edit-style-button");
1170 state->editor.combo = go_gtk_builder_get_widget (state->gui, "condition-combo");
1171 grid = GTK_GRID (go_gtk_builder_get_widget (state->gui, "condition-grid"));
1172 state->editor.expr_x = GTK_WIDGET (gnm_expr_entry_new (state->wbcg, TRUE));
1173 gtk_grid_attach (grid, state->editor.expr_x, 1, 2, 2, 1);
1174 gtk_widget_set_hexpand (state->editor.expr_x, TRUE);
1175 gtk_widget_show(state->editor.expr_x);
1176 gnm_expr_entry_set_flags (GNM_EXPR_ENTRY (state->editor.expr_x),
1177 GNM_EE_SHEET_OPTIONAL |
1178 GNM_EE_CONSTANT_ALLOWED,
1179 GNM_EE_MASK);
1181 state->editor.expr_y = GTK_WIDGET (gnm_expr_entry_new (state->wbcg, TRUE));
1182 gtk_grid_attach (grid, state->editor.expr_y, 1, 3, 2, 1);
1183 gtk_widget_set_hexpand (state->editor.expr_y, TRUE);
1184 gtk_widget_show(state->editor.expr_y);
1185 gnm_expr_entry_set_flags (GNM_EXPR_ENTRY (state->editor.expr_y),
1186 GNM_EE_SHEET_OPTIONAL |
1187 GNM_EE_CONSTANT_ALLOWED,
1188 GNM_EE_MASK);
1190 state->editor.typestore = GTK_LIST_STORE (gtk_combo_box_get_model
1191 (GTK_COMBO_BOX (state->editor.combo)));
1192 c_fmt_dialog_chooser_load_combo (state);
1194 state->editor.style_label = go_gtk_builder_get_widget (state->gui, "style-label");
1195 gtk_label_set_text (GTK_LABEL (state->editor.style_label), _("(undefined)"));
1197 c_fmt_dialog_set_expr_sensitive (state);
1199 g_signal_connect (G_OBJECT (state->editor.add_button),
1200 "clicked",
1201 G_CALLBACK (cb_c_fmt_dialog_add_button), state);
1202 g_signal_connect (G_OBJECT (state->editor.replace_button),
1203 "clicked",
1204 G_CALLBACK (cb_c_fmt_dialog_replace_button), state);
1205 g_signal_connect (G_OBJECT (state->editor.copy_button),
1206 "clicked",
1207 G_CALLBACK (cb_c_fmt_dialog_copy_button), state);
1208 g_signal_connect (G_OBJECT (state->editor.edit_style_button),
1209 "clicked",
1210 G_CALLBACK (cb_c_fmt_dialog_edit_style_button), state);
1211 g_signal_connect (G_OBJECT (state->editor.combo),
1212 "changed",
1213 G_CALLBACK (cb_c_fmt_dialog_chooser_type_changed), state);
1214 g_signal_connect (G_OBJECT (gnm_expr_entry_get_entry (GNM_EXPR_ENTRY (state->editor.expr_x))),
1215 "focus-out-event",
1216 G_CALLBACK (cb_c_fmt_dialog_chooser_entry_changed), state);
1217 g_signal_connect (G_OBJECT (gnm_expr_entry_get_entry (GNM_EXPR_ENTRY (state->editor.expr_y))),
1218 "focus-out-event",
1219 G_CALLBACK (cb_c_fmt_dialog_chooser_entry_changed), state);
1223 static void
1224 c_fmt_dialog_init_conditions_page (CFormatState *state)
1226 GtkTreeViewColumn * column;
1227 GtkCellRenderer *renderer;
1228 GtkLabel *hl;
1229 GString *str;
1231 g_return_if_fail (state != NULL);
1233 state->remove = GTK_BUTTON (go_gtk_builder_get_widget (state->gui,
1234 "conditions_remove"));
1235 gtk_widget_set_sensitive (GTK_WIDGET (state->remove), FALSE);
1236 state->clear = GTK_BUTTON (go_gtk_builder_get_widget (state->gui,
1237 "conditions_clear"));
1238 gtk_widget_set_sensitive (GTK_WIDGET (state->clear), FALSE);
1239 state->expand = GTK_BUTTON (go_gtk_builder_get_widget (state->gui,
1240 "conditions_expand"));
1241 gtk_widget_set_sensitive (GTK_WIDGET (state->expand), FALSE);
1243 state->model = gtk_tree_store_new (CONDITIONS_NUM_COLUMNS,
1244 G_TYPE_STRING,
1245 G_TYPE_STRING,
1246 G_TYPE_OBJECT);
1247 state->treeview = GTK_TREE_VIEW (go_gtk_builder_get_widget
1248 (state->gui, "conditions_treeview"));
1249 gtk_tree_view_set_fixed_height_mode (state->treeview, FALSE);
1250 gtk_tree_view_set_model (state->treeview, GTK_TREE_MODEL (state->model));
1251 g_object_unref (state->model);
1252 state->selection = gtk_tree_view_get_selection (state->treeview);
1253 gtk_tree_selection_set_mode (state->selection, GTK_SELECTION_SINGLE);
1254 gtk_tree_selection_set_select_function (state->selection,
1255 (GtkTreeSelectionFunc) cb_can_select,
1256 state, NULL);
1257 renderer = gtk_cell_renderer_text_new ();
1258 column = gtk_tree_view_column_new_with_attributes
1259 ("Range", renderer, "text", CONDITIONS_RANGE, NULL);
1260 gtk_tree_view_insert_column (state->treeview, column, -1);
1261 renderer = gtk_cell_renderer_text_new ();
1262 column = gtk_tree_view_column_new_with_attributes
1263 ("Conditions", renderer, "text", CONDITIONS_COND, NULL);
1264 gtk_tree_view_insert_column (state->treeview, column, -1);
1265 gtk_tree_view_set_expander_column (state->treeview, column);
1267 state->label = GTK_LABEL (go_gtk_builder_get_widget (state->gui,
1268 "conditions_label"));
1269 hl = GTK_LABEL (go_gtk_builder_get_widget (state->gui, "header-label"));
1270 gtk_label_set_ellipsize (hl, PANGO_ELLIPSIZE_END);
1271 str = g_string_new (_("Editing conditional formatting: "));
1272 sv_selection_foreach (state->sv,
1273 (GnmSelectionFunc)cb_c_format_dialog_range,
1274 str);
1275 g_string_truncate (str, str->len -2);
1276 gtk_label_set_text(hl, str->str);
1277 g_string_free (str, TRUE);
1279 g_signal_connect (G_OBJECT (state->selection), "changed",
1280 G_CALLBACK (cb_selection_changed), state);
1281 g_signal_connect (G_OBJECT (state->remove), "clicked",
1282 G_CALLBACK (cb_c_fmt_dialog_remove_clicked), state);
1283 g_signal_connect (G_OBJECT (state->clear), "clicked",
1284 G_CALLBACK (cb_c_fmt_dialog_clear_clicked), state);
1285 g_signal_connect (G_OBJECT (state->expand), "clicked",
1286 G_CALLBACK (cb_c_fmt_dialog_expand_clicked), state);
1289 /*****************************************************************************/
1292 void
1293 dialog_cell_format_cond (WBCGtk *wbcg)
1295 GtkBuilder *gui;
1296 CFormatState *state;
1297 GtkWidget *dialog;
1299 g_return_if_fail (wbcg != NULL);
1301 gui = gnm_gtk_builder_load ("res:ui/cell-format-cond.ui", NULL, GO_CMD_CONTEXT (wbcg));
1302 if (gui == NULL)
1303 return;
1305 /* Initialize */
1306 state = g_new (CFormatState, 1);
1307 state->wbcg = wbcg;
1308 state->gui = gui;
1309 state->sv = wb_control_cur_sheet_view (GNM_WBC (wbcg));
1310 state->sheet = sv_sheet (state->sv);
1311 state->style = NULL;
1312 state->editor.style = NULL;
1313 state->editor.dialog = NULL;
1315 dialog = go_gtk_builder_get_widget (state->gui, "CellFormat");
1316 g_return_if_fail (dialog != NULL);
1318 gtk_window_set_title (GTK_WINDOW (dialog), _("Conditional Cell Formatting"));
1320 /* Initialize */
1321 state->dialog = GTK_DIALOG (dialog);
1323 c_fmt_dialog_init_conditions_page (state);
1324 c_fmt_dialog_init_editor_page (state);
1326 c_fmt_dialog_load (state);
1328 gnm_init_help_button (
1329 go_gtk_builder_get_widget (state->gui, "helpbutton"),
1330 GNUMERIC_HELP_LINK_CELL_FORMAT_COND);
1332 state->close_button = go_gtk_builder_get_widget (state->gui, "closebutton");
1333 g_signal_connect (G_OBJECT (state->close_button),
1334 "clicked",
1335 G_CALLBACK (cb_c_fmt_dialog_dialog_buttons), state);
1337 gnm_dialog_setup_destroy_handlers (GTK_DIALOG (dialog), state->wbcg,
1338 GNM_DIALOG_DESTROY_CURRENT_SHEET_REMOVED);
1340 /* a candidate for merging into attach guru */
1341 wbc_gtk_attach_guru (state->wbcg, GTK_WIDGET (state->dialog));
1342 g_object_set_data_full (G_OBJECT (state->dialog),
1343 "state", state, (GDestroyNotify)cb_c_fmt_dialog_dialog_destroy);
1344 g_signal_connect (G_OBJECT (dialog), "destroy",
1345 G_CALLBACK (cb_dialog_destroy), NULL);
1347 gnm_restore_window_geometry (GTK_WINDOW (state->dialog),
1348 CELL_FORMAT_KEY);
1350 go_gtk_nonmodal_dialog (wbcg_toplevel (state->wbcg),
1351 GTK_WINDOW (state->dialog));
1353 gtk_widget_show (GTK_WIDGET (state->dialog));