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):
|
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):
|
||||||
|
@ -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:
|
||||||
|
@ -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)
|
||||||
|
@ -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))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user