UI for renaming files

This commit is contained in:
Kovid Goyal 2013-10-19 15:04:01 +05:30
parent 7f2a1f99da
commit e643e34688
4 changed files with 66 additions and 11 deletions

View File

@ -237,6 +237,10 @@ class Container(object): # {{{
def names_that_must_not_be_removed(self): def names_that_must_not_be_removed(self):
return {self.opf_name} return {self.opf_name}
@property
def names_that_must_not_be_changed(self):
return set()
def parse_xml(self, data): def parse_xml(self, data):
data, self.used_encoding = xml_to_unicode( data, self.used_encoding = xml_to_unicode(
data, strip_encoding_pats=True, assume_utf8=True, resolve_entities=True) data, strip_encoding_pats=True, assume_utf8=True, resolve_entities=True)
@ -678,6 +682,10 @@ class EpubContainer(Container):
def names_that_must_not_be_removed(self): def names_that_must_not_be_removed(self):
return super(EpubContainer, self).names_that_must_not_be_removed | {'META-INF/container.xml'} return super(EpubContainer, self).names_that_must_not_be_removed | {'META-INF/container.xml'}
@property
def names_that_must_not_be_changed(self):
return super(EpubContainer, self).names_that_must_not_be_changed | {'META-INF/' + x for x in self.META_INF}
def remove_item(self, name): def remove_item(self, name):
# Handle removal of obfuscated fonts # Handle removal of obfuscated fonts
if name == 'META-INF/encryption.xml': if name == 'META-INF/encryption.xml':
@ -870,6 +878,9 @@ class AZW3Container(Container):
def path_to_ebook(self): def path_to_ebook(self):
return self.pathtoepub return self.pathtoepub
@property
def names_that_must_not_be_changed(self):
return set(self.name_path_map)
# }}} # }}}
def get_container(path, log=None, tdir=None): def get_container(path, log=None, tdir=None):

View File

@ -11,7 +11,7 @@ from calibre.gui2.dialogs.confirm_delete_ui import Ui_Dialog
class Dialog(QDialog, Ui_Dialog): class Dialog(QDialog, Ui_Dialog):
def __init__(self, msg, name, parent): def __init__(self, msg, name, parent, config_set=dynamic):
QDialog.__init__(self, parent) QDialog.__init__(self, parent)
self.setupUi(self) self.setupUi(self)
@ -19,16 +19,18 @@ class Dialog(QDialog, Ui_Dialog):
self.name = name self.name = name
self.again.stateChanged.connect(self.toggle) self.again.stateChanged.connect(self.toggle)
self.buttonBox.setFocus(Qt.OtherFocusReason) self.buttonBox.setFocus(Qt.OtherFocusReason)
self.config_set = config_set
def toggle(self, *args): def toggle(self, *args):
dynamic[confirm_config_name(self.name)] = self.again.isChecked() self.config_set[confirm_config_name(self.name)] = self.again.isChecked()
def confirm(msg, name, parent=None, pixmap='dialog_warning.png', title=None, def confirm(msg, name, parent=None, pixmap='dialog_warning.png', title=None,
show_cancel_button=True, confirm_msg=None): show_cancel_button=True, confirm_msg=None, config_set=None):
if not dynamic.get(confirm_config_name(name), True): config_set = config_set or dynamic
if not config_set.get(confirm_config_name(name), True):
return True return True
d = Dialog(msg, name, parent) d = Dialog(msg, name, parent, config_set=config_set)
d.label.setPixmap(QPixmap(I(pixmap))) d.label.setPixmap(QPixmap(I(pixmap)))
d.setWindowIcon(QIcon(I(pixmap))) d.setWindowIcon(QIcon(I(pixmap)))
if title is not None: if title is not None:

View File

@ -6,17 +6,19 @@ from __future__ import (unicode_literals, division, absolute_import,
__license__ = 'GPL v3' __license__ = 'GPL v3'
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>' __copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
import tempfile, shutil, sys import tempfile, shutil, sys, os
from PyQt4.Qt import ( from PyQt4.Qt import (
QObject, QApplication, QDialog, QGridLayout, QLabel, QSize, Qt, QObject, QApplication, QDialog, QGridLayout, QLabel, QSize, Qt,
QDialogButtonBox, QIcon, QTimer, QPixmap) QDialogButtonBox, QIcon, QTimer, QPixmap)
from calibre import prints from calibre import prints
from calibre.gui2 import error_dialog, choose_files, question_dialog, info_dialog
from calibre.ptempfile import PersistentTemporaryDirectory from calibre.ptempfile import PersistentTemporaryDirectory
from calibre.ebooks.oeb.base import urlnormalize
from calibre.ebooks.oeb.polish.main import SUPPORTED from calibre.ebooks.oeb.polish.main import SUPPORTED
from calibre.ebooks.oeb.polish.container import get_container, clone_container from calibre.ebooks.oeb.polish.container import get_container, clone_container, guess_type
from calibre.gui2 import error_dialog, choose_files, question_dialog, info_dialog
from calibre.gui2.dialogs.confirm_delete import confirm
from calibre.gui2.tweak_book import set_current_container, current_container, tprefs from calibre.gui2.tweak_book import set_current_container, current_container, tprefs
from calibre.gui2.tweak_book.undo import GlobalUndoHistory from calibre.gui2.tweak_book.undo import GlobalUndoHistory
from calibre.gui2.tweak_book.save import SaveManager from calibre.gui2.tweak_book.save import SaveManager
@ -34,8 +36,10 @@ class Boss(QObject):
def __call__(self, gui): def __call__(self, gui):
self.gui = gui self.gui = gui
gui.file_list.delete_requested.connect(self.delete_requested) fl = gui.file_list
gui.file_list.reorder_spine.connect(self.reorder_spine) fl.delete_requested.connect(self.delete_requested)
fl.reorder_spine.connect(self.reorder_spine)
fl.rename_requested.connect(self.rename_requested)
def mkdtemp(self): def mkdtemp(self):
self.container_count += 1 self.container_count += 1
@ -141,6 +145,25 @@ class Boss(QObject):
self.gui.file_list.build(current_container()) # needed as the linear flag may have changed on some items self.gui.file_list.build(current_container()) # needed as the linear flag may have changed on some items
# TODO: If content.opf is open in an editor, reload it # TODO: If content.opf is open in an editor, reload it
def rename_requested(self, oldname, newname):
if guess_type(oldname) != guess_type(newname):
args = os.path.splitext(oldname) + os.path.splitext(newname)
if not confirm(
_('You are changing the file type of {0}<b>{1}</b> to {2}<b>{3}</b>.'
' Doing so can cause problems, are you sure?').format(*args),
'confirm-filetype-change', parent=self.gui, title=_('Are you sure?'),
config_set=tprefs):
return
if urlnormalize(newname) != newname:
if not confirm(
_('The name you have chosen {0} contains special characters, internally'
' it will look like: {1}Try to use only the English alphabet [a-z], numbers [0-9],'
' hyphens and underscores for file names. Other characters can cause problems for '
' different ebook viewers. Are you sure you want to proceed?').format(
'<pre>%s</pre>'%newname, '<pre>%s</pre>' % urlnormalize(newname)),
'confirm-urlunsafe-change', parent=self.gui, title=_('Are you sure?'), config_set=tprefs):
return
def save_book(self): def save_book(self):
self.gui.action_save.setEnabled(False) self.gui.action_save.setEnabled(False)
tdir = tempfile.mkdtemp(prefix='save-%05d-' % self.container_count, dir=self.tdir) tdir = tempfile.mkdtemp(prefix='save-%05d-' % self.container_count, dir=self.tdir)

View File

@ -27,6 +27,18 @@ NBSP = '\xa0'
class ItemDelegate(QStyledItemDelegate): # {{{ class ItemDelegate(QStyledItemDelegate): # {{{
rename_requested = pyqtSignal(object, object)
def setEditorData(self, editor, index):
name = unicode(index.data(NAME_ROLE).toString())
editor.setText(name)
def setModelData(self, editor, model, index):
newname = unicode(editor.text())
oldname = unicode(index.data(NAME_ROLE).toString())
if newname != oldname:
self.rename_requested.emit(oldname, newname)
def sizeHint(self, option, index): def sizeHint(self, option, index):
ans = QStyledItemDelegate.sizeHint(self, option, index) ans = QStyledItemDelegate.sizeHint(self, option, index)
top_level = not index.parent().isValid() top_level = not index.parent().isValid()
@ -58,15 +70,18 @@ class FileList(QTreeWidget):
delete_requested = pyqtSignal(object, object) delete_requested = pyqtSignal(object, object)
reorder_spine = pyqtSignal(object) reorder_spine = pyqtSignal(object)
rename_requested = pyqtSignal(object, object)
def __init__(self, parent=None): def __init__(self, parent=None):
QTreeWidget.__init__(self, parent) QTreeWidget.__init__(self, parent)
self.delegate = ItemDelegate(self) self.delegate = ItemDelegate(self)
self.delegate.rename_requested.connect(self.rename_requested)
self.setTextElideMode(Qt.ElideMiddle) self.setTextElideMode(Qt.ElideMiddle)
self.setItemDelegate(self.delegate) self.setItemDelegate(self.delegate)
self.setIconSize(QSize(16, 16)) self.setIconSize(QSize(16, 16))
self.header().close() self.header().close()
self.setDragEnabled(True) self.setDragEnabled(True)
self.setEditTriggers(self.EditKeyPressed)
self.setSelectionMode(self.ExtendedSelection) self.setSelectionMode(self.ExtendedSelection)
self.viewport().setAcceptDrops(True) self.viewport().setAcceptDrops(True)
self.setDropIndicatorShown(True) self.setDropIndicatorShown(True)
@ -200,6 +215,7 @@ class FileList(QTreeWidget):
item.setData(0, Qt.DecorationRole, icon) item.setData(0, Qt.DecorationRole, icon)
ok_to_be_unmanifested = container.names_that_need_not_be_manifested ok_to_be_unmanifested = container.names_that_need_not_be_manifested
cannot_be_renamed = container.names_that_must_not_be_changed
def create_item(name, linear=None): def create_item(name, linear=None):
imt = container.mime_map.get(name, guess_type(name)) imt = container.mime_map.get(name, guess_type(name))
@ -209,6 +225,8 @@ class FileList(QTreeWidget):
flags = Qt.ItemIsEnabled | Qt.ItemIsSelectable flags = Qt.ItemIsEnabled | Qt.ItemIsSelectable
if category == 'text': if category == 'text':
flags |= Qt.ItemIsDragEnabled flags |= Qt.ItemIsDragEnabled
if name not in cannot_be_renamed:
flags |= Qt.ItemIsEditable
item.setFlags(flags) item.setFlags(flags)
item.setStatusTip(0, _('Full path: ') + name) item.setStatusTip(0, _('Full path: ') + name)
item.setData(0, NAME_ROLE, name) item.setData(0, NAME_ROLE, name)
@ -313,6 +331,7 @@ class FileListWidget(QWidget):
delete_requested = pyqtSignal(object, object) delete_requested = pyqtSignal(object, object)
reorder_spine = pyqtSignal(object) reorder_spine = pyqtSignal(object)
rename_requested = pyqtSignal(object, object)
def __init__(self, parent=None): def __init__(self, parent=None):
QWidget.__init__(self, parent) QWidget.__init__(self, parent)
@ -320,7 +339,7 @@ class FileListWidget(QWidget):
self.file_list = FileList(self) self.file_list = FileList(self)
self.layout().addWidget(self.file_list) self.layout().addWidget(self.file_list)
self.layout().setContentsMargins(0, 0, 0, 0) self.layout().setContentsMargins(0, 0, 0, 0)
for x in ('delete_requested', 'reorder_spine'): for x in ('delete_requested', 'reorder_spine', 'rename_requested'):
getattr(self.file_list, x).connect(getattr(self, x)) getattr(self.file_list, x).connect(getattr(self, x))
for x in ('delete_done',): for x in ('delete_done',):
setattr(self, x, getattr(self.file_list, x)) setattr(self, x, getattr(self.file_list, x))