1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * dialog-merge.c: Dialog to merge a list of data into a given range
7 * Andreas J. Guelzow <aguelzow@taliesin.ca>
9 * Copyright (C) 2002 Andreas J. Guelzow <aguelzow@taliesin.ca>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, see <https://www.gnu.org/licenses/>.
25 #include <gnumeric-config.h>
26 #include <glib/gi18n-lib.h>
33 #include <workbook-view.h>
39 #include <selection.h>
40 #include <widgets/gnumeric-expr-entry.h>
44 #define MERGE_KEY "merge-dialog"
51 GtkWidget
*warning_dialog
;
58 GtkWidget
*change_btn
;
59 GtkWidget
*delete_btn
;
61 GtkWidget
*cancel_btn
;
72 cb_merge_update_buttons (G_GNUC_UNUSED gpointer ignored
,
75 /* Note: ignored could be NULL or an expr-entry. */
77 GtkTreeSelection
*selection
= gtk_tree_view_get_selection (state
->list
);
78 gboolean has_selection
, has_data
, has_work
;
80 has_selection
= gtk_tree_selection_get_selected (selection
, NULL
, &iter
);
81 has_data
= gnm_expr_entry_is_cell_ref (state
->data
, state
->sheet
, TRUE
)
82 && gnm_expr_entry_is_cell_ref (state
->field
, state
->sheet
, FALSE
);
83 has_work
= (gtk_tree_model_iter_n_children (GTK_TREE_MODEL (state
->model
), NULL
) > 0)
84 && gnm_expr_entry_is_cell_ref (state
->zone
, state
->sheet
, TRUE
);
86 gtk_widget_set_sensitive (state
->add_btn
, has_data
);
87 gtk_widget_set_sensitive (state
->change_btn
, has_data
&& has_selection
);
88 gtk_widget_set_sensitive (state
->delete_btn
, has_selection
);
89 gtk_widget_set_sensitive (state
->merge_btn
, has_work
);
93 cb_merge_selection_changed (GtkTreeSelection
*selection
, MergeState
*state
)
96 gboolean has_selection
;
97 char *data_string
= NULL
, *field_string
= NULL
;
99 has_selection
= gtk_tree_selection_get_selected (selection
, NULL
, &iter
);
102 gtk_tree_model_get (GTK_TREE_MODEL (state
->model
), &iter
,
103 DATA_RANGE
, &data_string
,
104 FIELD_LOCATION
, &field_string
,
106 gnm_expr_entry_load_from_text (state
->data
, data_string
);
107 gnm_expr_entry_load_from_text (state
->field
, field_string
);
108 g_free (data_string
);
109 g_free (field_string
);
111 cb_merge_update_buttons (NULL
, state
);
115 merge_store_info_in_list (GtkTreeIter
*iter
, MergeState
*state
)
117 char *data_text
, *field_text
;
118 GtkTreeSelection
*selection
;
120 data_text
= gnm_expr_entry_global_range_name (state
->data
, state
->sheet
);
121 field_text
= gnm_expr_entry_global_range_name (state
->field
, state
->sheet
);
123 gtk_list_store_set (state
->model
, iter
,
124 DATA_RANGE
, data_text
,
125 FIELD_LOCATION
, field_text
,
131 selection
= gtk_tree_view_get_selection (state
->list
);
132 gtk_tree_selection_select_iter (selection
, iter
);
137 cb_merge_add_clicked (G_GNUC_UNUSED GtkWidget
*ignore
,
141 GtkTreeIter sel_iter
;
142 GtkTreeSelection
*selection
= gtk_tree_view_get_selection (state
->list
);
144 if (!gtk_tree_selection_get_selected (selection
, NULL
, &sel_iter
))
145 gtk_list_store_append (state
->model
, &iter
);
147 gtk_list_store_insert_before (state
->model
, &iter
, &sel_iter
);
149 merge_store_info_in_list (&iter
, state
);
153 cb_merge_change_clicked (G_GNUC_UNUSED GtkWidget
*ignore
,
157 GtkTreeSelection
*selection
= gtk_tree_view_get_selection (state
->list
);
159 if (gtk_tree_selection_get_selected (selection
, NULL
, &iter
))
160 merge_store_info_in_list (&iter
, state
);
164 cb_merge_delete_clicked (G_GNUC_UNUSED GtkWidget
*ignore
,
168 GtkTreeSelection
*selection
= gtk_tree_view_get_selection (state
->list
);
170 if (gtk_tree_selection_get_selected (selection
, NULL
, &iter
)) {
171 gtk_list_store_remove (state
->model
, &iter
);
176 cb_merge_cancel_clicked (G_GNUC_UNUSED GtkWidget
*ignore
,
179 gtk_widget_destroy (GTK_WIDGET (state
->dialog
));
183 cb_merge_find_shortest_column (gpointer data
, gpointer lp
)
185 GnmValue
*range
= data
;
187 int r_length
= range
->v_range
.cell
.b
.row
- range
->v_range
.cell
.a
.row
+ 1;
189 if (r_length
< *length
)
194 cb_merge_find_longest_column (gpointer data
, gpointer lp
)
196 GnmValue
*range
= data
;
198 int r_length
= range
->v_range
.cell
.b
.row
- range
->v_range
.cell
.a
.row
+ 1;
200 if (r_length
> *length
)
205 cb_merge_trim_data (gpointer data
, gpointer lp
)
207 GnmValue
*range
= data
;
209 int r_length
= range
->v_range
.cell
.b
.row
- range
->v_range
.cell
.a
.row
+ 1;
211 if (r_length
> *length
)
212 range
->v_range
.cell
.b
.row
= range
->v_range
.cell
.a
.row
+ *length
- 1;
213 range
->v_range
.cell
.b
.col
= range
->v_range
.cell
.a
.col
;
218 cb_merge_merge_clicked (G_GNUC_UNUSED GtkWidget
*ignore
,
221 GtkTreeIter this_iter
;
223 char *data_string
= NULL
, *field_string
= NULL
;
224 GSList
*data_list
= NULL
, *field_list
= NULL
;
226 gint field_problems
= 0;
227 gint min_length
= gnm_sheet_get_max_rows (state
->sheet
);
230 v_zone
= gnm_expr_entry_parse_as_value (state
->zone
, state
->sheet
);
231 g_return_if_fail (v_zone
!= NULL
);
233 while (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (state
->model
),
234 &this_iter
, NULL
, n
)) {
235 GnmValue
*v_data
, *v_field
;
236 gtk_tree_model_get (GTK_TREE_MODEL (state
->model
), &this_iter
,
237 DATA_RANGE
, &data_string
,
238 FIELD_LOCATION
, &field_string
,
240 v_data
= value_new_cellrange_str (state
->sheet
, data_string
);
241 v_field
= value_new_cellrange_str (state
->sheet
, field_string
);
242 g_free (data_string
);
243 g_free (field_string
);
245 g_return_if_fail (v_data
!= NULL
&& v_field
!= NULL
);
246 if (!global_range_contained (state
->sheet
, v_field
, v_zone
))
248 data_list
= g_slist_prepend (data_list
, v_data
);
249 field_list
= g_slist_prepend (field_list
, v_field
);
253 if (field_problems
> 0) {
255 if (field_problems
== 1)
256 text
= g_strdup (_("One field is not part of the merge zone!"));
258 text
= g_strdup_printf (_("%i fields are not part of the merge zone!"),
260 go_gtk_notice_nonmodal_dialog ((GtkWindow
*) state
->dialog
,
261 &(state
->warning_dialog
),
265 value_release (v_zone
);
266 range_list_destroy (data_list
);
267 range_list_destroy (field_list
);
271 g_slist_foreach (data_list
, cb_merge_find_shortest_column
, &min_length
);
272 g_slist_foreach (data_list
, cb_merge_find_longest_column
, &max_length
);
274 if (min_length
< max_length
) {
275 char *text
= g_strdup_printf (_("The data columns range in length from "
276 "%i to %i. Shall we trim the lengths to "
277 "%i and proceed?"), min_length
, max_length
,
280 if (go_gtk_query_yes_no (GTK_WINDOW (state
->dialog
), TRUE
,
282 g_slist_foreach (data_list
, cb_merge_trim_data
, &min_length
);
286 value_release (v_zone
);
287 range_list_destroy (data_list
);
288 range_list_destroy (field_list
);
294 if (!cmd_merge_data (GNM_WBC (state
->wbcg
), state
->sheet
,
295 v_zone
, field_list
, data_list
))
296 gtk_widget_destroy (state
->dialog
);
300 cb_merge_destroy (MergeState
*state
)
302 if (state
->model
!= NULL
)
303 g_object_unref (state
->model
);
304 if (state
->gui
!= NULL
)
305 g_object_unref (state
->gui
);
310 dialog_merge (WBCGtk
*wbcg
)
316 GtkTreeViewColumn
*column
;
317 GtkTreeSelection
*selection
;
320 g_return_if_fail (wbcg
!= NULL
);
322 if (gnm_dialog_raise_if_exists (wbcg
, MERGE_KEY
))
324 gui
= gnm_gtk_builder_load ("res:ui/merge.ui", NULL
, GO_CMD_CONTEXT (wbcg
));
328 state
= g_new0 (MergeState
, 1);
331 state
->sheet
= wb_control_cur_sheet (GNM_WBC (state
->wbcg
));
332 state
->dialog
= go_gtk_builder_get_widget (gui
, "Merge");
333 state
->warning_dialog
= NULL
;
335 state
->add_btn
= go_gtk_builder_get_widget (gui
, "add_button");
336 state
->delete_btn
= go_gtk_builder_get_widget (gui
, "remove_button");
337 state
->merge_btn
= go_gtk_builder_get_widget (gui
, "merge_button");
338 state
->change_btn
= go_gtk_builder_get_widget (gui
, "change_button");
339 state
->cancel_btn
= go_gtk_builder_get_widget (gui
, "cancel_button");
340 gtk_widget_set_size_request (state
->delete_btn
, 100, -1);
342 gtk_button_set_alignment (GTK_BUTTON (state
->add_btn
), 0., .5);
343 gtk_button_set_alignment (GTK_BUTTON (state
->delete_btn
), 0., .5);
344 gtk_button_set_alignment (GTK_BUTTON (state
->change_btn
), 0., .5);
346 grid
= GTK_GRID (go_gtk_builder_get_widget (gui
, "main-grid"));
347 state
->zone
= gnm_expr_entry_new (wbcg
, TRUE
);
348 gnm_expr_entry_set_flags (state
->zone
, GNM_EE_SINGLE_RANGE
, GNM_EE_MASK
);
349 gnm_editable_enters (GTK_WINDOW (state
->dialog
),
350 GTK_WIDGET (state
->zone
));
351 gtk_label_set_mnemonic_widget (GTK_LABEL (go_gtk_builder_get_widget (gui
, "var1-label")),
352 GTK_WIDGET (state
->zone
));
353 gtk_widget_set_hexpand (GTK_WIDGET (state
->zone
), TRUE
);
354 gtk_grid_attach (grid
, GTK_WIDGET (state
->zone
), 1, 0, 2, 1);
355 r
= selection_first_range (
356 wb_control_cur_sheet_view (GNM_WBC (wbcg
)), NULL
, NULL
);
358 gnm_expr_entry_load_from_range (state
->zone
,
361 state
->data
= gnm_expr_entry_new (wbcg
, TRUE
);
362 gnm_expr_entry_set_flags (state
->data
, GNM_EE_SINGLE_RANGE
, GNM_EE_MASK
);
363 gtk_widget_set_hexpand (GTK_WIDGET (state
->data
), TRUE
);
364 gtk_grid_attach (grid
, GTK_WIDGET (state
->data
), 0, 5, 1, 1);
366 state
->field
= gnm_expr_entry_new (wbcg
, TRUE
);
367 gnm_expr_entry_set_flags (state
->field
, GNM_EE_SINGLE_RANGE
, GNM_EE_MASK
);
368 gtk_widget_set_hexpand (GTK_WIDGET (state
->field
), TRUE
);
369 gtk_grid_attach (grid
, GTK_WIDGET (state
->field
), 1, 5, 1, 1);
371 scrolled
= go_gtk_builder_get_widget (state
->gui
, "scrolled");
372 state
->model
= gtk_list_store_new (NUM_COLUMNS
, G_TYPE_STRING
, G_TYPE_STRING
);
373 state
->list
= GTK_TREE_VIEW (gtk_tree_view_new_with_model
374 (GTK_TREE_MODEL (state
->model
)));
375 selection
= gtk_tree_view_get_selection (state
->list
);
376 gtk_tree_selection_set_mode (selection
, GTK_SELECTION_BROWSE
);
378 column
= gtk_tree_view_column_new_with_attributes (_("Input Data"),
379 gtk_cell_renderer_text_new (),
382 gtk_tree_view_column_set_sort_column_id (column
, DATA_RANGE
);
383 gtk_tree_view_column_set_min_width (column
, 150);
384 gtk_tree_view_append_column (state
->list
, column
);
385 column
= gtk_tree_view_column_new_with_attributes (_("Merge Field"),
386 gtk_cell_renderer_text_new (),
387 "text", FIELD_LOCATION
,
389 gtk_tree_view_column_set_sort_column_id (column
, FIELD_LOCATION
);
390 gtk_tree_view_column_set_min_width (column
, 100);
391 gtk_tree_view_append_column (state
->list
, column
);
393 gtk_tree_view_set_headers_clickable (state
->list
, TRUE
);
394 gtk_container_add (GTK_CONTAINER (scrolled
), GTK_WIDGET (state
->list
));
396 cb_merge_update_buttons (NULL
, state
);
397 g_signal_connect (selection
,
399 G_CALLBACK (cb_merge_selection_changed
), state
);
401 g_signal_connect_after (G_OBJECT (state
->zone
),
403 G_CALLBACK (cb_merge_update_buttons
), state
);
404 g_signal_connect_after (G_OBJECT (state
->data
),
406 G_CALLBACK (cb_merge_update_buttons
), state
);
407 g_signal_connect_after (G_OBJECT (state
->field
),
409 G_CALLBACK (cb_merge_update_buttons
), state
);
411 g_signal_connect (G_OBJECT (state
->add_btn
),
413 G_CALLBACK (cb_merge_add_clicked
), state
);
414 g_signal_connect (G_OBJECT (state
->change_btn
),
416 G_CALLBACK (cb_merge_change_clicked
), state
);
417 g_signal_connect (G_OBJECT (state
->delete_btn
),
419 G_CALLBACK (cb_merge_delete_clicked
), state
);
420 g_signal_connect (G_OBJECT (state
->merge_btn
),
422 G_CALLBACK (cb_merge_merge_clicked
), state
);
423 g_signal_connect (G_OBJECT (state
->cancel_btn
),
425 G_CALLBACK (cb_merge_cancel_clicked
), state
);
427 gnm_dialog_setup_destroy_handlers (GTK_DIALOG (state
->dialog
),
429 GNM_DIALOG_DESTROY_CURRENT_SHEET_REMOVED
);
431 gnm_init_help_button (
432 go_gtk_builder_get_widget (state
->gui
, "help_button"),
433 GNUMERIC_HELP_LINK_DATA_MERGE
);
435 gnm_keyed_dialog (state
->wbcg
, GTK_WINDOW (state
->dialog
),
438 /* a candidate for merging into attach guru */
439 g_object_set_data_full (G_OBJECT (state
->dialog
),
440 "state", state
, (GDestroyNotify
) cb_merge_destroy
);
441 go_gtk_nonmodal_dialog (wbcg_toplevel (state
->wbcg
),
442 GTK_WINDOW (state
->dialog
));
443 wbc_gtk_attach_guru (state
->wbcg
, GTK_WIDGET (state
->dialog
));
444 gtk_widget_show_all (GTK_WIDGET (state
->dialog
));