mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
UI for renaming files
This commit is contained in:
parent
7f2a1f99da
commit
e643e34688
@ -237,6 +237,10 @@ class Container(object): # {{{
|
||||
def names_that_must_not_be_removed(self):
|
||||
return {self.opf_name}
|
||||
|
||||
@property
|
||||
def names_that_must_not_be_changed(self):
|
||||
return set()
|
||||
|
||||
def parse_xml(self, data):
|
||||
data, self.used_encoding = xml_to_unicode(
|
||||
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):
|
||||
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):
|
||||
# Handle removal of obfuscated fonts
|
||||
if name == 'META-INF/encryption.xml':
|
||||
@ -870,6 +878,9 @@ class AZW3Container(Container):
|
||||
def path_to_ebook(self):
|
||||
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):
|
||||
|
@ -11,7 +11,7 @@ from calibre.gui2.dialogs.confirm_delete_ui import 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)
|
||||
self.setupUi(self)
|
||||
|
||||
@ -19,16 +19,18 @@ class Dialog(QDialog, Ui_Dialog):
|
||||
self.name = name
|
||||
self.again.stateChanged.connect(self.toggle)
|
||||
self.buttonBox.setFocus(Qt.OtherFocusReason)
|
||||
self.config_set = config_set
|
||||
|
||||
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,
|
||||
show_cancel_button=True, confirm_msg=None):
|
||||
if not dynamic.get(confirm_config_name(name), True):
|
||||
show_cancel_button=True, confirm_msg=None, config_set=None):
|
||||
config_set = config_set or dynamic
|
||||
if not config_set.get(confirm_config_name(name), True):
|
||||
return True
|
||||
d = Dialog(msg, name, parent)
|
||||
d = Dialog(msg, name, parent, config_set=config_set)
|
||||
d.label.setPixmap(QPixmap(I(pixmap)))
|
||||
d.setWindowIcon(QIcon(I(pixmap)))
|
||||
if title is not None:
|
||||
|
@ -6,17 +6,19 @@ from __future__ import (unicode_literals, division, absolute_import,
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
import tempfile, shutil, sys
|
||||
import tempfile, shutil, sys, os
|
||||
|
||||
from PyQt4.Qt import (
|
||||
QObject, QApplication, QDialog, QGridLayout, QLabel, QSize, Qt,
|
||||
QDialogButtonBox, QIcon, QTimer, QPixmap)
|
||||
|
||||
from calibre import prints
|
||||
from calibre.gui2 import error_dialog, choose_files, question_dialog, info_dialog
|
||||
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.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.undo import GlobalUndoHistory
|
||||
from calibre.gui2.tweak_book.save import SaveManager
|
||||
@ -34,8 +36,10 @@ class Boss(QObject):
|
||||
|
||||
def __call__(self, gui):
|
||||
self.gui = gui
|
||||
gui.file_list.delete_requested.connect(self.delete_requested)
|
||||
gui.file_list.reorder_spine.connect(self.reorder_spine)
|
||||
fl = gui.file_list
|
||||
fl.delete_requested.connect(self.delete_requested)
|
||||
fl.reorder_spine.connect(self.reorder_spine)
|
||||
fl.rename_requested.connect(self.rename_requested)
|
||||
|
||||
def mkdtemp(self):
|
||||
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
|
||||
# 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):
|
||||
self.gui.action_save.setEnabled(False)
|
||||
tdir = tempfile.mkdtemp(prefix='save-%05d-' % self.container_count, dir=self.tdir)
|
||||
|
@ -27,6 +27,18 @@ NBSP = '\xa0'
|
||||
|
||||
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):
|
||||
ans = QStyledItemDelegate.sizeHint(self, option, index)
|
||||
top_level = not index.parent().isValid()
|
||||
@ -58,15 +70,18 @@ class FileList(QTreeWidget):
|
||||
|
||||
delete_requested = pyqtSignal(object, object)
|
||||
reorder_spine = pyqtSignal(object)
|
||||
rename_requested = pyqtSignal(object, object)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
QTreeWidget.__init__(self, parent)
|
||||
self.delegate = ItemDelegate(self)
|
||||
self.delegate.rename_requested.connect(self.rename_requested)
|
||||
self.setTextElideMode(Qt.ElideMiddle)
|
||||
self.setItemDelegate(self.delegate)
|
||||
self.setIconSize(QSize(16, 16))
|
||||
self.header().close()
|
||||
self.setDragEnabled(True)
|
||||
self.setEditTriggers(self.EditKeyPressed)
|
||||
self.setSelectionMode(self.ExtendedSelection)
|
||||
self.viewport().setAcceptDrops(True)
|
||||
self.setDropIndicatorShown(True)
|
||||
@ -200,6 +215,7 @@ class FileList(QTreeWidget):
|
||||
item.setData(0, Qt.DecorationRole, icon)
|
||||
|
||||
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):
|
||||
imt = container.mime_map.get(name, guess_type(name))
|
||||
@ -209,6 +225,8 @@ class FileList(QTreeWidget):
|
||||
flags = Qt.ItemIsEnabled | Qt.ItemIsSelectable
|
||||
if category == 'text':
|
||||
flags |= Qt.ItemIsDragEnabled
|
||||
if name not in cannot_be_renamed:
|
||||
flags |= Qt.ItemIsEditable
|
||||
item.setFlags(flags)
|
||||
item.setStatusTip(0, _('Full path: ') + name)
|
||||
item.setData(0, NAME_ROLE, name)
|
||||
@ -313,6 +331,7 @@ class FileListWidget(QWidget):
|
||||
|
||||
delete_requested = pyqtSignal(object, object)
|
||||
reorder_spine = pyqtSignal(object)
|
||||
rename_requested = pyqtSignal(object, object)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
QWidget.__init__(self, parent)
|
||||
@ -320,7 +339,7 @@ class FileListWidget(QWidget):
|
||||
self.file_list = FileList(self)
|
||||
self.layout().addWidget(self.file_list)
|
||||
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))
|
||||
for x in ('delete_done',):
|
||||
setattr(self, x, getattr(self.file_list, x))
|
||||
|
Loading…
x
Reference in New Issue
Block a user