From 05bec937b58bf4fdc1c7ea6bd157482678765a22 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 20 Nov 2016 10:03:01 +0530 Subject: [PATCH] Edit Book: Allow bulk changing of file extension for selected files by right clicking ont he selected files in the file browser. Fixes #1643232 [Enhancement request : bulk rename file extensions](https://bugs.launchpad.net/calibre/+bug/1643232) --- manual/edit.rst | 4 ++ src/calibre/gui2/tweak_book/file_list.py | 48 +++++++++++++++++------- 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/manual/edit.rst b/manual/edit.rst index 7f6dd3240e..20822e1cd7 100644 --- a/manual/edit.rst +++ b/manual/edit.rst @@ -104,6 +104,10 @@ Ctrl key and clicking the files. Then right click and select :guilabel:`Bulk rename`. Enter a prefix and what number you would like the automatic numbering to start at, click OK and you are done. +Finally, you can bulk change the file extension for all selected files. Select +multiple files, as above, and right click and choose :guilabel:`Change the file +extension for the selected files`. + Merging files ^^^^^^^^^^^^^^ diff --git a/src/calibre/gui2/tweak_book/file_list.py b/src/calibre/gui2/tweak_book/file_list.py index 0741357af8..13477364b5 100644 --- a/src/calibre/gui2/tweak_book/file_list.py +++ b/src/calibre/gui2/tweak_book/file_list.py @@ -6,7 +6,7 @@ from __future__ import (unicode_literals, division, absolute_import, __license__ = 'GPL v3' __copyright__ = '2013, Kovid Goyal ' -import os +import os, posixpath from binascii import hexlify from collections import OrderedDict, defaultdict, Counter from functools import partial @@ -15,7 +15,7 @@ import sip from PyQt5.Qt import ( QWidget, QTreeWidget, QGridLayout, QSize, Qt, QTreeWidgetItem, QIcon, QFont, QStyledItemDelegate, QStyle, QPixmap, QPainter, pyqtSignal, QMenu, QTimer, - QDialogButtonBox, QDialog, QLabel, QLineEdit, QVBoxLayout, QScrollArea, + QDialogButtonBox, QDialog, QLabel, QLineEdit, QVBoxLayout, QScrollArea, QInputDialog, QRadioButton, QFormLayout, QSpinBox, QListWidget, QListWidgetItem, QCheckBox) from calibre import human_readable, sanitize_file_name_unicode, plugins @@ -460,6 +460,7 @@ class FileList(QTreeWidget): m.addSeparator() if num > 1: m.addAction(QIcon(I('modified.png')), _('&Bulk rename the selected files'), self.request_bulk_rename) + m.addAction(QIcon(I('modified.png')), _('Change the file extension for the selected files'), self.request_change_ext) m.addAction(QIcon(I('trash.png')), ngettext( '&Delete the selected file', '&Delete the {} selected files', num).format(num), self.request_delete) m.addSeparator() @@ -525,7 +526,7 @@ class FileList(QTreeWidget): else: return QTreeWidget.keyPressEvent(self, ev) - def request_bulk_rename(self): + def request_rename_common(self): if not current_container().SUPPORTS_FILENAMES: error_dialog(self, _('Cannot rename'), _( '%s books do not support file renaming as they do not use file names' @@ -535,19 +536,38 @@ class FileList(QTreeWidget): names = {unicode(item.data(0, NAME_ROLE) or '') for item in self.selectedItems()} bad = names & current_container().names_that_must_not_be_changed if bad: - return error_dialog(self, _('Cannot rename'), + error_dialog(self, _('Cannot rename'), _('The file(s) %s cannot be renamed.') % ('%s' % ', '.join(bad)), show=True) + return names = sorted(names, key=self.index_of_name) - categories = Counter(unicode(item.data(0, CATEGORY_ROLE) or '') for item in self.selectedItems()) - fmt, num = get_bulk_rename_settings(self, len(names), category=categories.most_common(1)[0][0]) - if fmt is not None: - def change_name(name, num): - parts = name.split('/') - base, ext = parts[-1].rpartition('.')[0::2] - parts[-1] = (fmt % num) + '.' + ext - return '/'.join(parts) - name_map = {n:change_name(n, num + i) for i, n in enumerate(names)} - self.bulk_rename_requested.emit(name_map) + return names + + def request_bulk_rename(self): + names = self.request_rename_common() + if names is not None: + categories = Counter(unicode(item.data(0, CATEGORY_ROLE) or '') for item in self.selectedItems()) + fmt, num = get_bulk_rename_settings(self, len(names), category=categories.most_common(1)[0][0]) + if fmt is not None: + def change_name(name, num): + parts = name.split('/') + base, ext = parts[-1].rpartition('.')[0::2] + parts[-1] = (fmt % num) + '.' + ext + return '/'.join(parts) + name_map = {n:change_name(n, num + i) for i, n in enumerate(names)} + self.bulk_rename_requested.emit(name_map) + + def request_change_ext(self): + names = self.request_rename_common() + if names is not None: + text, ok = QInputDialog.getText(self, _('Rename files'), _('New file extension:')) + if ok and text: + ext = text.lstrip('.') + + def change_name(name): + base = posixpath.splitext(name)[0] + return base + '.' + ext + name_map = {n:change_name(n) for n in names} + self.bulk_rename_requested.emit(name_map) @property def selected_names(self):