GUI: Move .ui files from goffice resources to glib resources
[gnumeric.git] / src / dialogs / dialog-merge.c
blobec9f1584b9b6ae5bd64020a8e2842be9e7e35ce5
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * dialog-merge.c: Dialog to merge a list of data into a given range
6 * Author:
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>
27 #include <gnumeric.h>
28 #include "dialogs.h"
29 #include "help.h"
31 #include <gui-util.h>
32 #include <wbc-gtk.h>
33 #include <workbook-view.h>
34 #include <workbook.h>
35 #include <sheet.h>
36 #include <ranges.h>
37 #include <value.h>
38 #include <commands.h>
39 #include <selection.h>
40 #include <widgets/gnumeric-expr-entry.h>
42 #include <gtk/gtk.h>
44 #define MERGE_KEY "merge-dialog"
46 typedef struct {
47 WBCGtk *wbcg;
48 Sheet *sheet;
49 GtkBuilder *gui;
50 GtkWidget *dialog;
51 GtkWidget *warning_dialog;
52 GtkTreeView *list;
53 GtkListStore *model;
54 GnmExprEntry *zone;
55 GnmExprEntry *data;
56 GnmExprEntry *field;
57 GtkWidget *add_btn;
58 GtkWidget *change_btn;
59 GtkWidget *delete_btn;
60 GtkWidget *merge_btn;
61 GtkWidget *cancel_btn;
63 } MergeState;
65 enum {
66 DATA_RANGE,
67 FIELD_LOCATION,
68 NUM_COLUMNS
71 static void
72 cb_merge_update_buttons (G_GNUC_UNUSED gpointer ignored,
73 MergeState *state)
75 /* Note: ignored could be NULL or an expr-entry. */
76 GtkTreeIter iter;
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);
92 static void
93 cb_merge_selection_changed (GtkTreeSelection *selection, MergeState *state)
95 GtkTreeIter iter;
96 gboolean has_selection;
97 char *data_string = NULL, *field_string = NULL;
99 has_selection = gtk_tree_selection_get_selected (selection, NULL, &iter);
101 if (has_selection) {
102 gtk_tree_model_get (GTK_TREE_MODEL (state->model), &iter,
103 DATA_RANGE, &data_string,
104 FIELD_LOCATION, &field_string,
105 -1);
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);
114 static void
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,
126 -1);
128 g_free (data_text);
129 g_free (field_text);
131 selection = gtk_tree_view_get_selection (state->list);
132 gtk_tree_selection_select_iter (selection, iter);
136 static void
137 cb_merge_add_clicked (G_GNUC_UNUSED GtkWidget *ignore,
138 MergeState *state)
140 GtkTreeIter iter;
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);
146 else
147 gtk_list_store_insert_before (state->model, &iter, &sel_iter);
149 merge_store_info_in_list (&iter, state);
152 static void
153 cb_merge_change_clicked (G_GNUC_UNUSED GtkWidget *ignore,
154 MergeState *state)
156 GtkTreeIter iter;
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);
163 static void
164 cb_merge_delete_clicked (G_GNUC_UNUSED GtkWidget *ignore,
165 MergeState *state)
167 GtkTreeIter iter;
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);
175 static void
176 cb_merge_cancel_clicked (G_GNUC_UNUSED GtkWidget *ignore,
177 MergeState *state)
179 gtk_widget_destroy (GTK_WIDGET (state->dialog));
182 static void
183 cb_merge_find_shortest_column (gpointer data, gpointer lp)
185 GnmValue *range = data;
186 int *length = lp;
187 int r_length = range->v_range.cell.b.row - range->v_range.cell.a.row + 1;
189 if (r_length < *length)
190 *length = r_length;
193 static void
194 cb_merge_find_longest_column (gpointer data, gpointer lp)
196 GnmValue *range = data;
197 int *length = lp;
198 int r_length = range->v_range.cell.b.row - range->v_range.cell.a.row + 1;
200 if (r_length > *length)
201 *length = r_length;
204 static void
205 cb_merge_trim_data (gpointer data, gpointer lp)
207 GnmValue *range = data;
208 int *length = lp;
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;
217 static void
218 cb_merge_merge_clicked (G_GNUC_UNUSED GtkWidget *ignore,
219 MergeState *state)
221 GtkTreeIter this_iter;
222 gint n = 0;
223 char *data_string = NULL, *field_string = NULL;
224 GSList *data_list = NULL, *field_list = NULL;
225 GnmValue *v_zone;
226 gint field_problems = 0;
227 gint min_length = gnm_sheet_get_max_rows (state->sheet);
228 gint max_length = 0;
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,
239 -1);
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))
247 field_problems++;
248 data_list = g_slist_prepend (data_list, v_data);
249 field_list = g_slist_prepend (field_list, v_field);
250 n++;
253 if (field_problems > 0) {
254 char *text;
255 if (field_problems == 1)
256 text = g_strdup (_("One field is not part of the merge zone!"));
257 else
258 text = g_strdup_printf (_("%i fields are not part of the merge zone!"),
259 field_problems);
260 go_gtk_notice_nonmodal_dialog ((GtkWindow *) state->dialog,
261 &(state->warning_dialog),
262 GTK_MESSAGE_ERROR,
263 "%s", text);
264 g_free (text);
265 value_release (v_zone);
266 range_list_destroy (data_list);
267 range_list_destroy (field_list);
268 return;
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,
278 min_length);
280 if (go_gtk_query_yes_no (GTK_WINDOW (state->dialog), TRUE,
281 "%s", text)) {
282 g_slist_foreach (data_list, cb_merge_trim_data, &min_length);
283 g_free (text);
284 } else {
285 g_free (text);
286 value_release (v_zone);
287 range_list_destroy (data_list);
288 range_list_destroy (field_list);
289 return;
294 if (!cmd_merge_data (GNM_WBC (state->wbcg), state->sheet,
295 v_zone, field_list, data_list))
296 gtk_widget_destroy (state->dialog);
299 static void
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);
306 g_free (state);
309 void
310 dialog_merge (WBCGtk *wbcg)
312 MergeState *state;
313 GtkBuilder *gui;
314 GtkGrid *grid;
315 GtkWidget *scrolled;
316 GtkTreeViewColumn *column;
317 GtkTreeSelection *selection;
318 GnmRange const *r;
320 g_return_if_fail (wbcg != NULL);
322 if (gnm_dialog_raise_if_exists (wbcg, MERGE_KEY))
323 return;
324 gui = gnm_gtk_builder_load ("res:ui/merge.ui", NULL, GO_CMD_CONTEXT (wbcg));
325 if (gui == NULL)
326 return;
328 state = g_new0 (MergeState, 1);
329 state->gui = gui;
330 state->wbcg = wbcg;
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);
357 if (r != NULL)
358 gnm_expr_entry_load_from_range (state->zone,
359 state->sheet, r);
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 (),
380 "text", DATA_RANGE,
381 NULL);
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,
388 NULL);
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,
398 "changed",
399 G_CALLBACK (cb_merge_selection_changed), state);
401 g_signal_connect_after (G_OBJECT (state->zone),
402 "changed",
403 G_CALLBACK (cb_merge_update_buttons), state);
404 g_signal_connect_after (G_OBJECT (state->data),
405 "changed",
406 G_CALLBACK (cb_merge_update_buttons), state);
407 g_signal_connect_after (G_OBJECT (state->field),
408 "changed",
409 G_CALLBACK (cb_merge_update_buttons), state);
411 g_signal_connect (G_OBJECT (state->add_btn),
412 "clicked",
413 G_CALLBACK (cb_merge_add_clicked), state);
414 g_signal_connect (G_OBJECT (state->change_btn),
415 "clicked",
416 G_CALLBACK (cb_merge_change_clicked), state);
417 g_signal_connect (G_OBJECT (state->delete_btn),
418 "clicked",
419 G_CALLBACK (cb_merge_delete_clicked), state);
420 g_signal_connect (G_OBJECT (state->merge_btn),
421 "clicked",
422 G_CALLBACK (cb_merge_merge_clicked), state);
423 g_signal_connect (G_OBJECT (state->cancel_btn),
424 "clicked",
425 G_CALLBACK (cb_merge_cancel_clicked), state);
427 gnm_dialog_setup_destroy_handlers (GTK_DIALOG (state->dialog),
428 state->wbcg,
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),
436 MERGE_KEY);
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));