1 /*
2 ** 1999-09-11 -	A fun Rename command, with support for both simplistic replacement in filenames
3 **		as well as powerful regular expression replacement thingie. The former is handy
4 **		for changing the extension in all selected names from ".jpg" to ".jpeg", for
5 **		example. The latter RE mode can be used to do rather complex mappings and trans-
6 **		forms on the filenames. For example, consider having a set of 100 image files,
7 **		named "frame0.gif", "frame1.gif", ..., "frame99.gif". You want to rename these
8 **		to include a movie index (11), and also (of course) change the extension to ".png".
9 **		No problem. Just enter "^frame([0-9]+)\.gif$" in the 'From' text entry field
10 **		on the Reg Exp page, and "frame-11-$1.png" in the 'To' entry, and bam! The point
11 **		here is that you can use up to 9 parenthesized (?) subexpressions, and then access
12 **		the text that matched each with $n (n is a digit, 1 to 9) in the 'To' string.
13 **		If you've programmed any Perl, you might be familiar with the concept.
14 */
15 
16 #include "gentoo.h"
17 
18 #include <ctype.h>
19 
20 #include "cmd_delete.h"
21 #include "dialog.h"
22 #include "dirpane.h"
23 #include "errors.h"
24 #include "guiutil.h"
25 #include "overwrite.h"
26 #include "strutil.h"
27 
28 #include "cmd_renamere.h"
29 
30 #define	CMD_ID	"renamere"
31 
32 /* ----------------------------------------------------------------------------------------- */
33 
34 typedef enum {
35 	LOWER, UPPER, INITIAL, HEADLINE
36 } CaseMode;
37 
38 typedef struct {
39 	Dialog		*dlg;
40 
41 	GtkWidget	*nbook;
42 
43 	/* Simple replace mode. */
44 	GtkWidget	*r_vbox;
45 	GtkWidget	*r_from;
46 	GtkWidget	*r_to;
47 	GtkWidget	*r_cnocase;
48 	GtkWidget	*r_cglobal;
49 
50 	/* Full RE matching mode. */
51 	GtkWidget	*re_vbox;
52 	GtkWidget	*re_from;
53 	GtkWidget	*re_to;
54 	GtkWidget	*re_cnocase;
55 
56 	/* Mapping mode (for character substitutions). */
57 	GtkWidget	*map_vbox;
58 	GtkWidget	*map_from;
59 	GtkWidget	*map_to;
60 	GtkWidget	*map_remove;
61 
62 	/* Case-conversion mode. */
63 	GtkWidget	*case_vbox;
64 	GtkWidget	*case_lower;
65 	GtkWidget	*case_upper;
66 	GtkWidget	*case_initial;
67 
68 	MainInfo	*min;
69 	DirPane		*src;
70 	gint		page;
71 	GSList		*selection;
72 } RenREInfo;
73 
74 /* ----------------------------------------------------------------------------------------- */
75 
do_rename(MainInfo * min,DirPane * dp,const DirRow2 * row,const gchar * newname,GError ** error)76 static gboolean do_rename(MainInfo *min, DirPane *dp, const DirRow2 *row, const gchar *newname, GError **error)
77 {
78 	OvwRes		ores;
79 	GFile		*file;
80 	gboolean	ok, doit = TRUE;
81 
82 	if(strcmp(dp_row_get_name_display(dp_get_tree_model(dp), row), newname) == 0)
83 	{
84 		dp_unselect(dp, row);
85 		return TRUE;
86 	}
87 
88 	/* Get imaginary destination file. */
89 	if((file = dp_get_file_from_name(dp, newname)) == NULL)
90 		return FALSE;
91 	ores = ovw_overwrite_unary_file(dp, file);
92 	if(ores == OVW_SKIP)
93 		ok = !(doit = FALSE);	/* H4xX0r. */
94 	else if(ores == OVW_CANCEL)
95 		ok = doit = FALSE;
96 	else if(ores == OVW_PROCEED_FILE || ores == OVW_PROCEED_DIR)
97 		ok = del_delete_gfile(min, file, FALSE, error);
98 	else
99 		ok = TRUE;
100 
101 	if(ok && doit)
102 	{
103 		GFile	*dfile;
104 
105 		if((dfile = dp_get_file_from_row(dp, row)) != NULL)
106 		{
107 			GFile	*nfile;
108 
109 			if((nfile = g_file_set_display_name(dfile, newname, NULL, error)) != NULL)
110 				g_object_unref(nfile);
111 			g_object_unref(dfile);
112 			ok = nfile != NULL;
113 		}
114 		else
115 			ok = FALSE;
116 	}
117 	if(!ok && error != NULL && *error != NULL)
118 		err_set_gerror(min, error, CMD_ID, file);
119 	g_object_unref(file);
120 
121 	if(ok)
122 		dp_unselect(dp, row);
123 
124 	return ok;
125 }
126 
127 /* ----------------------------------------------------------------------------------------- */
128 
rename_simple(RenREInfo * ri,GError ** err)129 static gboolean rename_simple(RenREInfo *ri, GError **err)
130 {
131 	const gchar	*fromd, *tod;
132 	GString		*nn;
133 	GSList		*iter;
134 	gboolean	nocase, global, ok = TRUE;
135 
136 	if((fromd = gtk_entry_get_text(GTK_ENTRY(ri->r_from))) == NULL)
137 		return 0;
138 	tod = gtk_entry_get_text(GTK_ENTRY(ri->r_to));
139 
140 	nocase = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ri->r_cnocase));
141 	global = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ri->r_cglobal));
142 
143 	ovw_overwrite_begin(ri->min, _("\"%s\" Already Exists - Proceed With Rename?"), 0U);
144 	nn = g_string_new("");
145 	for(iter = ri->selection; ok && (iter != NULL); iter = g_slist_next(iter))
146 	{
147 		if(stu_replace_simple(nn, dp_row_get_name_display(dp_get_tree_model(ri->src), iter->data), fromd, tod, global, nocase) > 0)
148 			ok = do_rename(ri->min, ri->src, iter->data, nn->str, err);
149 	}
150 	g_string_free(nn, TRUE);
151 	ovw_overwrite_end(ri->min);
152 
153 	return ok;
154 }
155 
156 /* ----------------------------------------------------------------------------------------- */
157 
158 /* 1999-09-11 -	Go through <def>, copying characters to <str>. If the sequence $n is found,
159 **		where n is a digit 1..9, replace that with the n:th piece of <src>, as described
160 **		by <match>. To get a single dollar in the output, write $$ in <def>.
161 **		Returns TRUE if the entire <def> was successfully interpolated, FALSE on error.
162 */
interpolate(GString * str,const gchar * def,const GMatchInfo * match)163 static gboolean interpolate(GString *str, const gchar *def, const GMatchInfo *match)
164 {
165 	const gchar	*ptr;
166 
167 	g_string_truncate(str, 0);
168 	for(ptr = def; *ptr; ptr = g_utf8_next_char(ptr))
169 	{
170 		gunichar	here;
171 
172 		here = g_utf8_get_char(ptr);
173 		if(here == '$')
174 		{
175 			ptr = g_utf8_next_char(ptr);
176 			here = g_utf8_get_char(ptr);
177 			if(g_unichar_isdigit(here))
178 			{
179 				gint	slot;
180 
181 				slot = g_unichar_digit_value(here);
182 				if(slot >= 0 && slot <= 9)
183 				{
184 					gchar	*ms = g_match_info_fetch(match, slot);
185 
186 					if(ms != NULL)
187 					{
188 						g_string_append(str, ms);
189 						g_free(ms);
190 					}
191 					else
192 						return FALSE;
193 				}
194 				else
195 					return FALSE;
196 			}
197 			else if(*ptr == '$')
198 				g_string_append_c(str, '$'), ptr++;
199 			else
200 				return FALSE;
201 		}
202 		else
203 			g_string_append_unichar(str, here);
204 	}
205 	return TRUE;
206 }
207 
rename_regexp(RenREInfo * ri,GError ** err)208 static gboolean rename_regexp(RenREInfo *ri, GError **err)
209 {
210 	const gchar	*fromdefd, *tod;
211 	guint		reflags;
212 	GRegex		*fromre = NULL;
213 	gboolean	ok = TRUE;
214 
215 	if((fromdefd = gtk_entry_get_text(GTK_ENTRY(ri->re_from))) == NULL)
216 		return FALSE;
217 	if((tod = gtk_entry_get_text(GTK_ENTRY(ri->re_to))) == NULL)
218 		return FALSE;
219 
220 	reflags = G_REGEX_EXTENDED | (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ri->re_cnocase)) ? G_REGEX_CASELESS : 0);
221 	if((fromre = g_regex_new(fromdefd, reflags, G_REGEX_MATCH_NOTEMPTY, err)) != NULL)
222 	{
223 		GSList		*iter;
224 		GString		*nn;
225 
226 		ovw_overwrite_begin(ri->min, _("\"%s\" Already Exists - Proceed With Rename?"), 0U);
227 		nn = g_string_new("");
228 		for(iter = ri->selection; ok && (iter != NULL); iter = g_slist_next(iter))
229 		{
230 			GMatchInfo	*mi = NULL;
231 
232 			if(g_regex_match(fromre, dp_row_get_name_display(dp_get_tree_model(ri->src), iter->data), G_REGEX_MATCH_NOTEMPTY, &mi))
233 			{
234 				if(interpolate(nn, tod, mi) && g_utf8_strchr(nn->str, G_DIR_SEPARATOR, -1) == NULL)
235 					ok = do_rename(ri->min, ri->src, iter->data, nn->str, err);
236 				g_match_info_free(mi);
237 			}
238 		}
239 		g_string_free(nn, TRUE);
240 		ovw_overwrite_end(ri->min);
241 	}
242 	return ok;
243 }
244 
245 /* ----------------------------------------------------------------------------------------- */
246 
rename_map(RenREInfo * ri,GError ** err)247 static gboolean rename_map(RenREInfo *ri, GError **err)
248 {
249 	const gchar	*fromdef, *todef, *remdef;
250 	GSList		*iter;
251 	GString		*nn, *nr;
252 	gsize		fl, rl, i, nl;
253 	gboolean	ok = TRUE;
254 
255 	if((fromdef = gtk_entry_get_text(GTK_ENTRY(ri->map_from))) == NULL)
256 		return FALSE;
257 	if((todef = gtk_entry_get_text(GTK_ENTRY(ri->map_to))) == NULL)
258 		return FALSE;
259 	if(g_utf8_strchr(todef, -1, G_DIR_SEPARATOR) != NULL)	/* As always, prevent rogue moves. */
260 		return FALSE;
261 	if((remdef = gtk_entry_get_text(GTK_ENTRY(ri->map_remove))) == NULL)
262 		return FALSE;
263 
264 	/* Totally important: some lengths are in chars (for iteration), some in bytes for g_utf8_strchr(). */
265 	fl = strlen(fromdef);
266 	rl = strlen(remdef);
267 
268 	ovw_overwrite_begin(ri->min, _("\"%s\" Already Exists - Proceed With Rename?"), 0U);
269 	nn = g_string_new("");
270 	nr = g_string_new("");
271 	for(iter = ri->selection; iter != NULL; iter = g_slist_next(iter))
272 	{
273 		const gchar	*ptr, *map;
274 
275 		/* Step 1: clear the dynamic string. */
276 		g_string_truncate(nn, 0);
277 		ptr = dp_row_get_name_display(dp_get_tree_model(ri->src), iter->data);
278 		nl = g_utf8_strlen(ptr, -1);
279 		/* Step 2: go through and do the mapping. */
280 		for(i = 0; i < nl; i++, ptr = g_utf8_next_char(ptr))
281 		{
282 			gunichar	ch = g_utf8_get_char(ptr);
283 
284 			if((map = g_utf8_strchr(fromdef, fl, ch)) != NULL)
285 			{
286 				glong		index = g_utf8_pointer_to_offset(fromdef, map);
287 				const gchar	*tptr = g_utf8_offset_to_pointer(todef, index);
288 
289 				ch = g_utf8_get_char(tptr);	/* Map. */
290 			}
291 			g_string_append_unichar(nn, ch);
292 		}
293 		/* Step 3: remove any characters found in the 'remove' string. */
294 		ptr = nn->str;
295 		nl = g_utf8_strlen(ptr, -1);
296 		g_string_truncate(nr, 0);
297 		for(i = 0; i < nl; i++, ptr = g_utf8_next_char(ptr))
298 		{
299 			gunichar	ch = g_utf8_get_char(ptr);
300 
301 			if(g_utf8_strchr(remdef, rl, ch) == NULL)
302 				g_string_append_unichar(nr, ch);
303 		}
304 		/* Step 4: do the rename, using the now-built "display name". */
305 		ok = do_rename(ri->min, ri->src, iter->data, nr->str, err);
306 	}
307 	g_string_free(nr, TRUE);
308 	g_string_free(nn, TRUE);
309 	ovw_overwrite_end(ri->min);
310 
311 	return ok;
312 }
313 
314 /* ----------------------------------------------------------------------------------------- */
315 
316 /* 2008-07-19 -	Converts the given string to have an upper-case initial character, while the
317  *		remainder is lower-case. This is a very western idea of a useful case
318  *		conversion, but ... I'll include it anyway.
319 */
utf8_str_initial(const gchar * str,gssize len)320 static gchar * utf8_str_initial(const gchar *str, gssize len)
321 {
322 	GString		*tmp;
323 	gchar		*buf;
324 	gunichar	here;
325 
326 	tmp = g_string_sized_new(len);
327 
328 	here = g_utf8_get_char(str);
329 	g_string_append_unichar(tmp, g_unichar_toupper(here));
330 
331 	while(--len)
332 	{
333 		str = g_utf8_next_char(str);
334 		here = g_utf8_get_char(str);
335 		g_string_append_unichar(tmp, g_unichar_tolower(here));
336 	}
337 	buf = tmp->str;
338 	g_string_free(tmp, FALSE);
339 
340 	return buf;
341 }
342 
343 /* 2008-04-20 -	Minor touch-ups due to GTK+ 2.0 and UTF-8 in strings. We need to make a decision:
344  *		Is the map operation happening in file-system or display space? The obvious
345  *		choice seems to be file-system, which is after all what the filenames are for.
346  *		However, we don't have a general way of downcasing if the local file system is
347  *		using UTF-8 ... So we use the display versions of the names, downcase in UTF-8,
348  *		and then convert back.
349 */
rename_case(RenREInfo * ri,GError ** err)350 static gboolean rename_case(RenREInfo *ri, GError **err)
351 {
352 	GString		*nn;
353 	GSList		*iter;
354 	gchar *		(*func)(const gchar *, gssize), *cc;
355 	gboolean	ok = TRUE;
356 
357 	/* Select the case-conversion function, once. */
358 	if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ri->case_lower)))
359 		func = g_utf8_strdown;
360 	else if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ri->case_upper)))
361 		func = g_utf8_strup;
362 	else if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ri->case_initial)))
363 		func = utf8_str_initial;
364 	else
365 		return FALSE;
366 
367 	ovw_overwrite_begin(ri->min, _("\"%s\" Already Exists - Proceed With Rename?"), 0U);
368 	nn = g_string_new("");
369 	for(iter = ri->selection; ok && (iter != NULL); iter = g_slist_next(iter))
370 	{
371 		const gchar	*dn = dp_row_get_name_display(dp_get_tree_model(ri->src), iter->data);
372 
373 		/* Step 1: set the dynamic string to the input filename. */
374 		g_string_assign(nn, dn);
375 		/* Step 2: perform case conversion. */
376 		cc = func(nn->str, nn->len);
377 		/* Step 3: if resulting name is different, rename on disk. */
378 		if(strcmp(dn, cc) != 0)
379 		{
380 			ok = do_rename(ri->min, ri->src, iter->data, cc, err);
381 		}
382 		g_free(cc);
383 	}
384 	g_string_free(nn, TRUE);
385 	ovw_overwrite_end(ri->min);
386 
387 	return ok;
388 }
389 
390 /* ----------------------------------------------------------------------------------------- */
391 
evt_nbook_switchpage(GtkWidget * wid,gpointer page,gint page_num,gpointer user)392 static void evt_nbook_switchpage(GtkWidget *wid, gpointer page, gint page_num, gpointer user)
393 {
394 	RenREInfo	*ri = user;
395 
396 	ri->page = page_num;
397 	gtk_widget_grab_focus(ri->page ? ri->re_from : ri->r_from);
398 }
399 
cmd_renamere(MainInfo * min,DirPane * src,DirPane * dst,const CmdArg * ca)400 gint cmd_renamere(MainInfo *min, DirPane *src, DirPane *dst, const CmdArg *ca)
401 {
402 	GtkWidget		*label, *grid;
403 	static RenREInfo	ri = { NULL };
404 	gboolean		ok = TRUE;
405 
406 	ri.min	= min;
407 	ri.src	= src;
408 
409 	if((ri.selection = dp_get_selection(ri.src)) == NULL)
410 		return 1;
411 
412 	if(ri.dlg == NULL)
413 	{
414 		ri.nbook = gtk_notebook_new();
415 		g_signal_connect(G_OBJECT(ri.nbook), "switch-page", G_CALLBACK(evt_nbook_switchpage), &ri);
416 
417 		/* Build the 'Simple' mode's GUI. */
418 		ri.r_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
419 		label = gtk_label_new(_("Look for substring in all filenames, and replace\nit with another string."));
420 		gtk_box_pack_start(GTK_BOX(ri.r_vbox), label, FALSE, FALSE, 0);
421 		grid = gtk_grid_new();
422 		label = gtk_label_new(_("Replace"));
423 		gtk_grid_attach(GTK_GRID(grid), label, 0, 0, 1, 1);
424 		ri.r_from = gui_dialog_entry_new();
425 		gtk_widget_set_hexpand(ri.r_from, TRUE);
426 		gtk_widget_set_halign(ri.r_from, GTK_ALIGN_FILL);
427 		gtk_grid_attach(GTK_GRID(grid), ri.r_from, 1, 0, 1, 1);
428 		label = gtk_label_new(_("With"));
429 		gtk_grid_attach(GTK_GRID(grid), label, 0, 1, 1, 1);
430 		ri.r_to = gui_dialog_entry_new();
431 		gtk_grid_attach(GTK_GRID(grid), ri.r_to, 1, 1, 1, 1);
432 		gtk_box_pack_start(GTK_BOX(ri.r_vbox), grid, FALSE, FALSE, 0);
433 		grid = gtk_grid_new();
434 		ri.r_cnocase = gtk_check_button_new_with_label(_("Ignore Case?"));
435 		gtk_widget_set_hexpand(ri.r_cnocase, TRUE);
436 		gtk_widget_set_halign(ri.r_cnocase, GTK_ALIGN_FILL);
437 		gtk_grid_attach(GTK_GRID(grid), ri.r_cnocase, 0, 0, 1, 1);
438 		ri.r_cglobal = gtk_check_button_new_with_label(_("Replace All?"));
439 		gtk_grid_attach(GTK_GRID(grid), ri.r_cglobal, 1, 0, 1, 1);
440 		gtk_box_pack_start(GTK_BOX(ri.r_vbox), grid, FALSE, FALSE, 0);
441 
442 		gtk_notebook_append_page(GTK_NOTEBOOK(ri.nbook), ri.r_vbox, gtk_label_new(_("Simple")));
443 
444 		/* Build the 'Reg Exp' mode's GUI. */
445 		ri.re_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
446 		label = gtk_label_new(	_("Execute the 'From' RE on each filename, storing\n"
447 					"parenthesised subexpression matches. Then replace\n"
448 					"any occurance of $n in 'To', where n is the index\n"
449 					"(counting from 1) of a subexpression, with the text\n"
450 					"that matched, and use the result as a new filename."));
451 		gtk_box_pack_start(GTK_BOX(ri.re_vbox), label, FALSE, FALSE, 0);
452 		grid = gtk_grid_new();
453 		label = gtk_label_new(_("From"));
454 		gtk_grid_attach(GTK_GRID(grid), label, 0, 0, 1, 1);
455 		ri.re_from = gui_dialog_entry_new();
456 		gtk_widget_set_hexpand(ri.re_from, TRUE);
457 		gtk_widget_set_halign(ri.re_from, GTK_ALIGN_FILL);
458 		gtk_grid_attach(GTK_GRID(grid), ri.re_from, 1, 0, 1, 1);
459 		label = gtk_label_new(_("To"));
460 		gtk_grid_attach(GTK_GRID(grid), label, 0, 1, 1, 1);
461 		ri.re_to = gui_dialog_entry_new();
462 		gtk_grid_attach(GTK_GRID(grid), ri.re_to, 1, 1, 1, 1);
463 		gtk_box_pack_start(GTK_BOX(ri.re_vbox), grid, FALSE, FALSE, 0);
464 		ri.re_cnocase = gtk_check_button_new_with_label(_("Ignore Case?"));
465 		gtk_box_pack_start(GTK_BOX(ri.re_vbox), ri.re_cnocase, FALSE, FALSE, 0);
466 
467 		gtk_notebook_append_page(GTK_NOTEBOOK(ri.nbook), ri.re_vbox, gtk_label_new(_("Reg Exp")));
468 		gtk_widget_show_all(ri.re_vbox);
469 
470 		/* Build the 'Map' mode's GUI. */
471 		ri.map_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
472 		label = gtk_label_new(_("Look for each character in the 'From' string, and replace\n"
473 					"any hits with the corresponding character in the 'To' string.\n"
474 					"Then, any characters in the 'Remove' string are removed from\n"
475 					"the filename, and the result used as the new name for each file."));
476 		gtk_box_pack_start(GTK_BOX(ri.map_vbox), label, FALSE, FALSE, 0);
477 		grid = gtk_grid_new();
478 		label = gtk_label_new(_("From"));
479 		gtk_grid_attach(GTK_GRID(grid), label, 0, 0, 1, 1);
480 		ri.map_from = gui_dialog_entry_new();
481 		gtk_widget_set_hexpand(ri.map_from, TRUE);
482 		gtk_widget_set_halign(ri.map_from, GTK_ALIGN_FILL);
483 		gtk_grid_attach(GTK_GRID(grid), ri.map_from, 1, 0, 1, 1);
484 		label = gtk_label_new(_("To"));
485 		gtk_grid_attach(GTK_GRID(grid), label, 0, 1, 1, 1);
486 		ri.map_to = gui_dialog_entry_new();
487 		gtk_grid_attach(GTK_GRID(grid), ri.map_to, 1, 1, 1, 1);
488 		label = gtk_label_new(_("Remove"));
489 		gtk_grid_attach(GTK_GRID(grid), label, 0, 2, 1, 1);
490 		ri.map_remove = gui_dialog_entry_new();
491 		gtk_grid_attach(GTK_GRID(grid), ri.map_remove, 1, 2, 1, 1);
492 		gtk_box_pack_start(GTK_BOX(ri.map_vbox), grid, FALSE, FALSE, 0);
493 		gtk_notebook_append_page(GTK_NOTEBOOK(ri.nbook), ri.map_vbox, gtk_label_new(_("Map")));
494 
495 		/* Build the 'Case' mode's GUI. */
496 		ri.case_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
497 		label = gtk_label_new(_("Changes the case (upper/lower) of the characters\nin the selected filename(s)."));
498 		gtk_box_pack_start(GTK_BOX(ri.case_vbox), label, FALSE, FALSE, 0);
499 		ri.case_lower = gtk_radio_button_new_with_label(NULL, _("Lower Case?"));
500 		gtk_box_pack_start(GTK_BOX(ri.case_vbox), ri.case_lower, FALSE, FALSE, 0);
501 		ri.case_upper = gtk_radio_button_new_with_label(gtk_radio_button_get_group(GTK_RADIO_BUTTON(ri.case_lower)), _("Upper Case?"));
502 		gtk_box_pack_start(GTK_BOX(ri.case_vbox), ri.case_upper, FALSE, FALSE, 0);
503 		ri.case_initial = gtk_radio_button_new_with_label(gtk_radio_button_get_group(GTK_RADIO_BUTTON(ri.case_lower)), _("Upper Case Initial?"));
504 		gtk_box_pack_start(GTK_BOX(ri.case_vbox), ri.case_initial, FALSE, FALSE, 0);
505 		gtk_widget_show_all(ri.map_vbox);
506 		gtk_notebook_append_page(GTK_NOTEBOOK(ri.nbook), ri.case_vbox, gtk_label_new(_("Case")));
507 
508 		ri.dlg = dlg_dialog_sync_new(ri.nbook, _("RenameRE"), NULL);
509 
510 		ri.page = 0;
511 	}
512 	gtk_notebook_set_current_page(GTK_NOTEBOOK(ri.nbook), ri.page);
513 	gtk_widget_grab_focus(ri.page ? ri.re_from : ri.r_from);
514 
515 	if(dlg_dialog_sync_wait(ri.dlg) == DLG_POSITIVE)
516 	{
517 		GError		*err = NULL;
518 
519 		if(ri.page == 0)
520 			ok = rename_simple(&ri, &err);
521 		else if(ri.page == 1)
522 			ok = rename_regexp(&ri, &err);
523 		else if(ri.page == 2)
524 			ok = rename_map(&ri, &err);
525 		else
526 			ok = rename_case(&ri, &err);
527 		if(ok)
528 			dp_rescan_post_cmd(ri.src);
529 	}
530 	dp_free_selection(ri.selection);
531 	ri.selection = NULL;
532 
533 	return ok;
534 }
535