mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Implement renaming of data files
This commit is contained in:
parent
3742aa4364
commit
c52b3175c8
@ -3,12 +3,14 @@
|
|||||||
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import posixpath
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from functools import partial
|
||||||
from qt.core import (
|
from qt.core import (
|
||||||
QAbstractItemView, QAbstractListModel, QComboBox, QDialogButtonBox, QHBoxLayout,
|
QAbstractItemView, QAbstractListModel, QComboBox, QDialogButtonBox, QHBoxLayout,
|
||||||
QIcon, QItemSelection, QItemSelectionModel, QLabel, QListView, QPushButton, QSize,
|
QIcon, QItemSelection, QItemSelectionModel, QLabel, QListView, QPushButton, QSize,
|
||||||
Qt, QVBoxLayout,
|
QStyledItemDelegate, Qt, QTimer, QVBoxLayout, pyqtSignal, sip,
|
||||||
)
|
)
|
||||||
|
|
||||||
from calibre import human_readable
|
from calibre import human_readable
|
||||||
@ -21,6 +23,38 @@ from calibre.gui2.widgets2 import Dialog
|
|||||||
from calibre.utils.icu import primary_sort_key
|
from calibre.utils.icu import primary_sort_key
|
||||||
from calibre.utils.recycle_bin import delete_file
|
from calibre.utils.recycle_bin import delete_file
|
||||||
|
|
||||||
|
NAME_ROLE = Qt.ItemDataRole.UserRole
|
||||||
|
|
||||||
|
|
||||||
|
class Delegate(QStyledItemDelegate):
|
||||||
|
|
||||||
|
rename_requested = pyqtSignal(int, str)
|
||||||
|
|
||||||
|
def setModelData(self, editor, model, index):
|
||||||
|
newname = editor.text()
|
||||||
|
oldname = index.data(NAME_ROLE) or ''
|
||||||
|
if newname != oldname:
|
||||||
|
self.rename_requested.emit(index.row(), newname)
|
||||||
|
|
||||||
|
def setEditorData(self, editor, index):
|
||||||
|
name = index.data(NAME_ROLE) or ''
|
||||||
|
# We do this because Qt calls selectAll() unconditionally on the
|
||||||
|
# editor, and we want only a part of the file name to be selected
|
||||||
|
QTimer.singleShot(0, partial(self.set_editor_data, name, editor))
|
||||||
|
|
||||||
|
def set_editor_data(self, name, editor):
|
||||||
|
if sip.isdeleted(editor):
|
||||||
|
return
|
||||||
|
editor.setText(name)
|
||||||
|
ext_pos = name.rfind('.')
|
||||||
|
slash_pos = name.rfind(os.sep)
|
||||||
|
if slash_pos == -1 and ext_pos > 0:
|
||||||
|
editor.setSelection(0, ext_pos)
|
||||||
|
elif ext_pos > -1 and slash_pos > -1 and ext_pos > slash_pos + 1:
|
||||||
|
editor.setSelection(slash_pos+1, ext_pos - slash_pos - 1)
|
||||||
|
else:
|
||||||
|
editor.selectAll()
|
||||||
|
|
||||||
|
|
||||||
class Files(QAbstractListModel):
|
class Files(QAbstractListModel):
|
||||||
|
|
||||||
@ -78,10 +112,12 @@ class Files(QAbstractListModel):
|
|||||||
ef = self.files[row]
|
ef = self.files[row]
|
||||||
fmt = ef.relpath.rpartition('.')[-1].lower()
|
fmt = ef.relpath.rpartition('.')[-1].lower()
|
||||||
return self.fi.icon_from_ext(fmt)
|
return self.fi.icon_from_ext(fmt)
|
||||||
|
if role == NAME_ROLE:
|
||||||
|
return self.file_display_name(row)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def flags(self, index):
|
def flags(self, index):
|
||||||
return Qt.ItemFlag.ItemIsSelectable|Qt.ItemFlag.ItemIsEnabled
|
return Qt.ItemFlag.ItemIsSelectable | Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsEditable
|
||||||
|
|
||||||
|
|
||||||
class DataFilesManager(Dialog):
|
class DataFilesManager(Dialog):
|
||||||
@ -110,7 +146,10 @@ class DataFilesManager(Dialog):
|
|||||||
l.addLayout(h)
|
l.addLayout(h)
|
||||||
h.addWidget(la), h.addWidget(sb)
|
h.addWidget(la), h.addWidget(sb)
|
||||||
|
|
||||||
|
self.delegate = d = Delegate(self)
|
||||||
|
d.rename_requested.connect(self.rename_requested, type=Qt.ConnectionType.QueuedConnection)
|
||||||
self.fview = v = QListView(self)
|
self.fview = v = QListView(self)
|
||||||
|
v.setItemDelegate(d)
|
||||||
l.addWidget(v)
|
l.addWidget(v)
|
||||||
self.files = Files(self.db.new_api, self.book_id, parent=v)
|
self.files = Files(self.db.new_api, self.book_id, parent=v)
|
||||||
self.files.resort(self.sort_by.currentIndex())
|
self.files.resort(self.sort_by.currentIndex())
|
||||||
@ -227,6 +266,18 @@ class DataFilesManager(Dialog):
|
|||||||
with self.preserve_state():
|
with self.preserve_state():
|
||||||
self.files.refresh()
|
self.files.refresh()
|
||||||
|
|
||||||
|
def rename_requested(self, idx, new_name):
|
||||||
|
e = self.files.item_at(idx)
|
||||||
|
newrelpath = posixpath.normpath(posixpath.join(DATA_DIR_NAME, new_name.replace(os.sep, '/')))
|
||||||
|
if not newrelpath.startswith(DATA_DIR_NAME + '/'):
|
||||||
|
return error_dialog(self, _('Invalid name'), _('"{}" is not a valid file name').format(new_name), show=True)
|
||||||
|
if e.relpath not in self.db.rename_extra_files(self.book_id, {e.relpath: newrelpath}, replace=False):
|
||||||
|
if question_dialog(self, _('Replace existing file?'), _(
|
||||||
|
'Another data file with the name "{}" already exists. Replace it?').format(new_name)):
|
||||||
|
self.db.rename_extra_files(self.book_id, {e.relpath: newrelpath}, replace=True)
|
||||||
|
with self.preserve_state():
|
||||||
|
self.files.refresh()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
from calibre.gui2 import Application
|
from calibre.gui2 import Application
|
||||||
|
Loading…
x
Reference in New Issue
Block a user