1 /*
2  * dialog-paste-special.c: The dialog for selecting non-standard
3  *    behaviors when pasting.
4  *
5  * Author:
6  *  MIguel de Icaza (miguel@gnu.org)
7  *
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.
12  *
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.
17  *
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/>.
20  */
21 #include <gnumeric-config.h>
22 #include <glib/gi18n-lib.h>
23 #include <gnumeric.h>
24 #include <dialogs/dialogs.h>
25 #include <dialogs/help.h>
26 
27 #include <wbc-gtk.h>
28 #include <gui-util.h>
29 #include <clipboard.h>
30 #include <selection.h>
31 #include <cmd-edit.h>
32 
33 
34 static char const * const paste_type_group[] = {
35 	"paste-type-all",
36 	"paste-type-content",
37 	"paste-type-as-value",
38 	"paste-type-formats",
39 	"paste-type-comments",
40 	NULL
41 };
42 static const struct {
43 	gboolean permit_cell_ops;
44 	int paste_enum;
45 } paste_type_group_props[] = {
46 	{TRUE, PASTE_ALL_CELL},
47 	{TRUE, PASTE_CONTENTS},
48 	{TRUE, PASTE_AS_VALUES},
49 	{FALSE, PASTE_FORMATS},
50 	{FALSE, PASTE_COMMENTS},
51 };
52 static char const * const cell_operation_group[] = {
53 	"cell-operation-none",
54 	"cell-operation-add",
55 	"cell-operation-subtract",
56 	"cell-operation-multiply",
57 	"cell-operation-divide",
58 	NULL
59 };
60 static const struct {
61 	int paste_enum;
62 } cell_operation_props[] = {
63 	{0},
64 	{PASTE_OPER_ADD},
65 	{PASTE_OPER_SUB},
66 	{PASTE_OPER_MULT},
67 	{PASTE_OPER_DIV},
68 };
69 static char const * const region_operation_group[] = {
70 	"region-operation-none",
71 	"region-operation-transpose",
72 	"region-operation-flip-h",
73 	"region-operation-flip-v",
74 	NULL
75 };
76 static const struct {
77 	int paste_enum;
78 } region_operation_props[] = {
79 	{0},
80 	{PASTE_TRANSPOSE},
81 	{PASTE_FLIP_H},
82 	{PASTE_FLIP_V},
83 };
84 
85 typedef struct {
86 	GtkBuilder *gui;
87 	GtkWidget *dialog;
88 	GtkWidget *ok_button;
89 	GtkWidget *cancel_button;
90 	GtkWidget *link_button;
91 	GtkWidget *help_button;
92 	char const *help_link;
93 	Sheet	  *sheet;
94 	SheetView *sv;
95 	Workbook  *wb;
96 	WBCGtk  *wbcg;
97 } PasteSpecialState;
98 
99 #define GNM_PASTE_SPECIAL_KEY	"gnm-paste-special"
100 
101 /* The "Paste Link" button should be grayed-out, unless type "All" is
102    selected, cell operation "None" is selected,
103    region operation "None" is selected, and "Skip
104    Blanks" is not selected.  */
105 static void
paste_link_set_sensitive(PasteSpecialState * state)106 paste_link_set_sensitive (PasteSpecialState *state)
107 {
108 	gboolean sensitive =
109 		(!gtk_toggle_button_get_active
110 		 (GTK_TOGGLE_BUTTON (go_gtk_builder_get_widget (state->gui,"skip-blanks")))
111 		 && 0 == gnm_gui_group_value (state->gui, paste_type_group)
112 		 && 0 == gnm_gui_group_value (state->gui, cell_operation_group)
113 		 && 0 == gnm_gui_group_value (state->gui, region_operation_group));
114 	gtk_widget_set_sensitive (state->link_button, sensitive);
115 }
116 
117 static void
skip_blanks_set_sensitive(PasteSpecialState * state)118 skip_blanks_set_sensitive (PasteSpecialState *state)
119 {
120 	GtkWidget *button = go_gtk_builder_get_widget (state->gui,"skip-blanks");
121 	gboolean sensitive =
122 		(3 > gnm_gui_group_value (state->gui, paste_type_group)
123 		 && 0 == gnm_gui_group_value (state->gui, cell_operation_group));
124 	gtk_widget_set_sensitive (button, sensitive);
125 }
126 
127 static void
dont_change_formulae_set_sensitive(PasteSpecialState * state)128 dont_change_formulae_set_sensitive (PasteSpecialState *state)
129 {
130 	GtkWidget *button = go_gtk_builder_get_widget (state->gui,"dont-change-formulae");
131 	gboolean sensitive =
132 		(2 > gnm_gui_group_value (state->gui, paste_type_group)
133 		 && 0 == gnm_gui_group_value (state->gui, cell_operation_group));
134 	gtk_widget_set_sensitive (button, sensitive);
135 }
136 
137 static void
cb_destroy(PasteSpecialState * state)138 cb_destroy (PasteSpecialState *state)
139 {
140 	if (state->gui != NULL)
141 		g_object_unref (state->gui);
142 	wbcg_edit_finish (state->wbcg, WBC_EDIT_REJECT, NULL);
143 	g_free (state);
144 }
145 
146 static void
dialog_paste_special_type_toggled_cb(GtkWidget * button,PasteSpecialState * state)147 dialog_paste_special_type_toggled_cb (GtkWidget *button, PasteSpecialState *state)
148 {
149 	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) {
150 		int i = gnm_gui_group_value (state->gui, paste_type_group);
151 		char const * const *group;
152 		gboolean permit_cell_ops = paste_type_group_props[i].permit_cell_ops;
153 
154 		for (group = cell_operation_group; *group != NULL; group++)
155 			gtk_widget_set_sensitive (go_gtk_builder_get_widget (state->gui,*group),
156 						  permit_cell_ops);
157 		paste_link_set_sensitive (state);
158 		skip_blanks_set_sensitive (state);
159 		dont_change_formulae_set_sensitive (state);
160 	}
161 }
162 
163 static void
dialog_paste_special_cell_op_toggled_cb(GtkWidget * button,PasteSpecialState * state)164 dialog_paste_special_cell_op_toggled_cb (GtkWidget *button, PasteSpecialState *state)
165 {
166 	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) {
167 		paste_link_set_sensitive (state);
168 		skip_blanks_set_sensitive (state);
169 		dont_change_formulae_set_sensitive (state);
170 	}
171 }
172 
173 static void
dialog_paste_special_region_op_toggled_cb(GtkWidget * button,PasteSpecialState * state)174 dialog_paste_special_region_op_toggled_cb (GtkWidget *button, PasteSpecialState *state)
175 {
176 	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) {
177 		paste_link_set_sensitive (state);
178 	}
179 }
180 static void
dialog_paste_special_skip_blanks_toggled_cb(GtkWidget * button,PasteSpecialState * state)181 dialog_paste_special_skip_blanks_toggled_cb (GtkWidget *button, PasteSpecialState *state)
182 {
183 		paste_link_set_sensitive (state);
184 }
185 
186 static void
cb_tool_cancel_clicked(G_GNUC_UNUSED GtkWidget * button,PasteSpecialState * state)187 cb_tool_cancel_clicked (G_GNUC_UNUSED GtkWidget *button,
188 			PasteSpecialState *state)
189 {
190 	gtk_widget_destroy (state->dialog);
191 	return;
192 }
193 
194 static void
cb_tool_ok_clicked(G_GNUC_UNUSED GtkWidget * button,PasteSpecialState * state)195 cb_tool_ok_clicked (G_GNUC_UNUSED GtkWidget *button,
196 			PasteSpecialState *state)
197 {
198 	int result;
199 	int paste_type = gnm_gui_group_value (state->gui, paste_type_group);
200 	int region_op_type = gnm_gui_group_value (state->gui, region_operation_group);
201 
202 	result = paste_type_group_props[paste_type].paste_enum
203 		| region_operation_props[region_op_type].paste_enum;
204 
205 	if (paste_type_group_props[paste_type].permit_cell_ops) {
206 		int cell_op_type = gnm_gui_group_value (state->gui, cell_operation_group);
207 		result |= cell_operation_props[cell_op_type].paste_enum;
208 	}
209 
210 	if (gtk_toggle_button_get_active
211 	    (GTK_TOGGLE_BUTTON (go_gtk_builder_get_widget (state->gui,"skip-blanks"))))
212 		result |= PASTE_SKIP_BLANKS;
213 	if (gtk_toggle_button_get_active
214 	    (GTK_TOGGLE_BUTTON (go_gtk_builder_get_widget (state->gui,"dont-change-formulae"))))
215 		result |= PASTE_EXPR_LOCAL_RELOCATE;
216 
217 	if (gtk_toggle_button_get_active
218 	    (GTK_TOGGLE_BUTTON (go_gtk_builder_get_widget (state->gui,"row-heights"))))
219 		result |= PASTE_ROW_HEIGHTS;
220 	if (gtk_toggle_button_get_active
221 	    (GTK_TOGGLE_BUTTON (go_gtk_builder_get_widget (state->gui,"column-widths"))))
222 		result |= PASTE_COLUMN_WIDTHS;
223 
224 	cmd_paste_to_selection (GNM_WBC (state->wbcg), state->sv, result);
225 	gtk_widget_destroy (state->dialog);
226 	return;
227 }
228 
229 static void
cb_tool_paste_link_clicked(G_GNUC_UNUSED GtkWidget * button,PasteSpecialState * state)230 cb_tool_paste_link_clicked (G_GNUC_UNUSED GtkWidget *button,
231 			PasteSpecialState *state)
232 {
233 	cmd_paste_to_selection (GNM_WBC (state->wbcg), state->sv, PASTE_LINK);
234 	gtk_widget_destroy (state->dialog);
235 	return;
236 }
237 
238 void
dialog_paste_special(WBCGtk * wbcg)239 dialog_paste_special (WBCGtk *wbcg)
240 {
241 	PasteSpecialState *state;
242 	GtkBuilder *gui;
243 	char const * const *group;
244 
245 	if (gnm_dialog_raise_if_exists (wbcg, GNM_PASTE_SPECIAL_KEY))
246 		return;
247 	gui = gnm_gtk_builder_load ("res:ui/paste-special.ui", NULL, GO_CMD_CONTEXT (wbcg));
248 	if (gui == NULL)
249 		return;
250 
251 	state = g_new0 (PasteSpecialState, 1);
252 	state->wbcg   = wbcg;
253 	state->gui    = gui;
254 	state->dialog =  go_gtk_builder_get_widget (state->gui, "paste-special");
255 	state->sheet = wbcg_cur_sheet (wbcg);
256 	state->sv = wb_control_cur_sheet_view (GNM_WBC (wbcg));
257 
258 	g_return_if_fail (state->dialog != NULL);
259 
260 	state->link_button = go_gtk_builder_get_widget (state->gui,"paste-link_button");
261 	g_signal_connect (G_OBJECT (state->link_button),
262 			  "clicked",
263 			  G_CALLBACK (cb_tool_paste_link_clicked), state);
264 	state->help_button = go_gtk_builder_get_widget (state->gui, "help_button");
265 	gnm_init_help_button (state->help_button, GNUMERIC_HELP_LINK_PASTE_SPECIAL);
266 	state->cancel_button = go_gtk_builder_get_widget (state->gui, "cancel_button");
267 	g_signal_connect (G_OBJECT (state->cancel_button),
268 			  "clicked",
269 			  G_CALLBACK (cb_tool_cancel_clicked), state);
270 	state->ok_button = go_gtk_builder_get_widget (state->gui, "ok_button");
271 	g_signal_connect (G_OBJECT (state->ok_button),
272 			  "clicked",
273 			  G_CALLBACK (cb_tool_ok_clicked), state);
274 
275 
276 	for (group = paste_type_group; *group != NULL; group++)
277 		g_signal_connect_after (go_gtk_builder_get_widget (state->gui,*group),
278 					"toggled",
279 					G_CALLBACK (dialog_paste_special_type_toggled_cb), state);
280 	for (group = cell_operation_group; *group != NULL; group++)
281 		g_signal_connect_after (go_gtk_builder_get_widget (state->gui,*group),
282 					"toggled",
283 					G_CALLBACK (dialog_paste_special_cell_op_toggled_cb), state);
284 	for (group = region_operation_group; *group != NULL; group++)
285 		g_signal_connect_after (go_gtk_builder_get_widget (state->gui,*group),
286 					"toggled",
287 					G_CALLBACK (dialog_paste_special_region_op_toggled_cb), state);
288 	g_signal_connect_after (go_gtk_builder_get_widget (state->gui, "skip-blanks"),
289 				"toggled",
290 				G_CALLBACK (dialog_paste_special_skip_blanks_toggled_cb), state);
291 	paste_link_set_sensitive (state);
292 
293 	gtk_toggle_button_set_active
294 		(GTK_TOGGLE_BUTTON (go_gtk_builder_get_widget (state->gui,"column-widths")),
295 		 sv_is_full_colrow_selected (state->sv, TRUE, -1));
296 	gtk_toggle_button_set_active
297 		(GTK_TOGGLE_BUTTON (go_gtk_builder_get_widget (state->gui,"row-heights")),
298 		 sv_is_full_colrow_selected (state->sv, FALSE, -1));
299 
300 	gnm_dialog_setup_destroy_handlers (GTK_DIALOG (state->dialog), wbcg,
301 					   GNM_DIALOG_DESTROY_SHEET_REMOVED);
302 
303 	gnm_keyed_dialog (wbcg, GTK_WINDOW (state->dialog),
304 			       GNM_PASTE_SPECIAL_KEY);
305 
306 	wbc_gtk_attach_guru (state->wbcg, state->dialog);
307 	g_object_set_data_full (G_OBJECT (state->dialog),
308 				"state", state,
309 				(GDestroyNotify) cb_destroy);
310 
311 	gtk_widget_show (state->dialog);
312 }
313