1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * dialog-consolidate.c : implementation of the consolidation dialog.
5 * Copyright (C) Almer S. Tigelaar <almer@gnome.org>
6 * Copyright (C) Andreas J. Guelzow <aguelzow@taliesin.ca>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, see <https://www.gnu.org/licenses/>.
22 #include <gnumeric-config.h>
23 #include <glib/gi18n-lib.h>
27 #include "dialogs/tool-dialogs.h"
30 #include <consolidate.h>
35 #include <sheet-view.h>
36 #include <selection.h>
37 #include <widgets/gnumeric-expr-entry.h>
38 #include <widgets/gnumeric-cell-renderer-expr-entry.h>
39 #include <widgets/gnm-dao.h>
41 #include <dialogs/dao-gui-utils.h>
42 #include <tools/dao.h>
48 #define CONSOLIDATE_KEY "consolidate-dialog"
58 GnmGenericToolState base
;
60 GtkComboBox
*function
;
62 GtkTreeView
*source_view
;
63 GtkTreeModel
*source_areas
;
64 GnumericCellRendererExprEntry
*cellrenderer
;
70 GtkCheckButton
*labels_row
;
71 GtkCheckButton
*labels_col
;
72 GtkCheckButton
*labels_copy
;
74 int areas_index
; /* Select index in sources clist */
75 char *construct_error
; /* If set an error occurred in construct_consolidate */
78 static void dialog_set_button_sensitivity (G_GNUC_UNUSED GtkWidget
*dummy
,
79 ConsolidateState
*state
);
84 * ensures that we have exactly 2 empty rows
89 adjust_source_areas (ConsolidateState
*state
)
95 if (gtk_tree_model_get_iter_first
96 (state
->source_areas
, &iter
)) {
100 gtk_tree_model_get (state
->source_areas
,
102 SOURCE_COLUMN
, &source
,
104 if (strlen(source
) == 0)
107 } while (gtk_tree_model_iter_next
108 (state
->source_areas
,&iter
));
111 for (i
= 0; i
< cnt_empty
; i
++) {
112 gtk_list_store_append (GTK_LIST_STORE(state
->source_areas
),
114 gtk_list_store_set (GTK_LIST_STORE(state
->source_areas
),
116 IS_EDITABLE_COLUMN
, TRUE
,
118 PIXMAP_COLUMN
, state
->pixmap
,
121 dialog_set_button_sensitivity (NULL
, state
);
125 * construct_consolidate:
127 * Builts a new Consolidate structure from the
128 * state of all the widgets in the dialog, this can
129 * be used to actually "execute" a consolidation
131 static GnmConsolidate
*
132 construct_consolidate (ConsolidateState
*state
, data_analysis_output_t
*dao
)
134 GnmConsolidate
*cs
= gnm_consolidate_new ();
135 GnmConsolidateMode mode
= 0;
137 GnmValue
*range_value
;
141 switch (gtk_combo_box_get_active (state
->function
)) {
142 case 0 : func
= "SUM"; break;
143 case 1 : func
= "MIN"; break;
144 case 2 : func
= "MAX"; break;
145 case 3 : func
= "AVERAGE"; break;
146 case 4 : func
= "COUNT"; break;
147 case 5 : func
= "PRODUCT"; break;
148 case 6 : func
= "STDEV"; break;
149 case 7 : func
= "STDEVP"; break;
150 case 8 : func
= "VAR"; break;
151 case 9 : func
= "VARP"; break;
154 g_warning ("Unknown function index!");
157 gnm_consolidate_set_function (cs
, gnm_func_lookup (func
, NULL
));
159 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (state
->labels_row
)))
160 mode
|= CONSOLIDATE_COL_LABELS
;
161 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (state
->labels_col
)))
162 mode
|= CONSOLIDATE_ROW_LABELS
;
164 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (state
->labels_copy
)))
165 mode
|= CONSOLIDATE_COPY_LABELS
;
166 if (!dao_put_formulas (dao
))
167 mode
|= CONSOLIDATE_PUT_VALUES
;
169 gnm_consolidate_set_mode (cs
, mode
);
171 g_return_val_if_fail (gtk_tree_model_iter_n_children
172 (state
->source_areas
,
175 has_iter
= gtk_tree_model_get_iter_first (state
->source_areas
,
177 g_return_val_if_fail (has_iter
, NULL
);
181 gtk_tree_model_get (state
->source_areas
,
183 SOURCE_COLUMN
, &source
,
185 if (strlen(source
) != 0) {
186 range_value
= value_new_cellrange_str (state
->base
.sheet
, source
);
188 if (range_value
== NULL
) {
189 state
->construct_error
= g_strdup_printf (
190 _("Specification %s "
191 "does not define a region"),
194 gnm_consolidate_free (cs
, FALSE
);
197 if (!gnm_consolidate_add_source (cs
, range_value
)) {
198 state
->construct_error
= g_strdup_printf (
199 _("Source region %s overlaps "
200 "with the destination region"),
203 gnm_consolidate_free (cs
, FALSE
);
208 } while (gtk_tree_model_iter_next
209 (state
->source_areas
,&iter
));
214 /***************************************************************************************/
217 * dialog_set_button_sensitivity:
223 dialog_set_button_sensitivity (G_GNUC_UNUSED GtkWidget
*dummy
,
224 ConsolidateState
*state
)
228 ready
= gnm_dao_is_ready (GNM_DAO (state
->base
.gdao
))
229 && (gtk_tree_model_iter_n_children
230 (state
->source_areas
, NULL
)> 2);
231 gtk_widget_set_sensitive (GTK_WIDGET (state
->base
.ok_button
), ready
);
236 cb_selection_changed (G_GNUC_UNUSED GtkTreeSelection
*ignored
,
237 ConsolidateState
*state
)
240 GtkTreeSelection
*selection
= gtk_tree_view_get_selection (state
->source_view
);
242 gtk_widget_set_sensitive (GTK_WIDGET(state
->delete),
243 gtk_tree_selection_get_selected (selection
, NULL
, &iter
));
248 cb_source_edited (G_GNUC_UNUSED GtkCellRendererText
*cell
,
251 ConsolidateState
*state
)
256 path
= gtk_tree_path_new_from_string (path_string
);
258 if (gtk_tree_model_get_iter (state
->source_areas
, &iter
, path
))
259 gtk_list_store_set (GTK_LIST_STORE(state
->source_areas
),
260 &iter
, SOURCE_COLUMN
, new_text
, -1);
262 g_warning ("Did not get a valid iterator");
264 gtk_tree_path_free (path
);
265 adjust_source_areas (state
);
269 cb_dialog_destroy (ConsolidateState
*state
)
271 if (state
->pixmap
!= NULL
)
272 g_object_unref (state
->pixmap
);
273 if (state
->construct_error
!= NULL
) {
274 g_warning ("The construct error was not freed, this should not happen!");
275 g_free (state
->construct_error
);
280 cb_consolidate_ok_clicked (GtkWidget
*button
, ConsolidateState
*state
)
283 data_analysis_output_t
*dao
;
285 if (state
->cellrenderer
->entry
)
286 gnumeric_cell_renderer_expr_entry_editing_done (
287 GTK_CELL_EDITABLE (state
->cellrenderer
->entry
),
288 state
->cellrenderer
);
290 if (state
->base
.warning_dialog
!= NULL
)
291 gtk_widget_destroy (state
->base
.warning_dialog
);
293 dao
= parse_output ((GnmGenericToolState
*)state
, NULL
);
294 cs
= construct_consolidate (state
, dao
);
297 * If something went wrong consolidate_construct
298 * return NULL and sets the state->construct_error to
299 * a suitable error message
302 go_gtk_notice_nonmodal_dialog (GTK_WINDOW (state
->base
.dialog
),
303 &state
->base
.warning_dialog
,
305 "%s", state
->construct_error
);
306 g_free (state
->construct_error
);
308 state
->construct_error
= NULL
;
313 if (gnm_consolidate_check_destination (cs
, dao
)) {
314 if (!cmd_analysis_tool (GNM_WBC (state
->base
.wbcg
),
316 dao
, cs
, gnm_tool_consolidate_engine
,
318 (button
== state
->base
.ok_button
))
319 gtk_widget_destroy (state
->base
.dialog
);
321 go_gtk_notice_nonmodal_dialog (GTK_WINDOW (state
->base
.dialog
),
322 &state
->base
.warning_dialog
,
324 _("The output range overlaps "
325 "with the input ranges."));
327 gnm_consolidate_free (cs
, FALSE
);
332 cb_source_changed (G_GNUC_UNUSED GtkEntry
*ignored
,
333 ConsolidateState
*state
)
335 g_return_if_fail (state
!= NULL
);
340 cb_clear_clicked (G_GNUC_UNUSED GtkButton
*button
,
341 ConsolidateState
*state
)
343 g_return_if_fail (state
!= NULL
);
345 if (state
->cellrenderer
->entry
)
346 gnumeric_cell_renderer_expr_entry_editing_done (
347 GTK_CELL_EDITABLE (state
->cellrenderer
->entry
),
348 state
->cellrenderer
);
350 gtk_list_store_clear (GTK_LIST_STORE(state
->source_areas
));
351 adjust_source_areas (state
);
353 dialog_set_button_sensitivity (NULL
, state
);
357 cb_delete_clicked (G_GNUC_UNUSED GtkButton
*button
,
358 ConsolidateState
*state
)
360 GtkTreeIter sel_iter
;
361 GtkTreeSelection
*selection
=
362 gtk_tree_view_get_selection (state
->source_view
);
364 if (state
->cellrenderer
->entry
)
365 gnumeric_cell_renderer_expr_entry_editing_done (
366 GTK_CELL_EDITABLE (state
->cellrenderer
->entry
),
367 state
->cellrenderer
);
368 if (!gtk_tree_selection_get_selected (selection
, NULL
, &sel_iter
))
370 gtk_list_store_remove (GTK_LIST_STORE(state
->source_areas
),
372 adjust_source_areas (state
);
374 dialog_set_button_sensitivity (NULL
, state
);
378 cb_labels_toggled (G_GNUC_UNUSED GtkCheckButton
*button
,
379 ConsolidateState
*state
)
381 gboolean copy_labels
=
382 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (state
->labels_row
)) ||
383 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (state
->labels_col
));
385 gtk_widget_set_sensitive (GTK_WIDGET (state
->labels_copy
), copy_labels
);
387 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (state
->labels_copy
), FALSE
);
390 /***************************************************************************************/
393 connect_signal_labels_toggled (ConsolidateState
*state
, GtkCheckButton
*button
)
395 g_signal_connect (G_OBJECT (button
),
397 G_CALLBACK (cb_labels_toggled
), state
);
401 setup_widgets (ConsolidateState
*state
, GtkBuilder
*gui
)
403 GtkTreeViewColumn
*column
;
404 GtkTreeSelection
*selection
;
405 GtkCellRenderer
*renderer
;
407 state
->function
= go_gtk_builder_combo_box_init_text (gui
, "function");
408 gtk_combo_box_set_active (state
->function
, 0);
410 /* Begin: Source Areas View*/
411 state
->source_view
= GTK_TREE_VIEW (go_gtk_builder_get_widget
414 state
->source_areas
= GTK_TREE_MODEL(gtk_list_store_new
419 gtk_tree_view_set_model (state
->source_view
,
420 state
->source_areas
);
421 g_object_unref (state
->source_areas
);
423 selection
= gtk_tree_view_get_selection
424 (state
->source_view
);
425 gtk_tree_selection_set_mode (selection
, GTK_SELECTION_BROWSE
);
427 renderer
= gnumeric_cell_renderer_expr_entry_new (state
->base
.wbcg
);
428 state
->cellrenderer
=
429 GNM_CELL_RENDERER_EXPR_ENTRY (renderer
);
430 column
= gtk_tree_view_column_new_with_attributes
432 "text", SOURCE_COLUMN
,
433 "editable", IS_EDITABLE_COLUMN
,
435 g_signal_connect (G_OBJECT (renderer
), "edited",
436 G_CALLBACK (cb_source_edited
), state
);
437 gtk_tree_view_column_set_expand (column
, TRUE
);
438 gtk_tree_view_append_column (state
->source_view
, column
);
439 column
= gtk_tree_view_column_new_with_attributes
440 ("", gtk_cell_renderer_pixbuf_new (),
441 "pixbuf", PIXMAP_COLUMN
, NULL
);
442 gtk_tree_view_append_column (state
->source_view
, column
);
443 /* End: Source Areas View*/
445 state
->clear
= GTK_BUTTON (go_gtk_builder_get_widget (gui
, "clear"));
446 state
->delete = GTK_BUTTON (go_gtk_builder_get_widget (gui
, "delete"));
448 state
->labels_row
= GTK_CHECK_BUTTON (go_gtk_builder_get_widget (gui
, "labels_row"));
449 state
->labels_col
= GTK_CHECK_BUTTON (go_gtk_builder_get_widget (gui
, "labels_col"));
450 state
->labels_copy
= GTK_CHECK_BUTTON (go_gtk_builder_get_widget (gui
, "labels_copy"));
452 cb_selection_changed (NULL
, state
);
453 g_signal_connect (selection
,
455 G_CALLBACK (cb_selection_changed
), state
);
456 g_signal_connect (G_OBJECT (state
->clear
),
458 G_CALLBACK (cb_clear_clicked
), state
);
459 g_signal_connect (G_OBJECT (state
->delete),
461 G_CALLBACK (cb_delete_clicked
), state
);
463 connect_signal_labels_toggled (state
, state
->labels_row
);
464 connect_signal_labels_toggled (state
, state
->labels_col
);
465 connect_signal_labels_toggled (state
, state
->labels_copy
);
470 add_source_area (SheetView
*sv
, GnmRange
const *r
, gpointer closure
)
472 ConsolidateState
*state
= closure
;
476 if (range_is_singleton (r
))
479 range_name
= global_range_name (sv_sheet (sv
), r
);
481 gtk_list_store_prepend (GTK_LIST_STORE(state
->source_areas
),
483 gtk_list_store_set (GTK_LIST_STORE(state
->source_areas
),
485 IS_EDITABLE_COLUMN
, TRUE
,
486 SOURCE_COLUMN
, range_name
,
487 PIXMAP_COLUMN
, state
->pixmap
,
495 * dialog_consolidate_tool_init:
498 * Create the dialog (guru).
502 dialog_consolidate_tool_init (ConsolidateState
*state
)
504 state
->areas_index
= -1;
506 setup_widgets (state
, state
->base
.gui
);
507 state
->pixmap
= go_gtk_widget_render_icon_pixbuf
508 (GTK_WIDGET (state
->base
.dialog
),
509 "gnumeric-exprentry",
510 GTK_ICON_SIZE_LARGE_TOOLBAR
);
512 /* Dynamic initialization */
513 cb_source_changed (NULL
, state
);
514 cb_labels_toggled (state
->labels_row
, state
);
517 * When there are non-singleton selections add them all to the
518 * source range list for convenience
520 sv_selection_foreach (state
->base
.sv
, &add_source_area
, state
);
522 adjust_source_areas (state
);
523 dialog_set_button_sensitivity (NULL
, state
);
524 state
->base
.state_destroy
= (state_destroy_t
)cb_dialog_destroy
;
528 dialog_consolidate (WBCGtk
*wbcg
)
530 ConsolidateState
*state
;
534 g_return_if_fail (wbcg
!= NULL
);
535 sv
= wb_control_cur_sheet_view (GNM_WBC (wbcg
));
536 sheet
= sv_sheet (sv
);
538 /* Only pop up one copy per workbook */
539 if (gnm_dialog_raise_if_exists (wbcg
, CONSOLIDATE_KEY
)) {
543 /* Primary static initialization */
544 state
= g_new0 (ConsolidateState
, 1);
546 if (dialog_tool_init ((GnmGenericToolState
*)state
, wbcg
, sheet
,
547 GNUMERIC_HELP_LINK_CONSOLIDATE
,
548 "res:ui/consolidate.ui", "Consolidate",
549 _("Could not create the Consolidate dialog."),
551 G_CALLBACK (cb_consolidate_ok_clicked
),
553 G_CALLBACK (dialog_set_button_sensitivity
),
557 gnm_dao_set_put (GNM_DAO (state
->base
.gdao
), TRUE
, TRUE
);
558 dialog_consolidate_tool_init (state
);
559 gtk_widget_show (GTK_WIDGET (state
->base
.dialog
));