2 * dialog-merge.c: Dialog to merge a list of data into a given range
6 * Andreas J. Guelzow <aguelzow@taliesin.ca>
8 * Copyright (C) 2002 Andreas J. Guelzow <aguelzow@taliesin.ca>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, see <https://www.gnu.org/licenses/>.
24 #include <gnumeric-config.h>
25 #include <glib/gi18n-lib.h>
27 #include <dialogs/dialogs.h>
28 #include <dialogs/help.h>
32 #include <workbook-view.h>
38 #include <selection.h>
39 #include <widgets/gnm-expr-entry.h>
42 #define MERGE_KEY "merge-dialog"
49 GtkWidget
*warning_dialog
;
56 GtkWidget
*change_btn
;
57 GtkWidget
*delete_btn
;
59 GtkWidget
*cancel_btn
;
70 cb_merge_update_buttons (G_GNUC_UNUSED gpointer ignored
,
73 /* Note: ignored could be NULL or an expr-entry. */
75 GtkTreeSelection
*selection
= gtk_tree_view_get_selection (state
->list
);
76 gboolean has_selection
, has_data
, has_work
;
78 has_selection
= gtk_tree_selection_get_selected (selection
, NULL
, &iter
);
79 has_data
= gnm_expr_entry_is_cell_ref (state
->data
, state
->sheet
, TRUE
)
80 && gnm_expr_entry_is_cell_ref (state
->field
, state
->sheet
, FALSE
);
81 has_work
= (gtk_tree_model_iter_n_children (GTK_TREE_MODEL (state
->model
), NULL
) > 0)
82 && gnm_expr_entry_is_cell_ref (state
->zone
, state
->sheet
, TRUE
);
84 gtk_widget_set_sensitive (state
->add_btn
, has_data
);
85 gtk_widget_set_sensitive (state
->change_btn
, has_data
&& has_selection
);
86 gtk_widget_set_sensitive (state
->delete_btn
, has_selection
);
87 gtk_widget_set_sensitive (state
->merge_btn
, has_work
);
91 cb_merge_selection_changed (GtkTreeSelection
*selection
, MergeState
*state
)
94 gboolean has_selection
;
95 char *data_string
= NULL
, *field_string
= NULL
;
97 has_selection
= gtk_tree_selection_get_selected (selection
, NULL
, &iter
);
100 gtk_tree_model_get (GTK_TREE_MODEL (state
->model
), &iter
,
101 DATA_RANGE
, &data_string
,
102 FIELD_LOCATION
, &field_string
,
104 gnm_expr_entry_load_from_text (state
->data
, data_string
);
105 gnm_expr_entry_load_from_text (state
->field
, field_string
);
106 g_free (data_string
);
107 g_free (field_string
);
109 cb_merge_update_buttons (NULL
, state
);
113 merge_store_info_in_list (GtkTreeIter
*iter
, MergeState
*state
)
115 char *data_text
, *field_text
;
116 GtkTreeSelection
*selection
;
118 data_text
= gnm_expr_entry_global_range_name (state
->data
, state
->sheet
);
119 field_text
= gnm_expr_entry_global_range_name (state
->field
, state
->sheet
);
121 gtk_list_store_set (state
->model
, iter
,
122 DATA_RANGE
, data_text
,
123 FIELD_LOCATION
, field_text
,
129 selection
= gtk_tree_view_get_selection (state
->list
);
130 gtk_tree_selection_select_iter (selection
, iter
);
135 cb_merge_add_clicked (G_GNUC_UNUSED GtkWidget
*ignore
,
139 GtkTreeIter sel_iter
;
140 GtkTreeSelection
*selection
= gtk_tree_view_get_selection (state
->list
);
142 if (!gtk_tree_selection_get_selected (selection
, NULL
, &sel_iter
))
143 gtk_list_store_append (state
->model
, &iter
);
145 gtk_list_store_insert_before (state
->model
, &iter
, &sel_iter
);
147 merge_store_info_in_list (&iter
, state
);
151 cb_merge_change_clicked (G_GNUC_UNUSED GtkWidget
*ignore
,
155 GtkTreeSelection
*selection
= gtk_tree_view_get_selection (state
->list
);
157 if (gtk_tree_selection_get_selected (selection
, NULL
, &iter
))
158 merge_store_info_in_list (&iter
, state
);
162 cb_merge_delete_clicked (G_GNUC_UNUSED GtkWidget
*ignore
,
166 GtkTreeSelection
*selection
= gtk_tree_view_get_selection (state
->list
);
168 if (gtk_tree_selection_get_selected (selection
, NULL
, &iter
)) {
169 gtk_list_store_remove (state
->model
, &iter
);
174 cb_merge_cancel_clicked (G_GNUC_UNUSED GtkWidget
*ignore
,
177 gtk_widget_destroy (GTK_WIDGET (state
->dialog
));
181 cb_merge_find_shortest_column (gpointer data
, gpointer lp
)
183 GnmValue
*range
= data
;
185 int r_length
= range
->v_range
.cell
.b
.row
- range
->v_range
.cell
.a
.row
+ 1;
187 if (r_length
< *length
)
192 cb_merge_find_longest_column (gpointer data
, gpointer lp
)
194 GnmValue
*range
= data
;
196 int r_length
= range
->v_range
.cell
.b
.row
- range
->v_range
.cell
.a
.row
+ 1;
198 if (r_length
> *length
)
203 cb_merge_trim_data (gpointer data
, gpointer lp
)
205 GnmValue
*range
= data
;
207 int r_length
= range
->v_range
.cell
.b
.row
- range
->v_range
.cell
.a
.row
+ 1;
209 if (r_length
> *length
)
210 range
->v_range
.cell
.b
.row
= range
->v_range
.cell
.a
.row
+ *length
- 1;
211 range
->v_range
.cell
.b
.col
= range
->v_range
.cell
.a
.col
;
216 cb_merge_merge_clicked (G_GNUC_UNUSED GtkWidget
*ignore
,
219 GtkTreeIter this_iter
;
221 char *data_string
= NULL
, *field_string
= NULL
;
222 GSList
*data_list
= NULL
, *field_list
= NULL
;
224 gint field_problems
= 0;
225 gint min_length
= gnm_sheet_get_max_rows (state
->sheet
);
228 v_zone
= gnm_expr_entry_parse_as_value (state
->zone
, state
->sheet
);
229 g_return_if_fail (v_zone
!= NULL
);
231 while (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (state
->model
),
232 &this_iter
, NULL
, n
)) {
233 GnmValue
*v_data
, *v_field
;
234 gtk_tree_model_get (GTK_TREE_MODEL (state
->model
), &this_iter
,
235 DATA_RANGE
, &data_string
,
236 FIELD_LOCATION
, &field_string
,
238 v_data
= value_new_cellrange_str (state
->sheet
, data_string
);
239 v_field
= value_new_cellrange_str (state
->sheet
, field_string
);
240 g_free (data_string
);
241 g_free (field_string
);
243 g_return_if_fail (v_data
!= NULL
&& v_field
!= NULL
);
244 if (!global_range_contained (state
->sheet
, v_field
, v_zone
))
246 data_list
= g_slist_prepend (data_list
, v_data
);
247 field_list
= g_slist_prepend (field_list
, v_field
);
251 if (field_problems
> 0) {
253 if (field_problems
== 1)
254 text
= g_strdup (_("One field is not part of the merge zone!"));
256 text
= g_strdup_printf (_("%i fields are not part of the merge zone!"),
258 go_gtk_notice_nonmodal_dialog ((GtkWindow
*) state
->dialog
,
259 &(state
->warning_dialog
),
263 value_release (v_zone
);
264 range_list_destroy (data_list
);
265 range_list_destroy (field_list
);
269 g_slist_foreach (data_list
, cb_merge_find_shortest_column
, &min_length
);
270 g_slist_foreach (data_list
, cb_merge_find_longest_column
, &max_length
);
272 if (min_length
< max_length
) {
273 char *text
= g_strdup_printf (_("The data columns range in length from "
274 "%i to %i. Shall we trim the lengths to "
275 "%i and proceed?"), min_length
, max_length
,
278 if (go_gtk_query_yes_no (GTK_WINDOW (state
->dialog
), TRUE
,
280 g_slist_foreach (data_list
, cb_merge_trim_data
, &min_length
);
284 value_release (v_zone
);
285 range_list_destroy (data_list
);
286 range_list_destroy (field_list
);
292 if (!cmd_merge_data (GNM_WBC (state
->wbcg
), state
->sheet
,
293 v_zone
, field_list
, data_list
))
294 gtk_widget_destroy (state
->dialog
);
298 cb_merge_destroy (MergeState
*state
)
300 if (state
->model
!= NULL
)
301 g_object_unref (state
->model
);
302 if (state
->gui
!= NULL
)
303 g_object_unref (state
->gui
);
308 dialog_merge (WBCGtk
*wbcg
)
314 GtkTreeViewColumn
*column
;
315 GtkTreeSelection
*selection
;
318 g_return_if_fail (wbcg
!= NULL
);
320 if (gnm_dialog_raise_if_exists (wbcg
, MERGE_KEY
))
322 gui
= gnm_gtk_builder_load ("res:ui/merge.ui", NULL
, GO_CMD_CONTEXT (wbcg
));
326 state
= g_new0 (MergeState
, 1);
329 state
->sheet
= wb_control_cur_sheet (GNM_WBC (state
->wbcg
));
330 state
->dialog
= go_gtk_builder_get_widget (gui
, "Merge");
331 state
->warning_dialog
= NULL
;
333 state
->add_btn
= go_gtk_builder_get_widget (gui
, "add_button");
334 state
->delete_btn
= go_gtk_builder_get_widget (gui
, "remove_button");
335 state
->merge_btn
= go_gtk_builder_get_widget (gui
, "merge_button");
336 state
->change_btn
= go_gtk_builder_get_widget (gui
, "change_button");
337 state
->cancel_btn
= go_gtk_builder_get_widget (gui
, "cancel_button");
338 gtk_widget_set_size_request (state
->delete_btn
, 100, -1);
340 gtk_button_set_alignment (GTK_BUTTON (state
->add_btn
), 0., .5);
341 gtk_button_set_alignment (GTK_BUTTON (state
->delete_btn
), 0., .5);
342 gtk_button_set_alignment (GTK_BUTTON (state
->change_btn
), 0., .5);
344 grid
= GTK_GRID (go_gtk_builder_get_widget (gui
, "main-grid"));
345 state
->zone
= gnm_expr_entry_new (wbcg
, TRUE
);
346 gnm_expr_entry_set_flags (state
->zone
, GNM_EE_SINGLE_RANGE
, GNM_EE_MASK
);
347 gnm_editable_enters (GTK_WINDOW (state
->dialog
),
348 GTK_WIDGET (state
->zone
));
349 gtk_label_set_mnemonic_widget (GTK_LABEL (go_gtk_builder_get_widget (gui
, "var1-label")),
350 GTK_WIDGET (state
->zone
));
351 gtk_widget_set_hexpand (GTK_WIDGET (state
->zone
), TRUE
);
352 gtk_grid_attach (grid
, GTK_WIDGET (state
->zone
), 1, 0, 2, 1);
353 r
= selection_first_range (
354 wb_control_cur_sheet_view (GNM_WBC (wbcg
)), NULL
, NULL
);
356 gnm_expr_entry_load_from_range (state
->zone
,
359 state
->data
= gnm_expr_entry_new (wbcg
, TRUE
);
360 gnm_expr_entry_set_flags (state
->data
, GNM_EE_SINGLE_RANGE
, GNM_EE_MASK
);
361 gtk_widget_set_hexpand (GTK_WIDGET (state
->data
), TRUE
);
362 gtk_grid_attach (grid
, GTK_WIDGET (state
->data
), 0, 5, 1, 1);
364 state
->field
= gnm_expr_entry_new (wbcg
, TRUE
);
365 gnm_expr_entry_set_flags (state
->field
, GNM_EE_SINGLE_RANGE
, GNM_EE_MASK
);
366 gtk_widget_set_hexpand (GTK_WIDGET (state
->field
), TRUE
);
367 gtk_grid_attach (grid
, GTK_WIDGET (state
->field
), 1, 5, 1, 1);
369 scrolled
= go_gtk_builder_get_widget (state
->gui
, "scrolled");
370 state
->model
= gtk_list_store_new (NUM_COLUMNS
, G_TYPE_STRING
, G_TYPE_STRING
);
371 state
->list
= GTK_TREE_VIEW (gtk_tree_view_new_with_model
372 (GTK_TREE_MODEL (state
->model
)));
373 selection
= gtk_tree_view_get_selection (state
->list
);
374 gtk_tree_selection_set_mode (selection
, GTK_SELECTION_BROWSE
);
376 column
= gtk_tree_view_column_new_with_attributes (_("Input Data"),
377 gtk_cell_renderer_text_new (),
380 gtk_tree_view_column_set_sort_column_id (column
, DATA_RANGE
);
381 gtk_tree_view_column_set_min_width (column
, 150);
382 gtk_tree_view_append_column (state
->list
, column
);
383 column
= gtk_tree_view_column_new_with_attributes (_("Merge Field"),
384 gtk_cell_renderer_text_new (),
385 "text", FIELD_LOCATION
,
387 gtk_tree_view_column_set_sort_column_id (column
, FIELD_LOCATION
);
388 gtk_tree_view_column_set_min_width (column
, 100);
389 gtk_tree_view_append_column (state
->list
, column
);
391 gtk_tree_view_set_headers_clickable (state
->list
, TRUE
);
392 gtk_container_add (GTK_CONTAINER (scrolled
), GTK_WIDGET (state
->list
));
394 cb_merge_update_buttons (NULL
, state
);
395 g_signal_connect (selection
,
397 G_CALLBACK (cb_merge_selection_changed
), state
);
399 g_signal_connect_after (G_OBJECT (state
->zone
),
401 G_CALLBACK (cb_merge_update_buttons
), state
);
402 g_signal_connect_after (G_OBJECT (state
->data
),
404 G_CALLBACK (cb_merge_update_buttons
), state
);
405 g_signal_connect_after (G_OBJECT (state
->field
),
407 G_CALLBACK (cb_merge_update_buttons
), state
);
409 g_signal_connect (G_OBJECT (state
->add_btn
),
411 G_CALLBACK (cb_merge_add_clicked
), state
);
412 g_signal_connect (G_OBJECT (state
->change_btn
),
414 G_CALLBACK (cb_merge_change_clicked
), state
);
415 g_signal_connect (G_OBJECT (state
->delete_btn
),
417 G_CALLBACK (cb_merge_delete_clicked
), state
);
418 g_signal_connect (G_OBJECT (state
->merge_btn
),
420 G_CALLBACK (cb_merge_merge_clicked
), state
);
421 g_signal_connect (G_OBJECT (state
->cancel_btn
),
423 G_CALLBACK (cb_merge_cancel_clicked
), state
);
425 gnm_dialog_setup_destroy_handlers (GTK_DIALOG (state
->dialog
),
427 GNM_DIALOG_DESTROY_CURRENT_SHEET_REMOVED
);
429 gnm_init_help_button (
430 go_gtk_builder_get_widget (state
->gui
, "help_button"),
431 GNUMERIC_HELP_LINK_DATA_MERGE
);
433 gnm_keyed_dialog (state
->wbcg
, GTK_WINDOW (state
->dialog
),
436 /* a candidate for merging into attach guru */
437 g_object_set_data_full (G_OBJECT (state
->dialog
),
438 "state", state
, (GDestroyNotify
) cb_merge_destroy
);
439 go_gtk_nonmodal_dialog (wbcg_toplevel (state
->wbcg
),
440 GTK_WINDOW (state
->dialog
));
441 wbc_gtk_attach_guru (state
->wbcg
, GTK_WIDGET (state
->dialog
));
442 gtk_widget_show_all (GTK_WIDGET (state
->dialog
));