Skip to content

Commit

Permalink
allow rename of case varaints and greatly speed up bulk renaming
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinhendricks committed Apr 7, 2024
1 parent e3b0fea commit e9f3ffa
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 33 deletions.
2 changes: 2 additions & 0 deletions ChangeLog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ Pre-2.2.0
(in milliseconds) to control Preview updating intervals valid range 100 - 10000
default value: 1000, will be set once at Sigil startup.
- Add new Find and Replace targets: Selected SVG files, Selected Javascript Files, and Selected Misc XML
- Add ability to rename files when new name is just a case variant on case insensitive filesystems

Bug Fixes
- add in Bulk Resource Move and use bulk delete to handle epubs with thousands of files
- add in Bulk Resource Rename to handle epubs with thousands of files
- In FR, if Replace has focus and Find done, CodeView should get focus (Thanks BeckyEbook)
- Fix dark mode Toolbar continuation symbols for Windows (Thank you BeckyEbook)

Expand Down
24 changes: 23 additions & 1 deletion src/BookManipulation/FolderKeeper.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/************************************************************************
**
** Copyright (C) 2015-2023 Kevin B. Hendricks, Stratford, Ontario, Canada
** Copyright (C) 2015-2024 Kevin B. Hendricks, Stratford, Ontario, Canada
** Copyright (C) 2009-2011 Strahinja Markovic <[email protected]>
**
** This file is part of Sigil.
Expand Down Expand Up @@ -595,6 +595,28 @@ void FolderKeeper::RemoveResource(const Resource *resource)
emit ResourceRemoved(resource);
}


void FolderKeeper::BulkRenameResources(const QList<Resource *> resources, const QStringList &newnames)
{
bool in_bulk = true;
QHash<QString, Resource*> renamedDict;
for (int i=0; i < resources.size(); i++) {
Resource * rsc = resources.at(i);
QString newnm = newnames.at(i);
QString oldbookpath = rsc->GetRelativePath();
bool success = rsc->RenameTo(newnm, in_bulk);
if (success) {
renamedDict[oldbookpath] = rsc;
QString newbookpath = rsc->GetRelativePath();
m_Path2Resource.remove(oldbookpath);
m_Path2Resource[newbookpath] = rsc;
}
}
m_OPF->BulkResourcesRenamed(renamedDict);
updateShortPathNames();
}


void FolderKeeper::ResourceRenamed(const Resource *resource, const QString &old_full_path)
{
// Renaming means the resource book path has changed and so we need to update it
Expand Down
2 changes: 2 additions & 0 deletions src/BookManipulation/FolderKeeper.h
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,8 @@ public slots:

void BulkMoveResources(const QList<Resource *>resources, const QStringList &newpaths);

void BulkRenameResources(const QList<Resource *> resources, const QStringList &newnames);

private slots:

/**
Expand Down
92 changes: 64 additions & 28 deletions src/MainUI/OPFModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -348,62 +348,86 @@ bool OPFModel:: RenameResourceList(const QList<Resource *> &resources, const QSt
QApplication::setOverrideCursor(Qt::WaitCursor);
QStringList not_renamed;
QHash<QString, QString> update;
QList<Resource*> bulk_rename;
QStringList newnames;
QStringList oldpaths;
SettingsStore ss;
int i = 0;
foreach(Resource * resource, resources) {
QString old_bookpath = resource->GetRelativePath();
QString old_filename = resource->Filename();
QString extension = old_filename.right(old_filename.length() - old_filename.lastIndexOf('.'));

QString new_filename = new_filenames.at(i++);
QString new_filename_with_extension = new_filename;
QString oldbookpath = resource->GetRelativePath();
QString oldfilename = resource->Filename();
QString extension = oldfilename.right(oldfilename.length() - oldfilename.lastIndexOf('.'));
QString newfilename = new_filenames.at(i++);
QString newfilename_with_extension = newfilename;

// do not rename files in META-INF
if (old_bookpath.startsWith("META-INF/")) continue;
if (oldbookpath.startsWith("META-INF/")) continue;

if (!new_filename.contains('.')) {
new_filename_with_extension.append(extension);
if (!newfilename.contains('.')) {
newfilename_with_extension.append(extension);
}

if (old_filename == new_filename_with_extension) {
if (oldfilename == newfilename_with_extension) {
continue;
}

if (!FilenameIsValid(old_bookpath, new_filename_with_extension)) {
if (!FilenameIsValid(oldbookpath, newfilename_with_extension)) {
if (ss.showFullPathOn()) {
not_renamed.append(resource->GetRelativePath());
} else {
not_renamed.append(resource->ShortPathName());
}

continue;
}

bool rename_success = false;
// special case the OPFResource and the NCXResource
if (resource->Type() == Resource::OPFResourceType) {
OPFResource* opfres = qobject_cast<OPFResource*>(resource);
if (opfres) {
rename_success = opfres->RenameTo(new_filename_with_extension);
if (opfres->RenameTo(newfilename_with_extension)) {
QString newbookpath = opfres->GetRelativePath();
resource->SetCurrentBookRelPath(oldbookpath);
update[ oldbookpath ] = newbookpath;
} else {
not_renamed.append(oldbookpath);
}
}
} else if (resource->Type() == Resource::NCXResourceType) {
continue;
}

if (resource->Type() == Resource::NCXResourceType) {
NCXResource* ncxres = qobject_cast<NCXResource*>(resource);
if (ncxres) {
rename_success = ncxres->RenameTo(new_filename_with_extension);
if (ncxres->RenameTo(newfilename_with_extension)) {
QString newbookpath = ncxres->GetRelativePath();
resource->SetCurrentBookRelPath(oldbookpath);
update[ oldbookpath ] = newbookpath;
} else {
not_renamed.append(oldbookpath);
}
}
} else {
rename_success = resource->RenameTo(new_filename_with_extension);
continue;
}
if (!rename_success) {
if (ss.showFullPathOn()) {
not_renamed.append(resource->GetRelativePath());

// otherwise add it to the bulk rename list
bulk_rename << resource;
newnames << newfilename_with_extension;
oldpaths << oldbookpath;
}

if (bulk_rename.size() > 0) {
m_Book->GetFolderKeeper()->BulkRenameResources(bulk_rename, newnames);
// add these resources to the update map if successfully renamed
for (int i=0; i < bulk_rename.size(); i++) {
Resource* rsc = bulk_rename.at(i);
if (rsc->GetRelativePath() != oldpaths.at(i)) {
// it was renamed successfully
rsc->SetCurrentBookRelPath(oldpaths.at(i));
update[ oldpaths.at(i) ] = rsc->GetRelativePath();
} else {
not_renamed.append(resource->ShortPathName());
not_renamed.append(oldpaths.at(i));
}
continue;
}

update[ old_bookpath ] = resource->GetRelativePath();
}

if (update.count() > 0) {
Expand All @@ -421,6 +445,7 @@ bool OPFModel:: RenameResourceList(const QList<Resource *> &resources, const QSt
return false;
}


bool OPFModel::MoveResourceList(const QList<Resource *> &resources, const QStringList &new_bookpaths)
{
QApplication::setOverrideCursor(Qt::WaitCursor);
Expand Down Expand Up @@ -765,10 +790,21 @@ bool OPFModel::FilenameIsValid(const QString &old_bookpath, const QString &new_f
QString sdir = Utility::startingDir(old_bookpath);
QString proposed_bookpath = sdir.isEmpty() ? new_filename : sdir + "/" + new_filename;
const QStringList existing_bookpaths = m_Book->GetFolderKeeper()->GetAllBookPaths();
// Using QFileInfo is slow so pretest with a much faster method before using it
if (existing_bookpaths.contains(proposed_bookpath, Qt::CaseInsensitive)) {
Utility::DisplayStdErrorDialog(
tr("The filename \"%1\" is already in use.\n").arg(new_filename));
return false;
// potential issue on filesystems that are case insensitive
QString FullPathToMainFolder = m_Book->GetFolderKeeper()->GetFullPathToMainFolder();
QString orig_full_path = FullPathToMainFolder + "/" + old_bookpath;
QString proposed_full_path = FullPathToMainFolder + "/" + proposed_bookpath;
if (QFileInfo::exists(proposed_full_path)) {
// return false if orig_full_path and proposed_full_path are different files
// but allow through case only variationss from case insensitive filesystems
if (QFileInfo(orig_full_path) != QFileInfo(proposed_full_path)) {
Utility::DisplayStdErrorDialog(
tr("The filename \"%1\" is already in use.\n").arg(new_filename));
return false;
}
}
}
return true;
}
Expand Down
27 changes: 27 additions & 0 deletions src/ResourceObjects/OPFResource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1277,6 +1277,33 @@ void OPFResource::BulkResourcesMoved(const QHash<QString, Resource *> movedDict)
}


void OPFResource::BulkResourcesRenamed(const QHash<QString, Resource *> renamedDict)
{
QWriteLocker locker(&GetLock());
QString opf_start_dir = Utility::startingDir(GetRelativePath());
QString source = CleanSource::ProcessXML(GetText(),"application/oebps-package+xml");
OPFParser p;
p.parse(source);

// a rename should not impact the id so leave the old unique manifest id unchanged
for (int i=0; i < p.m_manifest.count(); ++i) {
QString href = p.m_manifest.at(i).m_href;
QString bookpath = Utility::buildBookPath(href, opf_start_dir);
if (renamedDict.contains(bookpath)) {
Resource * resource = renamedDict[bookpath];
ManifestEntry me = p.m_manifest.at(i);
QString old_me_href = me.m_href;
me.m_href = Utility::URLEncodePath(GetRelativePathToResource(resource));
p.m_idpos[me.m_id] = i;
p.m_hrefpos.remove(old_me_href);
p.m_hrefpos[me.m_href] = i;
p.m_manifest.replace(i, me);
}
}
UpdateText(p);
}


int OPFResource::GetCoverMeta(const OPFParser& p) const
{
for (int i = 0; i < p.m_metadata.count(); ++i) {
Expand Down
3 changes: 2 additions & 1 deletion src/ResourceObjects/OPFResource.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/************************************************************************
**
** Copyright (C) 2015-2023 Kevin B. Hendricks, Stratford ON
** Copyright (C) 2015-2024 Kevin B. Hendricks, Stratford ON
** Copyright (C) 2013 John Schember <[email protected]>
** Copyright (C) 2009-2011 Strahinja Markovic <[email protected]>
**
Expand Down Expand Up @@ -174,6 +174,7 @@ public slots:
void UpdateSpineOrder(const QList<HTMLResource *> html_files);

void ResourceRenamed(const Resource *resource, QString old_full_path);
void BulkResourcesRenamed(const QHash<QString, Resource *> renamedDict);

void ResourceMoved(const Resource *resource, QString old_full_path);
void BulkResourcesMoved(const QHash<QString, Resource *> movedDict);
Expand Down
6 changes: 4 additions & 2 deletions src/ResourceObjects/Resource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ QIcon Resource::Icon() const
}


bool Resource::RenameTo(const QString &new_filename)
bool Resource::RenameTo(const QString &new_filename, bool in_bulk)
{
QString new_path;
bool successful = false;
Expand All @@ -202,7 +202,9 @@ bool Resource::RenameTo(const QString &new_filename)
QString old_path = m_FullFilePath;
m_FullFilePath = new_path;
SetShortPathName(new_filename);
emit Renamed(this, old_path);
if (!in_bulk) {
emit Renamed(this, old_path);
}
}

return successful;
Expand Down
2 changes: 1 addition & 1 deletion src/ResourceObjects/Resource.h
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ class Resource : public QObject
* @param new_filename The new name.
* @return \c true if the operation was successful.
*/
virtual bool RenameTo(const QString &new_filename);
virtual bool RenameTo(const QString &new_filename, bool in_bulk=false);


/**
Expand Down

0 comments on commit e9f3ffa

Please sign in to comment.