GNOME Bugzilla – Bug 306489
rename multiple files at once
Last modified: 2010-11-01 16:29:25 UTC
premise: renaming is an integral part of file management and normal users need a way to rename multiple files at once without being forced to use the command line. (see also http://udu.wiki.ubuntu.com/CommandLineDisintegration ) there are various ways this could be implemented and it is up to the developer to decide but please do decide on something rather than nothing. The file explorer in Windows XP provides a fairly straighforward (if slightly inflexible) system, which is described in this article http://graphicssoft.about.com/cs/renamers/ht/renamexp.htm I have also taken a series of screenshots in an attempt to better illustrate the process http://www.maths.tcd.ie/~horkana/batch-rename/ Essentially when users have more than one file selected they are prompted to rename the first file and the other files are renamed to match the same pattern (although the labelling and rudimentary pattern matching could stand to be massively improved). The operation is entirely undoable.
This sounds like an excellent idea! Maybe you could try out how it is figured out which file receives a particular number? Is the alphabetical order decisive or the sorting order? How does it work with manual layout in icon views? The first item always seems to be the "master" (i.e. the one not receiving a number in parenthesis).
Slight addition: My idea would be to just assume alphabetical order for manual layout.
> This sounds like an excellent idea! I'm sorry I didn't file it years ago. Proves that if I dont bother to file requests for these things no one else will. I'll give you some samples, the first item in each set will be exactly what the user would have to type file1.ext file1 (2).ext file1 (3).ext file1 (4).ext file1 (5).ext number 01.ext number 01 (2).ext number 01 (3).ext number 01 (4).ext number 01 (5).ext nametest (10).ext nametest (11).ext nametest (12).ext nametest (13).ext Unless you start with a filename using exactly the number format (N) it will be ignored and the following files will have (2) ... (N) appended to them. If you do use exactly the format they require then the list will go from the where you specify. The system they use is pretty simplistic/stupid, and hopefully someone who implemented this for Nautilus could be a lot smarter about the pattern matching (possibly borrow some algorithms from Gnumeric for predicting the next items in the sequence). Developers keep having to remind me "the perfect is the enemy of the good" when I get carried away with details but i think it is far more important to have something, anything that gets the job done than waiting for a better solution and i fully support whoever is willing to implement any kind of batch renaming functionality. FYI Gthumb has a batch rename tool, which could potentially be reused as an alternative possibly easier to implement solution. If you want more information or alternative suggestions let me know and I'll log into a windows XP machine and suffer the Microsoft crapfest long enough to get the information you need.
Created attachment 47265 [details] [review] Proposed patch (against HEAD). This is a very first show at this issue, so I'm not entirely sure about the architecture. Also the code quality may be a bit poor. The idea would be that the first item in the selection is also the first icon to be renamed and renaming always involves the whole selection. Turns out that the list view (probably GtkListView's fault) unselects all rows except the one being renamed, so this currently only works for the icon view. I'd appreciate if you could apply this patch and give some feedback. If the first item is a directory, we currently use the directory name and append " (i)" for the subsequent items. But if the first item is not a directory, we construct the filename of the subsequent items as "basename (i).extension". Both patterns are always applied to subsequent items, no matter whether it is a directory or file. We could be way more smart here. Another bug I've noticed is that the order of selected files does not depend on the currently active sort order, which may in some cases make the "rename master" just one of its dups and make one of the "rename slaves" turn into "bastename" or "basename.extension" (the "master's" filename).
I'm hoping to test the patch real soon now and it is gradually creaping up my priority list. I promise to get to it before the end of the week.
Any news on your priority list? :) Now that we have fixed the selection order, this should even be useful.
Note that this should be rather handled by specialised app, just well integrated with Nautilus. I happen to have written one :), although it's still unfinished. Right now, the latest released version (0.5) is not suitable for everyday use (it's known to destroy data under many circumstances), and 0.8, which will have that fixed is still not out yet. To be developed remain (of the things that matter for Nautilus) 1) Proper nautilus extension (it's simply a script right now) 2) Way for ordinary user to easily build name templates, the syntax used is fairly straightforward, but could still look cryptic for someone not accustomed. If you want, check it out at http://mathrick.org/software/purrr.html (0.5) and http://mathrick.org/software/0.8/purrr.html (0.8 prerelease) Oh, and with selection order fixed, it'll be much more useful, before you needed to always manually force -1 as counter step to overcome reversed direction.
FYI there exists a "perfect" solution for batch renaming. It's an Artificial Intelligence technique called Programming By Example (PBE). This article shows a simple learning algorithm for watching how the user edits the first rename, and then generalizing it to repeat the renaming on all the other files: Learning Repetitive Text-editing Procedures with SMARTedit http://web.media.mit.edu/~lieber/Your-Wish/11-Lau.pdf This visual representation of regular expressions is also relevant - it would be much better than text "templates" for creating the new names: SWYN: A Visual Representation for Regular Expressions http://web.media.mit.edu/~lieber/Your-Wish/13-Blackwell.pdf Those articles belong to a PBE book called "Your wish is my command". The whole book contents are published online: http://web.media.mit.edu/~lieber/Your-Wish/#ToC
TuringTest: Thanks for the links, I browsed the documents you pointed out, but in this context I couldn't see any practical link to SMARTedit. An algorithm for pattern detection might indeed be desirable, for instance we could recognize a string composed of a distinct sequence of numbers and other text, like "foo01bar", decompose it and increment the "01" for the other candidates, i.e. an arbitrary set of selected files would be renamed according to the first example.
*** Bug 334138 has been marked as a duplicate of this bug. ***
*** Bug 347187 has been marked as a duplicate of this bug. ***
Created attachment 69039 [details] Possible design for Rename dialog The argument that this should be done by an external program would make more sense if Nautilus didn't already have a "Rename…" menu item. Since it does, making that menu item available when multiple items are selected isn't making Nautilus's interface more complex, it's making it more consistent. This is an updated version of a design I came up with a while ago for a Rename dialog.
Matthew, that is a great start. Does it allow for numbering in a certain format, however? Suppose I had files with a variety of names, such as eiffeltower.jpg, louvre.jpg and notredame.jpg and so on and I wished to rename these in them format triptoparis00001.jpg, triptoparis00002.jpg and triptoparis00003.jpg Does it allow renumbering and incrementing at the smae time? Irfanview would do this by allowing you to set it in the format: triptoparisnnnnn whereby the "nnnnn" is the number of digits wanted. IT would then increment by one each time. I was a little unclear as to whether your design allowed this facility. Thanks and keep up the great work, my fellow GNOME community members, David L S Hobart, TASMANIA
*** Bug 354080 has been marked as a duplicate of this bug. ***
*** Bug 379302 has been marked as a duplicate of this bug. ***
*** Bug 431831 has been marked as a duplicate of this bug. ***
From duplicate bug #431831: "When using single-click policy it's quite difficult to get one file selected to have it renamed when trying to do that on a number of files in one folder - you have to click outside of any file icon (which is hard when having a lot of files in one folder) or ctrl+click to deselect the previous file. This one is related to bug 306489, but I'd like the solution to be user-configured - if one wants to use multi-file rename - ok, but I'd like it to behave like this - when clicking F2 with multiple files selected, only the last one selected gets renamed. That should be fairly easy to implement."
I once had an idea about another way to handle multiple file rename (at least some of it). If you select a number of files and bring up the property dialog we'd try to determine the parts of the filenames that are the same, and then have the name entry contain uneditable XXX characters for the non-common parts, and only allow you to edit the common parts.
Just for reference, here[1] is an utility for MacOS, NameChanger. It should be an "external" utility, i.e. not integrated in Finder. It provides a simple mode and advanced mode. Take a look to the demo video too, showing the ability to change the intial order using an icon (pre)view. About Nautilus, I think the optimal solution could be provide an external well beaving, full featured tool in GNOME Desktop and let it integrate in Nautilus, instead try to fix this directly in Nautilus. I don't think we want to include in Nautilus code something like NameChanger, but also users will love - no need, love :) - to have an ultra-mega-special tool like this. And they'll complain about the missing advanced feautures. I think the best could be something like the Archive <-> File Roller approach: if you want create a new archive, you can select files in Nautilus and choose "Create Archive...": this will bring you a small dialog with limited options (name, type, location). But you can also directly open File Roller and add files and folders from differente locations to a new empty archive, changing their names or adding directories too. Of course could be really good if we can always use the existing "Rename.." menu item. So, summarizing, Nautilus + NYNACF[2] should: * 1 file selected + Rename --> current Nautilus behavior * N files selected + Rename --> invoke NYNACF in simple mode (something like apply the same template to all files) * NYNACF + d'n'd --> all advanced renaming options that users love to have [1] http://web.mac.com/mickeyroberson/MRR_Software/NameChanger.html [2] == No Yet Name Application to Change Filenames
*** Bug 563223 has been marked as a duplicate of this bug. ***
I fully agree to #19, that an approach similar to File Roller or Seahorse would be great. I wonder how Nautilus would start that external application. This is different to extensions like Seahorse or File Roller, in that the external application doesn't add menu entries or the like, but is supposed to be triggered on Nautilus very own "Rename" action in the case of multiple files being selected. That has some implications, for example that by design at most one such tool can be registered at any given time. I propose the following integration, which roughly resembles "preferred applications" desktop settings: Nautilus could add a string-valued GSettings key like "bulk-rename-application". When not empty, the Rename action stays sensitive even when multiple files are selected. On activation, Nautilus would execute "$KEY_VALUE URI1 URI2 [URI3 URI4 ...]". Bulk rename applications could register themselves with Nautilus during their installation or first start by setting that key. Nautilus could maybe clear that key on failed invokation attempts. I may have a look at creating a patch for that if the proposal is acceptable.
Created attachment 171786 [details] [review] Add possibility to register an external bulk rename tool If the new setting "bulk-rename-tool" in org.gnome.nautilus.preferences is set to a non-empty string, it is treated as a bulk renamer and gets invoked on a Rename action if multiple files are selected. Nautilus appends a space separated list of URIs of all selected files to the command string.
Review of attachment 171786 [details] [review]: Hi Holger, I quite like having this working with external applications targeting power-users. Could you link me to one you think is nice to use so that I can try it? (or are you developing one application of this kind yourself? :)) The approach seems fine, I inlined some comments about the code below. ::: src/file-manager/fm-directory-view.c @@ +1888,3 @@ + +static void +update_rename_button_sensitivity_bulk_rename_changed (GSettings *settings, gchar *key, FMDirectoryView *view) There's no need to update the actions here, as you would update them anyway next time real_update_menus() is called (i.e. when the selection changes, which is what we care about); see below for a longer explanation. If you remove this callback, the prototype for _can_rename_file() you added at the beginning of the file would go away. @@ +6141,3 @@ + quoted_parameter = g_shell_quote (parameter); + g_free (parameter); + tmp = g_strconcat (full_cmd, " ", quoted_parameter, NULL); It's probably easier to use a GString for this kind of operations. @@ +8570,3 @@ + g_signal_connect (nautilus_preferences, + "changed::" NAUTILUS_PREFERENCES_BULK_RENAME_TOOL, + G_CALLBACK (bulk_rename_tool_setting_changed_callback), NULL); AFAICS this will connect a signal handler every time real_update_menus() is called, which is wrong. I think what I would do is: - don't connect to any 'changed' signal coming from GSettings - in have_bulk_rename_tool(), update your local stripped copy of the GSettings string - in real_update_menus(), do basically what you do now, except for the if (block_rename_tool == NULL) {} block, which would go away. You could also connect to the 'changed' signal in _init_view_iface() as you do now, instead of fetching the string from the settings at every menu refresh, but you would just update the stripped string copy there.
Created attachment 172512 [details] [review] Add possibility to register an external bulk rename tool If the new setting "bulk-rename-tool" in org.gnome.nautilus.preferences is set to a non-empty string, it is treated as a bulk renamer and gets invoked on a Rename action if multiple files are selected. Nautilus appends a space separated list of URIs of all selected files to the command string.
Thanks a lot for reviewing, Cosimo! > Could you link me to one you think is nice to use so > that I can try it? Any bulk renamer that can handle URIs on the command line should work fine (e.g. "thunar -B" for local URIs). > (or are you developing one application of this kind > yourself? :)) Actually, I am (though it is a Python app, and currently a bit stalled on gobject introspection porting). Basically, it's supposed to work like Luca wrote in #19. The idea is that the simple mode defaults to what Alex describes in #18 and changes common name substring if there is one, and offers some simple common options if that's not the case ("Replace spaces with underscores" kind of stuff) - plus a "Show me the full thing" button that opens the bulk renamer in normal mode. > There's no need to update the actions here, as you would update them anyway > next time real_update_menus() is called (i.e. when the selection changes, which > is what we care about); see below for a longer explanation. Actually, I was aware of that. The callback was supposed to update the action sensitivity of the current selection when the GSettings key changes. But you're probably right that this is a bit of an overkill. I removed it from the updated patch. > It's probably easier to use a GString for this kind of operations. Updated. > AFAICS this will connect a signal handler every time real_update_menus() is > called, which is wrong. No, only the very first time. The bulk_rename_tool variable is global and shared among all views, and is only NULL at the very beginning. On consecutive calls, if no bulk rename tool is registered, it will be "". Just in case if there is some reason why g_settings_get_string() might return NULL that I am not aware of, I added a safety net in bulk_rename_tool_setting_changed_callback(). Otherwise, I left it as it was (except more elaborate comments). Please don't hesitate to throw this one back to me if I still didn't get it :-) > I think what I would do is: > - don't connect to any 'changed' signal coming from GSettings > - in have_bulk_rename_tool(), update your local stripped copy of the GSettings > string > - in real_update_menus(), do basically what you do now, except for the if > (block_rename_tool == NULL) {} block, which would go away. Hm, I was thinking about that too, but changing the selection is a very common operation. I understand that GSettings is optimized for reading, but I was hesitant if it's really a good idea to read that rather exotic key with high frequency - especially when it's not really necessary.
Review of attachment 172512 [details] [review]: Some other comments on the code. ::: libnautilus-private/org.gnome.nautilus.gschema.xml.in @@ +241,3 @@ <_description>If set to true, then hidden files are shown by default in the file manager. Hidden files are either dotfiles, listed in the folder's .hidden file or backup files ending with a tilde (~).</_description> </key> + <key name="bulk-rename-tool" type="s"> I think you should make it clearer whether this should be a full path to an executable file or the name of one in $PATH. It could also be safe to allow both, but this should be made clearer in the description. Note that in both cases, I think the setting should become an array of bytes ('ay' signature), as we are storing a filename, not an UTF-8 string. ::: src/file-manager/fm-directory-view.c @@ +154,3 @@ static int scripts_directory_uri_length; +static char *bulk_rename_tool; If you use this global variable, you should initialize it to NULL here. @@ +1882,3 @@ +have_bulk_rename_tool () +{ + return (bulk_rename_tool && (*bulk_rename_tool != '\0')); Could you please use "== NULL" and "!= NULL" explicitly when you check pointers? I know nautilus' coding style is not consistent about this, but we should do that for new code. @@ +6103,3 @@ + cmd = g_string_new (bulk_rename_tool); + for (walk = selection; walk; walk = walk->next) { + file = NAUTILUS_FILE (walk->data); You don't need to cast walk->data. @@ +6122,3 @@ + +static void +bulk_rename_tool_setting_changed_callback (GSettings *settings) The signature of the "changed" signal of GSettings is (GSettings *, gchar *, gpointer), so this function should use that. @@ +6150,3 @@ + } + } + else { Coding style: the curly braces and 'else' should all be on the same line. @@ +8545,3 @@ + gtk_action_set_sensitive (action, have_bulk_rename_tool ()); + } + else { Ditto.
(In reply to comment #25) > > AFAICS this will connect a signal handler every time real_update_menus() is > > called, which is wrong. > > No, only the very first time. The bulk_rename_tool variable is global and > shared > among all views, and is only NULL at the very beginning. On consecutive calls, > if no bulk rename tool is registered, it will be "". > > Just in case if there is some reason why g_settings_get_string() might > return NULL that I am not aware of, I added a safety net in > bulk_rename_tool_setting_changed_callback(). Otherwise, I left it as it > was (except more elaborate comments). Please don't hesitate to throw this one > back to me if I still didn't get it :-) Yeah, you're right, that's connected only once. Although... > > I think what I would do is: > > - don't connect to any 'changed' signal coming from GSettings > > - in have_bulk_rename_tool(), update your local stripped copy of the GSettings > > string > > - in real_update_menus(), do basically what you do now, except for the if > > (block_rename_tool == NULL) {} block, which would go away. > > Hm, I was thinking about that too, but changing the selection is a very common > operation. I understand that GSettings is optimized for reading, but I was > hesitant if it's really a good idea to read that rather exotic key with high > frequency - especially when it's not really necessary. ...that global variable could just go away if you did what I suggested here. real_update_menus() already calls into GSettings many times, so that should not be a performance problem (and if it is, GSettings should be fixed and applications should not workaround it).
Created attachment 173597 [details] [review] Add possibility to register an external bulk rename tool Thanks again for the thorough review, Cosimo. > ...that global variable could just go away if you did what I suggested here. > real_update_menus() already calls into GSettings many times, so that should not > be a performance problem (and if it is, GSettings should be fixed and > applications should not workaround it). I changed the patch so that now, GSettings is queried on selection changes, and got rid of the locally cached copy. > I think you should make it clearer whether this should be a full path to an > executable file or the name of one in $PATH. It could also be safe to allow > both, but this should be made clearer in the description. Description updated (though a native English speaker could surely do a better job). Like other command invokations in Nautilus, the patch uses gdk_spawn_command_line_on_screen(), which searches the path. > Note that in both cases, I think the setting should become an array of bytes > ('ay' signature), as we are storing a filename, not an UTF-8 string. Good point. Done. > +static char *bulk_rename_tool; > If you use this global variable, you should initialize it to NULL here. Not applicable anymore. > Could you please use "== NULL" and "!= NULL" explicitly when you check > pointers? Done. > + file = NAUTILUS_FILE (walk->data); > You don't need to cast walk->data. Done. > The signature of the "changed" signal of GSettings is (GSettings *, gchar *, > gpointer), so this function should use that. Not applicable anymore. > Coding style: the curly braces and 'else' should all be on the same line. Done.
Review of attachment 173597 [details] [review]: Thanks for the patch, looks good.
Attachment 173597 [details] pushed as 27f14a8 - Add possibility to register an external bulk rename tool