Compilation: don't compile dialogs separately.
[gnumeric.git] / src / dialogs / dialog-cell-format-cond.c
blob65b3e9ccca06cef94131e04231d5bad075f9a616
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.h"
24 #include "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>
46 #include <gtk/gtk.h>
48 #include <string.h>
50 #define CELL_FORMAT_KEY "cell-format-cond-dialog"
51 #define CELL_FORMAT_DEF_KEY "cell-format-cond-def-dialog"
53 typedef struct _CFormatState {
54 GtkBuilder *gui;
55 WBCGtk *wbcg;
56 GtkDialog *dialog;
57 GtkWidget *close_button;
59 Sheet *sheet;
60 SheetView *sv;
61 unsigned int conflicts;
62 gboolean homogeneous;
63 GnmStyle *style;
65 GtkButton *remove;
66 GtkButton *clear;
67 GtkButton *expand;
68 GtkLabel *label;
69 GtkTreeView *treeview;
70 GtkTreeStore *model;
71 GtkTreeSelection *selection;
73 struct {
74 GOUndo *undo;
75 GOUndo *redo;
76 int size;
77 GnmStyle *new_style;
78 GnmStyle *old_style;
79 gboolean existing_conds_only;
80 } action;
81 struct {
82 GtkWidget *edit_style_button;
83 GtkWidget *add_button;
84 GtkWidget *replace_button;
85 GtkWidget *copy_button;
86 GtkWidget *combo;
87 GtkWidget *expr_x;
88 GtkWidget *expr_y;
89 GtkListStore *typestore;
90 GnmStyle *style;
91 GtkWidget *style_label;
92 GtkDialog *dialog;
93 } editor;
94 } CFormatState;
96 enum {
97 CONDITIONS_RANGE,
98 CONDITIONS_COND,
99 CONDITIONS_REFERENCE,
100 CONDITIONS_NUM_COLUMNS
105 /*****************************************************************************/
106 static void c_fmt_dialog_load (CFormatState *state);
107 static void c_fmt_dialog_apply_add_choice (CFormatState *state, GnmStyleCond *cond, gboolean add);
109 /*****************************************************************************/
111 /* button handlers */
112 static void
113 cb_c_fmt_dialog_dialog_buttons (G_GNUC_UNUSED GtkWidget *btn, CFormatState *state)
115 /* users may accidentally click on 'close' before adding the formatting style see #733352 */
116 if (!gtk_widget_get_sensitive (GTK_WIDGET (state->editor.add_button)) ||
117 gtk_widget_get_sensitive (GTK_WIDGET (state->clear)) ||
118 go_gtk_query_yes_no (GTK_WINDOW (state->dialog), FALSE,
119 _("You did not add the defined conditional format."
120 " Do you really want to close the conditional formatting dialog?")))
121 gtk_widget_destroy (GTK_WIDGET (state->dialog));
124 /* Handler for destroy */
125 static void
126 cb_c_fmt_dialog_dialog_destroy (CFormatState *state)
128 if (state->editor.dialog) {
129 gtk_widget_destroy (GTK_WIDGET (state->editor.dialog));
130 state->editor.dialog = NULL;
132 if (state->editor.style)
133 gnm_style_unref (state->editor.style);
134 if (state->style)
135 gnm_style_unref (state->style);
136 g_object_unref (state->gui);
137 g_free (state);
140 static void
141 cb_dialog_destroy (GtkDialog *dialog)
143 g_object_set_data (G_OBJECT (dialog), "state", NULL);
146 /*****************************************************************************/
148 static void
149 c_fmt_dialog_set_sensitive (CFormatState *state)
151 gboolean ok = (state->editor.style != NULL && state->homogeneous);
152 GnmParsePos pp;
153 GtkTreeIter iter;
154 gboolean not_empty, selected;
156 not_empty = gtk_tree_model_get_iter_first
157 (GTK_TREE_MODEL (state->model), &iter);
158 selected = gtk_tree_selection_get_selected
159 (state->selection, NULL, NULL);
161 gtk_widget_set_sensitive (GTK_WIDGET (state->clear), not_empty);
163 gtk_widget_set_sensitive (GTK_WIDGET (state->remove),
164 state->homogeneous && selected);
165 gtk_widget_set_sensitive (GTK_WIDGET (state->expand),
166 (!state->homogeneous) && selected);
168 parse_pos_init_editpos (&pp, state->sv);
170 if (ok && gtk_widget_get_sensitive (state->editor.expr_x)) {
171 GnmExprTop const *texpr = gnm_expr_entry_parse (GNM_EXPR_ENTRY (state->editor.expr_x), &pp,
172 NULL, FALSE,
173 GNM_EXPR_PARSE_UNKNOWN_NAMES_ARE_STRINGS);
174 ok = (texpr != NULL);
175 if (texpr)
176 gnm_expr_top_unref (texpr);
178 if (ok && gtk_widget_get_sensitive (state->editor.expr_y)) {
179 GnmExprTop const *texpr = gnm_expr_entry_parse (GNM_EXPR_ENTRY (state->editor.expr_y), &pp,
180 NULL, FALSE,
181 GNM_EXPR_PARSE_UNKNOWN_NAMES_ARE_STRINGS);
182 ok = (texpr != NULL);
183 if (texpr)
184 gnm_expr_top_unref (texpr);
187 gtk_widget_set_sensitive (state->editor.add_button, ok);
188 gtk_widget_set_sensitive (state->editor.replace_button, ok && selected);
189 gtk_widget_set_sensitive (state->editor.copy_button, selected && state->homogeneous);
192 static void
193 c_fmt_dialog_set_expr_sensitive (CFormatState *state)
195 GtkTreeIter iter;
196 gint n_expr = 0;
198 if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (state->editor.combo), &iter))
199 gtk_tree_model_get (GTK_TREE_MODEL (state->editor.typestore),
200 &iter,
201 2, &n_expr,
202 -1);
203 if (n_expr < 1) {
204 gtk_widget_set_sensitive (state->editor.expr_x, FALSE);
205 gtk_entry_set_text (gnm_expr_entry_get_entry (GNM_EXPR_ENTRY (state->editor.expr_x)), "");
206 } else
207 gtk_widget_set_sensitive (state->editor.expr_x, TRUE);
208 if (n_expr < 2) {
209 gtk_widget_set_sensitive (state->editor.expr_y, FALSE);
210 gtk_entry_set_text (gnm_expr_entry_get_entry (GNM_EXPR_ENTRY (state->editor.expr_y)), "");
211 } else
212 gtk_widget_set_sensitive (state->editor.expr_y, TRUE);
215 static void
216 cb_c_fmt_dialog_chooser_type_changed (G_GNUC_UNUSED GtkComboBox *widget, CFormatState *state)
218 c_fmt_dialog_set_expr_sensitive (state);
219 c_fmt_dialog_set_sensitive (state);
222 static gboolean
223 cb_c_fmt_dialog_chooser_entry_changed (G_GNUC_UNUSED GnmExprEntry *widget, G_GNUC_UNUSED GdkEvent *event,
224 CFormatState *state)
226 c_fmt_dialog_set_sensitive (state);
227 return FALSE;
230 void
231 dialog_cell_format_style_added (gpointer closure, GnmStyle *style)
233 CFormatState *state = closure;
235 if (state->editor.style)
236 gnm_style_unref (state->editor.style);
237 state->editor.style = style;
238 gtk_label_set_text (GTK_LABEL (state->editor.style_label),
239 style ? _("(defined)") : _("(undefined)"));
240 c_fmt_dialog_set_sensitive (state);
243 static void
244 c_fmt_dialog_set_component (CFormatState *state, GnmStyle *overlay, gchar const *name,
245 GnmStyleElement elem, gboolean uncheck)
247 GtkWidget *w = go_gtk_builder_get_widget (state->gui, name);
249 if (gnm_style_is_element_set (overlay, elem))
250 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), TRUE);
251 else if (uncheck)
252 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), FALSE);
256 static gint
257 cb_c_fmt_dialog_chooser_check_page (CFormatState *state, gchar const *name,
258 gint page)
260 GtkWidget *w = go_gtk_builder_get_widget (state->gui, name);
262 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w)))
263 return (1 << page);
264 else
265 return 0;
268 static void
269 editor_destroy_cb (G_GNUC_UNUSED GObject *obj, CFormatState *state)
271 state->editor.dialog = NULL;
274 static void
275 c_fmt_dialog_select_style (CFormatState *state, int pages)
277 if (state->editor.dialog)
278 gtk_widget_destroy (GTK_WIDGET (state->editor.dialog));
279 state->editor.dialog = dialog_cell_format_select_style
280 (state->wbcg, pages,
281 GTK_WINDOW (state->dialog),
282 state->editor.style, state);
283 if (state->editor.dialog)
284 g_signal_connect
285 (G_OBJECT (state->editor.dialog),
286 "destroy", G_CALLBACK (editor_destroy_cb), state);
289 static void
290 cb_c_fmt_dialog_edit_style_button (G_GNUC_UNUSED GtkWidget *btn, CFormatState *state)
292 int pages = 0;
293 pages |= cb_c_fmt_dialog_chooser_check_page
294 (state, "check-background", FD_BACKGROUND);
295 pages |= cb_c_fmt_dialog_chooser_check_page
296 (state, "check-number", FD_NUMBER);
297 pages |= cb_c_fmt_dialog_chooser_check_page
298 (state, "check-align", FD_ALIGNMENT);
299 pages |= cb_c_fmt_dialog_chooser_check_page
300 (state, "check-font", FD_FONT);
301 pages |= cb_c_fmt_dialog_chooser_check_page
302 (state, "check-border", FD_BORDER);
303 pages |= cb_c_fmt_dialog_chooser_check_page
304 (state, "check-protection", FD_PROTECTION);
305 pages |= cb_c_fmt_dialog_chooser_check_page
306 (state, "check-validation", FD_VALIDATION);
308 if (state->editor.style != NULL)
309 gnm_style_ref (state->editor.style);
310 c_fmt_dialog_select_style (state, pages);
313 static GnmStyleCond *
314 c_fmt_dialog_get_condition (CFormatState *state)
316 GnmStyleCondOp op;
317 GnmStyleCond *cond;
318 GtkTreeIter iter;
319 gint n_expr = 0;
320 GnmParsePos pp;
321 GnmStyle *overlay;
323 parse_pos_init_editpos (&pp, state->sv);
325 overlay = gnm_style_new ();
326 if (state->editor.style) {
327 if (cb_c_fmt_dialog_chooser_check_page
328 (state, "check-background", FD_BACKGROUND)) {
329 gnm_style_merge_element (overlay, state->editor.style,
330 MSTYLE_COLOR_BACK);
331 gnm_style_merge_element (overlay, state->editor.style,
332 MSTYLE_COLOR_PATTERN);
333 gnm_style_merge_element (overlay, state->editor.style,
334 MSTYLE_PATTERN);
336 if (cb_c_fmt_dialog_chooser_check_page
337 (state, "check-number", FD_NUMBER)) {
338 gnm_style_merge_element (overlay, state->editor.style,
339 MSTYLE_FORMAT);
341 if (cb_c_fmt_dialog_chooser_check_page
342 (state, "check-align", FD_ALIGNMENT)) {
343 gnm_style_merge_element (overlay, state->editor.style,
344 MSTYLE_ALIGN_V);
345 gnm_style_merge_element (overlay, state->editor.style,
346 MSTYLE_ALIGN_H);
347 gnm_style_merge_element (overlay, state->editor.style,
348 MSTYLE_INDENT);
349 gnm_style_merge_element (overlay, state->editor.style,
350 MSTYLE_ROTATION);
351 gnm_style_merge_element (overlay, state->editor.style,
352 MSTYLE_TEXT_DIR);
353 gnm_style_merge_element (overlay, state->editor.style,
354 MSTYLE_WRAP_TEXT);
355 gnm_style_merge_element (overlay, state->editor.style,
356 MSTYLE_SHRINK_TO_FIT);
358 if (cb_c_fmt_dialog_chooser_check_page
359 (state, "check-font", FD_FONT)) {
360 gnm_style_merge_element (overlay, state->editor.style,
361 MSTYLE_FONT_COLOR);
362 gnm_style_merge_element (overlay, state->editor.style,
363 MSTYLE_FONT_NAME);
364 gnm_style_merge_element (overlay, state->editor.style,
365 MSTYLE_FONT_BOLD);
366 gnm_style_merge_element (overlay, state->editor.style,
367 MSTYLE_FONT_ITALIC);
368 gnm_style_merge_element (overlay, state->editor.style,
369 MSTYLE_FONT_UNDERLINE);
370 gnm_style_merge_element (overlay, state->editor.style,
371 MSTYLE_FONT_STRIKETHROUGH);
372 gnm_style_merge_element (overlay, state->editor.style,
373 MSTYLE_FONT_SCRIPT);
374 gnm_style_merge_element (overlay, state->editor.style,
375 MSTYLE_FONT_SIZE);
377 if (cb_c_fmt_dialog_chooser_check_page
378 (state, "check-border", FD_BORDER)) {
379 gnm_style_merge_element (overlay, state->editor.style,
380 MSTYLE_BORDER_TOP);
381 gnm_style_merge_element (overlay, state->editor.style,
382 MSTYLE_BORDER_BOTTOM);
383 gnm_style_merge_element (overlay, state->editor.style,
384 MSTYLE_BORDER_LEFT);
385 gnm_style_merge_element (overlay, state->editor.style,
386 MSTYLE_BORDER_RIGHT);
387 gnm_style_merge_element (overlay, state->editor.style,
388 MSTYLE_BORDER_REV_DIAGONAL);
389 gnm_style_merge_element (overlay, state->editor.style,
390 MSTYLE_BORDER_DIAGONAL);
392 if (cb_c_fmt_dialog_chooser_check_page
393 (state, "check-protection", FD_PROTECTION)) {
396 if (cb_c_fmt_dialog_chooser_check_page
397 (state, "check-validation", FD_VALIDATION)) {
401 if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (state->editor.combo), &iter))
402 gtk_tree_model_get (GTK_TREE_MODEL (state->editor.typestore),
403 &iter,
404 1, &op,
405 2, &n_expr,
406 -1);
407 else
408 op = GNM_STYLE_COND_CONTAINS_ERR;
410 cond = gnm_style_cond_new (op, state->sheet);
411 gnm_style_cond_set_overlay (cond, overlay);
412 gnm_style_unref (overlay);
414 if (n_expr > 0) {
415 GnmExprTop const *texpr = gnm_expr_entry_parse (GNM_EXPR_ENTRY (state->editor.expr_x), &pp,
416 NULL, FALSE,
417 GNM_EXPR_PARSE_UNKNOWN_NAMES_ARE_STRINGS);
418 gnm_style_cond_set_expr (cond, texpr, 0);
419 gnm_expr_top_unref (texpr);
421 if (n_expr > 1) {
422 GnmExprTop const *texpr = gnm_expr_entry_parse (GNM_EXPR_ENTRY (state->editor.expr_y), &pp,
423 NULL, FALSE,
424 GNM_EXPR_PARSE_UNKNOWN_NAMES_ARE_STRINGS);
425 gnm_style_cond_set_expr (cond, texpr, 1);
426 gnm_expr_top_unref (texpr);
428 return cond;
431 static void
432 cb_c_fmt_dialog_add_button (G_GNUC_UNUSED GtkWidget *btn, CFormatState *state)
434 GnmStyleCond *cond = c_fmt_dialog_get_condition (state);
435 c_fmt_dialog_apply_add_choice (state, cond, TRUE);
436 gnm_style_cond_free (cond);
439 static void
440 cb_c_fmt_dialog_replace_button (G_GNUC_UNUSED GtkWidget *btn, CFormatState *state)
442 GnmStyleCond *cond = c_fmt_dialog_get_condition (state);
443 c_fmt_dialog_apply_add_choice (state, cond, FALSE);
444 gnm_style_cond_free (cond);
447 static void
448 cb_c_fmt_dialog_copy_button (G_GNUC_UNUSED GtkWidget *btn, CFormatState *state)
450 GnmStyleConditions *sc;
451 GtkTreeIter iter;
452 sc = gnm_style_get_conditions (state->style);
453 if (sc != NULL && gtk_tree_selection_get_selected (state->selection, NULL, &iter)) {
454 GtkTreePath *path = gtk_tree_model_get_path
455 (GTK_TREE_MODEL (state->model), &iter);
456 gint *pind = gtk_tree_path_get_indices (path);
457 GPtrArray const *conds = gnm_style_conditions_details (sc);
458 if (pind && conds) {
459 gint ind = *pind;
460 GnmStyleCond *gsc = g_ptr_array_index (conds, ind);
461 GtkTreeIter iter;
462 GnmParsePos pp;
463 GnmStyle *style;
464 GnmStyleConditions *conds;
466 /* Set the condition op */
467 if (gtk_tree_model_get_iter_first
468 (GTK_TREE_MODEL (state->editor.typestore), &iter)) {
469 do {
470 guint op;
471 gtk_tree_model_get (GTK_TREE_MODEL (state->editor.typestore),
472 &iter,
473 1, &op,
474 -1);
475 if (op == gsc->op) {
476 gtk_combo_box_set_active_iter
477 (GTK_COMBO_BOX (state->editor.combo), &iter);
478 break;
480 } while (gtk_tree_model_iter_next
481 (GTK_TREE_MODEL (state->editor.typestore), &iter));
483 /* Set the expressions */
484 parse_pos_init_editpos (&pp, state->sv);
485 if (gnm_style_cond_get_expr (gsc, 0))
486 gnm_expr_entry_load_from_expr (GNM_EXPR_ENTRY (state->editor.expr_x),
487 gnm_style_cond_get_expr (gsc, 0),
488 &pp);
489 else
490 gnm_expr_entry_load_from_text (GNM_EXPR_ENTRY (state->editor.expr_x),
491 "");
492 if (gnm_style_cond_get_expr (gsc, 1))
493 gnm_expr_entry_load_from_expr (GNM_EXPR_ENTRY (state->editor.expr_y),
494 gnm_style_cond_get_expr (gsc, 1),
495 &pp);
496 else
497 gnm_expr_entry_load_from_text (GNM_EXPR_ENTRY (state->editor.expr_y),
498 "");
499 /* Set the style */
500 conds = state->style
501 ? gnm_style_get_conditions (state->style)
502 : NULL;
503 if (conds)
504 style = gnm_style_dup
505 (gnm_style_get_cond_style (state->style, ind));
506 else {
507 style = gnm_style_new_default ();
508 gnm_style_merge (style, gsc->overlay);
510 dialog_cell_format_style_added (state, style);
511 /* Set the appl. style components */
512 c_fmt_dialog_set_component (state, gsc->overlay, "check-background",
513 MSTYLE_COLOR_BACK, TRUE);
514 c_fmt_dialog_set_component (state, gsc->overlay, "check-background",
515 MSTYLE_COLOR_PATTERN, FALSE);
516 c_fmt_dialog_set_component (state, gsc->overlay, "check-background",
517 MSTYLE_PATTERN, FALSE);
518 c_fmt_dialog_set_component (state, gsc->overlay, "check-number",
519 MSTYLE_FORMAT, TRUE);
520 c_fmt_dialog_set_component (state, gsc->overlay, "check-align",
521 MSTYLE_ALIGN_V, TRUE);
522 c_fmt_dialog_set_component (state, gsc->overlay, "check-align",
523 MSTYLE_ALIGN_H, FALSE);
524 c_fmt_dialog_set_component (state, gsc->overlay, "check-align",
525 MSTYLE_ROTATION, FALSE);
526 c_fmt_dialog_set_component (state, gsc->overlay, "check-align",
527 MSTYLE_INDENT, FALSE);
528 c_fmt_dialog_set_component (state, gsc->overlay, "check-align",
529 MSTYLE_TEXT_DIR, FALSE);
530 c_fmt_dialog_set_component (state, gsc->overlay, "check-align",
531 MSTYLE_WRAP_TEXT, FALSE);
532 c_fmt_dialog_set_component (state, gsc->overlay, "check-align",
533 MSTYLE_SHRINK_TO_FIT, FALSE);
534 c_fmt_dialog_set_component (state, gsc->overlay, "check-font",
535 MSTYLE_FONT_COLOR, TRUE);
536 c_fmt_dialog_set_component (state, gsc->overlay, "check-font",
537 MSTYLE_FONT_NAME, FALSE);
538 c_fmt_dialog_set_component (state, gsc->overlay, "check-font",
539 MSTYLE_FONT_BOLD, FALSE);
540 c_fmt_dialog_set_component (state, gsc->overlay, "check-font",
541 MSTYLE_FONT_ITALIC, FALSE);
542 c_fmt_dialog_set_component (state, gsc->overlay, "check-font",
543 MSTYLE_FONT_UNDERLINE, FALSE);
544 c_fmt_dialog_set_component (state, gsc->overlay, "check-font",
545 MSTYLE_FONT_STRIKETHROUGH, FALSE);
546 c_fmt_dialog_set_component (state, gsc->overlay, "check-font",
547 MSTYLE_FONT_SCRIPT, FALSE);
548 c_fmt_dialog_set_component (state, gsc->overlay, "check-font",
549 MSTYLE_FONT_SIZE, FALSE);
550 c_fmt_dialog_set_component (state, gsc->overlay, "check-border",
551 MSTYLE_BORDER_TOP, TRUE);
552 c_fmt_dialog_set_component (state, gsc->overlay, "check-border",
553 MSTYLE_BORDER_BOTTOM, FALSE);
554 c_fmt_dialog_set_component (state, gsc->overlay, "check-border",
555 MSTYLE_BORDER_LEFT, FALSE);
556 c_fmt_dialog_set_component (state, gsc->overlay, "check-border",
557 MSTYLE_BORDER_RIGHT, FALSE);
558 c_fmt_dialog_set_component (state, gsc->overlay, "check-border",
559 MSTYLE_BORDER_REV_DIAGONAL, FALSE);
560 c_fmt_dialog_set_component (state, gsc->overlay, "check-border",
561 MSTYLE_BORDER_DIAGONAL, FALSE);
563 gtk_tree_path_free (path);
567 static void
568 c_fmt_dialog_chooser_load_combo (CFormatState *state)
570 static struct {
571 char const *label;
572 gint type;
573 gint n_expressions;
574 } cond_types[] = {
575 /* without any expression */
576 { N_("Cell contains an error value."), GNM_STYLE_COND_CONTAINS_ERR, 0},
577 { N_("Cell does not contain an error value."), GNM_STYLE_COND_NOT_CONTAINS_ERR, 0},
578 { N_("Cell contains whitespace."), GNM_STYLE_COND_CONTAINS_BLANKS, 0},
579 { N_("Cell does not contain whitespace."), GNM_STYLE_COND_NOT_CONTAINS_BLANKS, 0},
580 /* with one expression */
581 { N_("Cell value is = x."), GNM_STYLE_COND_EQUAL, 1},
582 { N_("Cell value is \xe2\x89\xa0 x."), GNM_STYLE_COND_NOT_EQUAL, 1},
583 { N_("Cell value is > x."), GNM_STYLE_COND_GT, 1},
584 { N_("Cell value is < x."), GNM_STYLE_COND_LT, 1},
585 { N_("Cell value is \xe2\x89\xa7 x."), GNM_STYLE_COND_GTE, 1},
586 { N_("Cell value is \xe2\x89\xa6 x."), GNM_STYLE_COND_LTE, 1},
587 { N_("Expression x evaluates to TRUE."), GNM_STYLE_COND_CUSTOM, 1},
588 { N_("Cell contains the string x."), GNM_STYLE_COND_CONTAINS_STR, 1},
589 { N_("Cell does not contain the string x."), GNM_STYLE_COND_NOT_CONTAINS_STR, 1},
590 { N_("Cell value begins with the string x."), GNM_STYLE_COND_BEGINS_WITH_STR, 1},
591 { N_("Cell value does not begin with the string x."), GNM_STYLE_COND_NOT_BEGINS_WITH_STR, 1},
592 { N_("Cell value ends with the string x."), GNM_STYLE_COND_ENDS_WITH_STR, 1},
593 { N_("Cell value does not end with the string x."), GNM_STYLE_COND_NOT_ENDS_WITH_STR, 1},
594 /* with two expressions */
595 { N_("Cell value is between x and y (incl.)."), GNM_STYLE_COND_BETWEEN, 2},
596 { N_("Cell value is not between x and y (incl.)."), GNM_STYLE_COND_NOT_BETWEEN, 2}
598 guint i;
599 GtkCellRenderer *cell;
600 GtkTreeIter iter;
602 for (i = 0; i < G_N_ELEMENTS (cond_types); i++)
603 gtk_list_store_insert_with_values (state->editor.typestore,
604 NULL, G_MAXINT,
605 0, _(cond_types[i].label),
606 1, cond_types[i].type,
607 2, cond_types[i].n_expressions,
608 -1);
609 cell = gtk_cell_renderer_text_new();
610 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(state->editor.combo), cell, TRUE);
611 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(state->editor.combo), cell, "text", 0, NULL);
612 if (gtk_tree_model_get_iter_first
613 (GTK_TREE_MODEL (state->editor.typestore), &iter))
614 gtk_combo_box_set_active_iter (GTK_COMBO_BOX (state->editor.combo), &iter);
618 /*****************************************************************************/
619 /*****************************************************************************/
620 static gboolean
621 c_fmt_dialog_condition_setter (SheetView *sv, GnmRange const *range, CFormatState *state)
623 GnmSheetRange *sr = g_new (GnmSheetRange, 1);
624 sr->range = *range;
625 sr->sheet = sv->sheet;
626 state->action.redo = go_undo_combine
627 (state->action.redo,
628 sheet_apply_style_undo (sr, state->action.new_style));
629 sr = g_new (GnmSheetRange, 1);
630 sr->range = *range;
631 sr->sheet = sv->sheet;
632 state->action.undo = go_undo_combine
633 (sheet_apply_style_undo (sr, state->action.old_style),
634 state->action.undo);
635 state->action.size++;
636 return TRUE;
639 static gboolean
640 c_fmt_dialog_condition_setter_tiled (G_GNUC_UNUSED SheetView *sv, GnmRange const *range,
641 CFormatState *state)
643 GnmStyleList *l, *list;
644 if (state->action.existing_conds_only)
645 list = sheet_style_collect_conditions (state->sheet, range);
646 else
647 list = sheet_style_get_range (state->sheet, range);
648 for (l = list; l != NULL; l = l->next) {
649 GnmStyleConditions *old_cond;
650 GnmStyleRegion const *sr = l->data;
651 GnmRange r = *((GnmRange *) l->data);
653 r.start.row += range->start.row;
654 r.end.row += range->start.row;
655 r.start.col += range->start.col;
656 r.end.col += range->start.col;
657 state->action.old_style = gnm_style_new ();
658 if (gnm_style_is_element_set (sr->style, MSTYLE_CONDITIONS) &&
659 NULL != (old_cond = gnm_style_get_conditions (sr->style)))
660 gnm_style_set_conditions (state->action.old_style,
661 g_object_ref (old_cond));
662 else
663 gnm_style_set_conditions (state->action.old_style, NULL);
664 c_fmt_dialog_condition_setter (state->sv, &r, state);
665 gnm_style_unref (state->action.old_style);
666 state->action.old_style = NULL;
668 style_list_free (list);
669 return TRUE;
672 static void
673 c_fmt_dialog_set_conditions (CFormatState *state, char const *cmd_label)
675 GnmStyleConditions *old_cond;
677 state->action.undo = NULL;
678 state->action.redo = NULL;
679 state->action.size = 0;
681 if (state->homogeneous) {
682 state->action.old_style = gnm_style_new ();
683 old_cond = gnm_style_get_conditions (state->style);
684 gnm_style_set_conditions (state->action.old_style,
685 old_cond ? g_object_ref (old_cond) : NULL);
687 sv_selection_foreach (state->sv,
688 (GnmSelectionFunc)c_fmt_dialog_condition_setter,
689 state);
690 } else {
691 sv_selection_foreach (state->sv,
692 (GnmSelectionFunc)c_fmt_dialog_condition_setter_tiled,
693 state);
695 cmd_generic_with_size (GNM_WBC (state->wbcg), cmd_label,
696 state->action.size, state->action.undo, state->action.redo);
698 state->action.undo = NULL;
699 state->action.redo = NULL;
700 if (state->action.old_style) {
701 gnm_style_unref (state->action.old_style);
702 state->action.old_style = NULL;
706 static void
707 c_fmt_dialog_apply_add_choice (CFormatState *state, GnmStyleCond *cond, gboolean add)
709 if (cond != NULL) {
710 GnmStyleConditions *sc;
711 int index = -1;
712 sc = gnm_style_conditions_dup (gnm_style_get_conditions (state->style));
713 if (sc == NULL)
714 sc = gnm_style_conditions_new (state->sheet);
715 if (!add) {
716 GtkTreeIter iter;
717 if (gtk_tree_selection_get_selected (state->selection, NULL, &iter)) {
718 GtkTreePath *path = gtk_tree_model_get_path
719 (GTK_TREE_MODEL (state->model), &iter);
720 gint *ind = gtk_tree_path_get_indices (path);
721 if (ind) {
722 gnm_style_conditions_delete (sc, *ind);
723 index = *ind;
725 gtk_tree_path_free (path);
728 gnm_style_conditions_insert (sc, cond, index);
729 state->action.new_style = gnm_style_new ();
730 gnm_style_set_conditions (state->action.new_style, sc);
731 state->action.existing_conds_only = FALSE;
733 c_fmt_dialog_set_conditions (state, _("Set conditional formatting"));
735 gnm_style_unref (state->action.new_style);
736 state->action.new_style = NULL;
738 c_fmt_dialog_load (state);
742 static void
743 cb_c_fmt_dialog_clear_clicked (G_GNUC_UNUSED GtkButton *button, CFormatState *state)
745 state->action.new_style = gnm_style_new ();
746 gnm_style_set_conditions (state->action.new_style, NULL);
747 state->action.existing_conds_only = TRUE;
749 c_fmt_dialog_set_conditions (state, _("Clear conditional formatting"));
751 gnm_style_unref (state->action.new_style);
752 state->action.new_style = NULL;
754 c_fmt_dialog_load (state);
757 static void
758 cb_c_fmt_dialog_remove_clicked (GtkButton *button, CFormatState *state)
760 if (1 == gtk_tree_model_iter_n_children (GTK_TREE_MODEL (state->model), NULL))
761 cb_c_fmt_dialog_clear_clicked (button, state);
762 else {
763 GtkTreeIter iter;
764 if (gtk_tree_selection_get_selected (state->selection, NULL, &iter)) {
765 GtkTreePath *path = gtk_tree_model_get_path
766 (GTK_TREE_MODEL (state->model), &iter);
767 gint *ind = gtk_tree_path_get_indices (path);
768 if (ind) {
769 GnmStyleConditions *sc;
770 sc = gnm_style_conditions_dup
771 (gnm_style_get_conditions (state->style));
772 if (sc != NULL) {
773 gnm_style_conditions_delete (sc, *ind);
774 state->action.new_style = gnm_style_new ();
775 gnm_style_set_conditions
776 (state->action.new_style, sc);
777 state->action.existing_conds_only = TRUE;
779 c_fmt_dialog_set_conditions
780 (state,
781 _("Remove condition from conditional "
782 "formatting"));
784 gnm_style_unref (state->action.new_style);
785 state->action.new_style = NULL;
787 c_fmt_dialog_load (state);
790 gtk_tree_path_free (path);
795 static void
796 cb_c_fmt_dialog_expand_clicked (G_GNUC_UNUSED GtkButton *button, CFormatState *state)
798 GtkTreeIter iter;
799 if (!state->homogeneous && gtk_tree_selection_get_selected (state->selection, NULL, &iter)) {
800 GnmStyleConditions *sc;
801 gtk_tree_model_get (GTK_TREE_MODEL (state->model),
802 &iter,
803 CONDITIONS_REFERENCE, &sc,
804 -1);
805 if (sc != NULL) {
806 state->action.new_style = gnm_style_new ();
807 gnm_style_set_conditions
808 (state->action.new_style, sc);
809 state->action.existing_conds_only = FALSE;
811 c_fmt_dialog_set_conditions
812 (state,
813 _("Expand conditional formatting"));
815 gnm_style_unref (state->action.new_style);
816 state->action.new_style = NULL;
818 c_fmt_dialog_load (state);
823 static void
824 c_fmt_dialog_conditions_page_load_cond_single_f (CFormatState *state,
825 GnmExprTop const *texpr, GtkTreeIter *iter1)
827 char *formula;
828 GnmParsePos pp;
829 GtkTreeIter iter2;
831 gtk_tree_store_append (state->model, &iter2, iter1);
833 parse_pos_init_editpos (&pp, state->sv);
835 formula = gnm_expr_top_as_string (texpr, &pp, gnm_conventions_default);
836 gtk_tree_store_set (state->model, &iter2, CONDITIONS_RANGE, NULL,
837 CONDITIONS_COND, formula, CONDITIONS_REFERENCE, NULL, -1);
838 g_free (formula);
842 static void
843 c_fmt_dialog_conditions_page_load_cond_double_f (CFormatState *state,
844 GnmStyleCond const *cond, GtkTreeIter *iter1)
846 c_fmt_dialog_conditions_page_load_cond_single_f (state, gnm_style_cond_get_expr (cond, 0), iter1);
847 c_fmt_dialog_conditions_page_load_cond_single_f (state, gnm_style_cond_get_expr (cond, 1), iter1);
850 static void
851 c_fmt_dialog_conditions_page_load_cond (CFormatState *state, GnmStyleCond const *cond,
852 GtkTreeIter *iter)
854 GtkTreeIter iter1;
856 gtk_tree_store_append (state->model, &iter1, iter);
858 switch (cond->op) {
859 case GNM_STYLE_COND_BETWEEN:
860 gtk_tree_store_set (state->model, &iter1, CONDITIONS_RANGE, NULL,
861 CONDITIONS_COND,
862 _("If the cell content is between these "
863 "two values, a special style is used."),
864 CONDITIONS_REFERENCE, NULL, -1);
865 c_fmt_dialog_conditions_page_load_cond_double_f (state, cond, &iter1);
866 break;
867 case GNM_STYLE_COND_NOT_BETWEEN:
868 gtk_tree_store_set (state->model, &iter1, CONDITIONS_RANGE, NULL,
869 CONDITIONS_COND,
870 _("If the cell content is not between these"
871 " two values, a special style is used."),
872 CONDITIONS_REFERENCE, NULL, -1);
873 c_fmt_dialog_conditions_page_load_cond_double_f (state, cond, &iter1);
874 break;
875 case GNM_STYLE_COND_EQUAL:
876 gtk_tree_store_set (state->model, &iter1, CONDITIONS_RANGE, NULL,
877 CONDITIONS_COND,
878 _("If the cell content is equal to this value"
879 ", a special style is used."),
880 CONDITIONS_REFERENCE, NULL, -1);
881 c_fmt_dialog_conditions_page_load_cond_single_f (state, gnm_style_cond_get_expr (cond, 0), &iter1);
882 break;
883 case GNM_STYLE_COND_NOT_EQUAL:
884 gtk_tree_store_set (state->model, &iter1, CONDITIONS_RANGE, NULL,
885 CONDITIONS_COND,
886 _("If the cell content is not equal to this value"
887 ", a special style is used."),
888 CONDITIONS_REFERENCE, NULL, -1);
889 c_fmt_dialog_conditions_page_load_cond_single_f (state, gnm_style_cond_get_expr (cond, 0), &iter1);
890 break;
891 case GNM_STYLE_COND_GT:
892 gtk_tree_store_set (state->model, &iter1, CONDITIONS_RANGE, NULL,
893 CONDITIONS_COND,
894 _("If the cell content is > this value, a "
895 "special style is used."), -1);
896 c_fmt_dialog_conditions_page_load_cond_single_f (state, gnm_style_cond_get_expr (cond, 0), &iter1);
897 break;
898 case GNM_STYLE_COND_LT:
899 gtk_tree_store_set (state->model, &iter1, CONDITIONS_RANGE, NULL,
900 CONDITIONS_COND,
901 _("If the cell content is < this value, a "
902 "special style is used."),
903 CONDITIONS_REFERENCE, NULL, -1);
904 c_fmt_dialog_conditions_page_load_cond_single_f (state, gnm_style_cond_get_expr (cond, 0), &iter1);
905 break;
906 case GNM_STYLE_COND_GTE:
907 gtk_tree_store_set (state->model, &iter1, CONDITIONS_RANGE, NULL,
908 CONDITIONS_COND,
909 _("If the cell content is \xe2\x89\xa7 this "
910 "value, a special style is used."),
911 CONDITIONS_REFERENCE, NULL, -1);
913 c_fmt_dialog_conditions_page_load_cond_single_f (state, gnm_style_cond_get_expr (cond, 0), &iter1);
914 break;
915 case GNM_STYLE_COND_LTE:
916 gtk_tree_store_set (state->model, &iter1, CONDITIONS_RANGE, NULL,
917 CONDITIONS_COND,
918 _("If the cell content is \xe2\x89\xa6 this "
919 "value, a special style is used."),
920 CONDITIONS_REFERENCE, NULL, -1);
921 c_fmt_dialog_conditions_page_load_cond_single_f (state, gnm_style_cond_get_expr (cond, 0), &iter1);
922 break;
924 case GNM_STYLE_COND_CUSTOM:
925 gtk_tree_store_set (state->model, &iter1, CONDITIONS_RANGE, NULL,
926 CONDITIONS_COND,
927 _("If this formula evaluates to TRUE, a special style is used."),
928 CONDITIONS_REFERENCE, NULL, -1);
929 c_fmt_dialog_conditions_page_load_cond_single_f (state, gnm_style_cond_get_expr (cond, 0), &iter1);
930 break;
931 case GNM_STYLE_COND_CONTAINS_STR:
932 gtk_tree_store_set (state->model, &iter1, CONDITIONS_RANGE, NULL,
933 CONDITIONS_COND,
934 _("If the cell content contains this string"
935 ", a special style is used."),
936 CONDITIONS_REFERENCE, NULL, -1);
937 c_fmt_dialog_conditions_page_load_cond_single_f (state, gnm_style_cond_get_expr (cond, 0), &iter1);
938 break;
939 case GNM_STYLE_COND_NOT_CONTAINS_STR:
940 gtk_tree_store_set (state->model, &iter1, CONDITIONS_RANGE, NULL,
941 CONDITIONS_COND,
942 _("If the cell content does not contain this string"
943 ", a special style is used."),
944 CONDITIONS_REFERENCE, NULL, -1);
945 c_fmt_dialog_conditions_page_load_cond_single_f (state, gnm_style_cond_get_expr (cond, 0), &iter1);
946 break;
947 case GNM_STYLE_COND_BEGINS_WITH_STR:
948 gtk_tree_store_set (state->model, &iter1, CONDITIONS_RANGE, NULL,
949 CONDITIONS_COND,
950 _("If the cell content begins with this string"
951 ", a special style is used."),
952 CONDITIONS_REFERENCE, NULL, -1);
953 c_fmt_dialog_conditions_page_load_cond_single_f (state, gnm_style_cond_get_expr (cond, 0), &iter1);
954 break;
955 case GNM_STYLE_COND_NOT_BEGINS_WITH_STR:
956 gtk_tree_store_set (state->model, &iter1, CONDITIONS_RANGE, NULL,
957 CONDITIONS_COND,
958 _("If the cell content does not begin with this string,"
959 " a special style is used."), -1);
960 c_fmt_dialog_conditions_page_load_cond_single_f (state, gnm_style_cond_get_expr (cond, 0), &iter1);
961 break;
962 case GNM_STYLE_COND_ENDS_WITH_STR:
963 gtk_tree_store_set (state->model, &iter1, CONDITIONS_RANGE, NULL,
964 CONDITIONS_COND,
965 _("If the cell content ends with this string"
966 ", a special style is used."),
967 CONDITIONS_REFERENCE, NULL, -1);
968 c_fmt_dialog_conditions_page_load_cond_single_f (state, gnm_style_cond_get_expr (cond, 0), &iter1);
969 break;
970 case GNM_STYLE_COND_NOT_ENDS_WITH_STR:
971 gtk_tree_store_set (state->model, &iter1, CONDITIONS_RANGE, NULL,
972 CONDITIONS_COND,
973 _("If the cell content does not end "
974 "with this string, a special style is used."),
975 CONDITIONS_REFERENCE, NULL, -1);
976 c_fmt_dialog_conditions_page_load_cond_single_f (state, gnm_style_cond_get_expr (cond, 0), &iter1);
977 break;
978 case GNM_STYLE_COND_CONTAINS_ERR:
979 gtk_tree_store_set (state->model, &iter1, CONDITIONS_RANGE, NULL,
980 CONDITIONS_COND,
981 _("If the cell contains an error "
982 "value, a special style is used."), -1);
983 break;
984 case GNM_STYLE_COND_NOT_CONTAINS_ERR:
985 gtk_tree_store_set (state->model, &iter1, CONDITIONS_RANGE, NULL,
986 CONDITIONS_COND,
987 _("If the cell does not contain an error value"
988 ", a special style is used."),
989 CONDITIONS_REFERENCE, NULL, -1);
990 break;
991 case GNM_STYLE_COND_CONTAINS_BLANKS:
992 gtk_tree_store_set (state->model, &iter1, CONDITIONS_RANGE, NULL,
993 CONDITIONS_COND,
994 _("If the cell content "
995 "contains blanks, a special style is used."),
996 CONDITIONS_REFERENCE, NULL, -1);
997 break;
998 case GNM_STYLE_COND_NOT_CONTAINS_BLANKS:
999 gtk_tree_store_set (state->model, &iter1, CONDITIONS_RANGE, NULL,
1000 CONDITIONS_COND,
1001 _("If the cell content does not contain blanks"
1002 ", a special style is used."),
1003 CONDITIONS_REFERENCE, NULL, -1);
1004 break;
1005 default:
1006 gtk_tree_store_set (state->model, &iter1, CONDITIONS_RANGE, NULL,
1007 CONDITIONS_COND,
1008 _("This is an unknown condition type."),
1009 CONDITIONS_REFERENCE, NULL, -1);
1010 return;
1014 static void
1015 c_fmt_dialog_conditions_page_load_conditions (GnmStyle *style, char const *range, CFormatState *state)
1017 GnmStyleConditions const *sc;
1018 GPtrArray const *conds;
1019 guint i;
1020 GtkTreeIter iter1, *iter;
1022 if (gnm_style_is_element_set (style, MSTYLE_CONDITIONS) &&
1023 NULL != (sc = gnm_style_get_conditions (style)) &&
1024 NULL != (conds = gnm_style_conditions_details (sc))) {
1025 if (range == NULL)
1026 iter = NULL;
1027 else {
1028 iter = &iter1;
1029 gtk_tree_store_append (state->model, iter, NULL);
1030 gtk_tree_store_set (state->model, iter, CONDITIONS_RANGE, range,
1031 CONDITIONS_COND, NULL,
1032 CONDITIONS_REFERENCE, sc, -1);
1034 for (i = 0 ; i < conds->len ; i++)
1035 c_fmt_dialog_conditions_page_load_cond
1036 (state, g_ptr_array_index (conds, i), iter);
1041 static gboolean
1042 c_fmt_dialog_condition_collector (G_GNUC_UNUSED SheetView *sv, GnmRange const *range,
1043 gpointer user_data)
1045 CFormatState *state = user_data;
1046 GnmStyleList *l, *list = sheet_style_collect_conditions (state->sheet, range);
1048 for (l = list; l != NULL; l = l->next) {
1049 GnmStyleRegion const *sr = l->data;
1050 GnmRange r = *((GnmRange *) l->data);
1051 r.start.row += range->start.row;
1052 r.end.row += range->start.row;
1053 r.start.col += range->start.col;
1054 r.end.col += range->start.col;
1055 c_fmt_dialog_conditions_page_load_conditions
1056 (sr->style, range_as_string (&r), state);
1059 style_list_free (list);
1060 return TRUE;
1064 static gboolean
1065 c_fmt_dialog_selection_type (SheetView *sv,
1066 GnmRange const *range,
1067 gpointer user_data)
1069 GnmBorder *borders[GNM_STYLE_BORDER_EDGE_MAX] = {NULL};
1070 CFormatState *state = user_data;
1071 int i;
1072 GSList *merged = gnm_sheet_merge_get_overlap (sv->sheet, range);
1073 GnmRange r = *range;
1074 gboolean allow_multi =
1075 merged == NULL ||
1076 merged->next != NULL ||
1077 !range_equal ((GnmRange *)merged->data, range);
1080 g_slist_free (merged);
1082 /* allow_multi == FALSE && !is_singleton (range) means that we are in
1083 * an merge cell, use only the top left */
1084 if (!allow_multi) {
1085 if (r.start.col != r.end.col)
1086 r.end.col = r.start.col;
1087 if (range->start.row != range->end.row)
1088 r.end.row = r.start.row;
1091 state->conflicts = sheet_style_find_conflicts (state->sheet, &r,
1092 &(state->style), borders);
1094 for (i = GNM_STYLE_BORDER_TOP ; i < GNM_STYLE_BORDER_EDGE_MAX ; i++) {
1095 gnm_style_border_unref (borders[i]);
1098 return TRUE;
1101 static void
1102 c_fmt_dialog_load (CFormatState *state)
1104 gtk_tree_store_clear (state->model);
1105 if (state->style)
1106 gnm_style_unref (state->style);
1107 state->style = NULL;
1109 (void) sv_selection_foreach (state->sv,
1110 c_fmt_dialog_selection_type, state);
1112 state->homogeneous = !(state->conflicts & (1 << MSTYLE_CONDITIONS));
1114 if (state->homogeneous) {
1115 gtk_label_set_markup (state->label,
1116 _("The selection is homogeneous with "
1117 "respect to conditions."));
1118 if (state->style != NULL)
1119 c_fmt_dialog_conditions_page_load_conditions
1120 (state->style, NULL, state);
1121 gtk_tree_view_expand_all (state->treeview);
1122 } else {
1123 gtk_label_set_markup (state->label,
1124 _("The selection is <b>not</b> "
1125 "homogeneous "
1126 "with respect to conditions!"));
1127 (void) sv_selection_foreach (state->sv,
1128 c_fmt_dialog_condition_collector, state);
1130 gtk_tree_view_column_queue_resize
1131 (gtk_tree_view_get_column (state->treeview, CONDITIONS_RANGE));
1132 c_fmt_dialog_set_sensitive (state);
1135 static void
1136 cb_selection_changed (G_GNUC_UNUSED GtkTreeSelection *treeselection, CFormatState *state)
1138 c_fmt_dialog_set_sensitive (state);
1141 static gboolean
1142 cb_can_select (G_GNUC_UNUSED GtkTreeSelection *selection,
1143 G_GNUC_UNUSED GtkTreeModel *model,
1144 GtkTreePath *path,
1145 gboolean path_currently_selected,
1146 G_GNUC_UNUSED CFormatState *state)
1148 if (path_currently_selected)
1149 return TRUE;
1151 return (gtk_tree_path_get_depth (path) == 1);
1154 static gboolean
1155 cb_c_format_dialog_range (G_GNUC_UNUSED SheetView *sv, GnmRange const *range, GString *str)
1157 g_string_append (str, range_as_string (range));
1158 g_string_append (str, ", ");
1159 return TRUE;
1162 static void
1163 c_fmt_dialog_init_editor_page (CFormatState *state)
1165 GtkGrid *grid;
1167 state->editor.add_button = go_gtk_builder_get_widget (state->gui, "add-button");
1168 state->editor.replace_button = go_gtk_builder_get_widget (state->gui, "replace-button");
1169 state->editor.copy_button = go_gtk_builder_get_widget (state->gui, "copy-button");
1170 state->editor.edit_style_button = go_gtk_builder_get_widget (state->gui, "edit-style-button");
1171 state->editor.combo = go_gtk_builder_get_widget (state->gui, "condition-combo");
1172 grid = GTK_GRID (go_gtk_builder_get_widget (state->gui, "condition-grid"));
1173 state->editor.expr_x = GTK_WIDGET (gnm_expr_entry_new (state->wbcg, TRUE));
1174 gtk_grid_attach (grid, state->editor.expr_x, 1, 2, 2, 1);
1175 gtk_widget_set_hexpand (state->editor.expr_x, TRUE);
1176 gtk_widget_show(state->editor.expr_x);
1177 gnm_expr_entry_set_flags (GNM_EXPR_ENTRY (state->editor.expr_x),
1178 GNM_EE_SHEET_OPTIONAL |
1179 GNM_EE_CONSTANT_ALLOWED,
1180 GNM_EE_MASK);
1182 state->editor.expr_y = GTK_WIDGET (gnm_expr_entry_new (state->wbcg, TRUE));
1183 gtk_grid_attach (grid, state->editor.expr_y, 1, 3, 2, 1);
1184 gtk_widget_set_hexpand (state->editor.expr_y, TRUE);
1185 gtk_widget_show(state->editor.expr_y);
1186 gnm_expr_entry_set_flags (GNM_EXPR_ENTRY (state->editor.expr_y),
1187 GNM_EE_SHEET_OPTIONAL |
1188 GNM_EE_CONSTANT_ALLOWED,
1189 GNM_EE_MASK);
1191 state->editor.typestore = GTK_LIST_STORE (gtk_combo_box_get_model
1192 (GTK_COMBO_BOX (state->editor.combo)));
1193 c_fmt_dialog_chooser_load_combo (state);
1195 state->editor.style_label = go_gtk_builder_get_widget (state->gui, "style-label");
1196 gtk_label_set_text (GTK_LABEL (state->editor.style_label), _("(undefined)"));
1198 c_fmt_dialog_set_expr_sensitive (state);
1200 g_signal_connect (G_OBJECT (state->editor.add_button),
1201 "clicked",
1202 G_CALLBACK (cb_c_fmt_dialog_add_button), state);
1203 g_signal_connect (G_OBJECT (state->editor.replace_button),
1204 "clicked",
1205 G_CALLBACK (cb_c_fmt_dialog_replace_button), state);
1206 g_signal_connect (G_OBJECT (state->editor.copy_button),
1207 "clicked",
1208 G_CALLBACK (cb_c_fmt_dialog_copy_button), state);
1209 g_signal_connect (G_OBJECT (state->editor.edit_style_button),
1210 "clicked",
1211 G_CALLBACK (cb_c_fmt_dialog_edit_style_button), state);
1212 g_signal_connect (G_OBJECT (state->editor.combo),
1213 "changed",
1214 G_CALLBACK (cb_c_fmt_dialog_chooser_type_changed), state);
1215 g_signal_connect (G_OBJECT (gnm_expr_entry_get_entry (GNM_EXPR_ENTRY (state->editor.expr_x))),
1216 "focus-out-event",
1217 G_CALLBACK (cb_c_fmt_dialog_chooser_entry_changed), state);
1218 g_signal_connect (G_OBJECT (gnm_expr_entry_get_entry (GNM_EXPR_ENTRY (state->editor.expr_y))),
1219 "focus-out-event",
1220 G_CALLBACK (cb_c_fmt_dialog_chooser_entry_changed), state);
1224 static void
1225 c_fmt_dialog_init_conditions_page (CFormatState *state)
1227 GtkTreeViewColumn * column;
1228 GtkCellRenderer *renderer;
1229 GtkLabel *hl;
1230 GString *str;
1232 g_return_if_fail (state != NULL);
1234 state->remove = GTK_BUTTON (go_gtk_builder_get_widget (state->gui,
1235 "conditions_remove"));
1236 gtk_widget_set_sensitive (GTK_WIDGET (state->remove), FALSE);
1237 state->clear = GTK_BUTTON (go_gtk_builder_get_widget (state->gui,
1238 "conditions_clear"));
1239 gtk_widget_set_sensitive (GTK_WIDGET (state->clear), FALSE);
1240 state->expand = GTK_BUTTON (go_gtk_builder_get_widget (state->gui,
1241 "conditions_expand"));
1242 gtk_widget_set_sensitive (GTK_WIDGET (state->expand), FALSE);
1244 state->model = gtk_tree_store_new (CONDITIONS_NUM_COLUMNS,
1245 G_TYPE_STRING,
1246 G_TYPE_STRING,
1247 G_TYPE_OBJECT);
1248 state->treeview = GTK_TREE_VIEW (go_gtk_builder_get_widget
1249 (state->gui, "conditions_treeview"));
1250 gtk_tree_view_set_fixed_height_mode (state->treeview, FALSE);
1251 gtk_tree_view_set_model (state->treeview, GTK_TREE_MODEL (state->model));
1252 g_object_unref (state->model);
1253 state->selection = gtk_tree_view_get_selection (state->treeview);
1254 gtk_tree_selection_set_mode (state->selection, GTK_SELECTION_SINGLE);
1255 gtk_tree_selection_set_select_function (state->selection,
1256 (GtkTreeSelectionFunc) cb_can_select,
1257 state, NULL);
1258 renderer = gtk_cell_renderer_text_new ();
1259 column = gtk_tree_view_column_new_with_attributes
1260 ("Range", renderer, "text", CONDITIONS_RANGE, NULL);
1261 gtk_tree_view_insert_column (state->treeview, column, -1);
1262 renderer = gtk_cell_renderer_text_new ();
1263 column = gtk_tree_view_column_new_with_attributes
1264 ("Conditions", renderer, "text", CONDITIONS_COND, NULL);
1265 gtk_tree_view_insert_column (state->treeview, column, -1);
1266 gtk_tree_view_set_expander_column (state->treeview, column);
1268 state->label = GTK_LABEL (go_gtk_builder_get_widget (state->gui,
1269 "conditions_label"));
1270 hl = GTK_LABEL (go_gtk_builder_get_widget (state->gui, "header-label"));
1271 gtk_label_set_ellipsize (hl, PANGO_ELLIPSIZE_END);
1272 str = g_string_new (_("Editing conditional formatting: "));
1273 sv_selection_foreach (state->sv,
1274 (GnmSelectionFunc)cb_c_format_dialog_range,
1275 str);
1276 g_string_truncate (str, str->len -2);
1277 gtk_label_set_text(hl, str->str);
1278 g_string_free (str, TRUE);
1280 g_signal_connect (G_OBJECT (state->selection), "changed",
1281 G_CALLBACK (cb_selection_changed), state);
1282 g_signal_connect (G_OBJECT (state->remove), "clicked",
1283 G_CALLBACK (cb_c_fmt_dialog_remove_clicked), state);
1284 g_signal_connect (G_OBJECT (state->clear), "clicked",
1285 G_CALLBACK (cb_c_fmt_dialog_clear_clicked), state);
1286 g_signal_connect (G_OBJECT (state->expand), "clicked",
1287 G_CALLBACK (cb_c_fmt_dialog_expand_clicked), state);
1290 /*****************************************************************************/
1293 void
1294 dialog_cell_format_cond (WBCGtk *wbcg)
1296 GtkBuilder *gui;
1297 CFormatState *state;
1298 GtkWidget *dialog;
1300 g_return_if_fail (wbcg != NULL);
1302 gui = gnm_gtk_builder_load ("res:ui/cell-format-cond.ui", NULL, GO_CMD_CONTEXT (wbcg));
1303 if (gui == NULL)
1304 return;
1306 /* Initialize */
1307 state = g_new (CFormatState, 1);
1308 state->wbcg = wbcg;
1309 state->gui = gui;
1310 state->sv = wb_control_cur_sheet_view (GNM_WBC (wbcg));
1311 state->sheet = sv_sheet (state->sv);
1312 state->style = NULL;
1313 state->editor.style = NULL;
1314 state->editor.dialog = NULL;
1316 dialog = go_gtk_builder_get_widget (state->gui, "CellFormat");
1317 g_return_if_fail (dialog != NULL);
1319 gtk_window_set_title (GTK_WINDOW (dialog), _("Conditional Cell Formatting"));
1321 /* Initialize */
1322 state->dialog = GTK_DIALOG (dialog);
1324 c_fmt_dialog_init_conditions_page (state);
1325 c_fmt_dialog_init_editor_page (state);
1327 c_fmt_dialog_load (state);
1329 gnm_init_help_button (
1330 go_gtk_builder_get_widget (state->gui, "helpbutton"),
1331 GNUMERIC_HELP_LINK_CELL_FORMAT_COND);
1333 state->close_button = go_gtk_builder_get_widget (state->gui, "closebutton");
1334 g_signal_connect (G_OBJECT (state->close_button),
1335 "clicked",
1336 G_CALLBACK (cb_c_fmt_dialog_dialog_buttons), state);
1338 gnm_dialog_setup_destroy_handlers (GTK_DIALOG (dialog), state->wbcg,
1339 GNM_DIALOG_DESTROY_CURRENT_SHEET_REMOVED);
1341 /* a candidate for merging into attach guru */
1342 wbc_gtk_attach_guru (state->wbcg, GTK_WIDGET (state->dialog));
1343 g_object_set_data_full (G_OBJECT (state->dialog),
1344 "state", state, (GDestroyNotify)cb_c_fmt_dialog_dialog_destroy);
1345 g_signal_connect (G_OBJECT (dialog), "destroy",
1346 G_CALLBACK (cb_dialog_destroy), NULL);
1348 gnm_restore_window_geometry (GTK_WINDOW (state->dialog),
1349 CELL_FORMAT_KEY);
1351 go_gtk_nonmodal_dialog (wbcg_toplevel (state->wbcg),
1352 GTK_WINDOW (state->dialog));
1354 gtk_widget_show (GTK_WIDGET (state->dialog));