Skip to content

Commit 78d5ba4

Browse files
dragonpawclaude
andcommitted
Add "Delete current comic" with library IPC support (Ctrl+Del)
When opened from YACReaderLibrary, sends an IPC delete request so the library removes the comic from the DB and refreshes its view. In standalone mode, deletes the file directly. Includes a confirmation dialog with a "Don't ask again this session" checkbox, and navigates to the next (or previous) comic after deletion. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 03f521a commit 78d5ba4

16 files changed

Lines changed: 337 additions & 1 deletion

YACReader/main_window_viewer.cpp

Lines changed: 132 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@
3535
#include <QImage>
3636
#include <QDate>
3737
#include <QMenuBar>
38+
#include <QCheckBox>
39+
#include <QFile>
40+
41+
#include "QsLog.h"
3842

3943
#ifdef use_unarr
4044
#include "unarr.h"
@@ -94,6 +98,7 @@ MainWindowViewer::~MainWindowViewer()
9498
delete adjustToFullSizeAction;
9599
delete fitToPageAction;
96100
delete showFlowAction;
101+
delete deleteCurrentComicAction;
97102
}
98103
void MainWindowViewer::loadConfiguration()
99104
{
@@ -841,6 +846,7 @@ void MainWindowViewer::open(QString path, qint64 comicId, qint64 libraryId, YACR
841846

842847
if (success) {
843848
isClient = true;
849+
currentComicFilePath = path + currentComicDB.path;
844850
open(path + currentComicDB.path, currentComicDB, siblingComics);
845851
} else {
846852
isClient = false;
@@ -869,6 +875,7 @@ void MainWindowViewer::openComic(QString pathFile)
869875
{
870876
QFileInfo fi(pathFile);
871877
currentDirectory = fi.dir().absolutePath();
878+
currentComicFilePath = fi.absoluteFilePath();
872879
getSiblingComics(fi.absolutePath(), fi.fileName());
873880

874881
setWindowTitle("YACReader - " + fi.fileName());
@@ -1158,13 +1165,17 @@ void MainWindowViewer::setUpShortcutsManagement()
11581165
// Get current theme for initial icons
11591166
const auto &theme = ThemeManager::instance().getCurrentTheme();
11601167

1168+
deleteCurrentComicAction = addActionWithShortcut(tr("Delete current comic"), DELETE_CURRENT_COMIC_ACTION_Y);
1169+
connect(deleteCurrentComicAction, &QAction::triggered, this, &MainWindowViewer::deleteCurrentComic);
1170+
11611171
editShortcutsDialog->addActionsGroup(tr("Comics"), theme.shortcutsIcons.comicsIcon,
11621172
tmpList = { openAction,
11631173
openLatestComicAction,
11641174
openFolderAction,
11651175
saveImageAction,
11661176
openComicOnTheLeftAction,
1167-
openComicOnTheRightAction });
1177+
openComicOnTheRightAction,
1178+
deleteCurrentComicAction });
11681179

11691180
allActions << tmpList;
11701181

@@ -1325,6 +1336,126 @@ void MainWindowViewer::doubleMangaPageSwitch()
13251336
}
13261337
}
13271338

1339+
void MainWindowViewer::deleteCurrentComic()
1340+
{
1341+
if (currentComicFilePath.isEmpty()) {
1342+
return;
1343+
}
1344+
1345+
// Confirmation dialog
1346+
if (!skipDeleteConfirmation) {
1347+
QMessageBox msgBox(this);
1348+
msgBox.setWindowTitle(tr("Delete Comic"));
1349+
msgBox.setText(tr("Are you sure you want to delete the current comic?"));
1350+
msgBox.setInformativeText(QFileInfo(currentComicFilePath).fileName());
1351+
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel);
1352+
msgBox.setDefaultButton(QMessageBox::Cancel);
1353+
msgBox.setIcon(QMessageBox::Warning);
1354+
1355+
auto *checkBox = new QCheckBox(tr("Don't ask again this session"), &msgBox);
1356+
msgBox.setCheckBox(checkBox);
1357+
1358+
if (msgBox.exec() != QMessageBox::Yes) {
1359+
return;
1360+
}
1361+
1362+
if (checkBox->isChecked()) {
1363+
skipDeleteConfirmation = true;
1364+
}
1365+
}
1366+
1367+
// Determine navigation before deleting
1368+
bool hasNext = false;
1369+
bool hasPrevious = false;
1370+
1371+
if (isClient && !siblingComics.isEmpty()) {
1372+
int currentIndex = siblingComics.indexOf(currentComicDB);
1373+
hasNext = (currentIndex + 1 < siblingComics.count());
1374+
hasPrevious = (currentIndex - 1 >= 0);
1375+
} else {
1376+
hasNext = !nextComicPath.isEmpty();
1377+
hasPrevious = !previousComicPath.isEmpty();
1378+
}
1379+
1380+
if (isClient) {
1381+
int currentIndex = siblingComics.indexOf(currentComicDB);
1382+
1383+
// Close the comic to release the file handle
1384+
viewer->closeCurrentComic();
1385+
1386+
// Send delete request via IPC
1387+
YACReaderLocalClient client;
1388+
bool success = client.sendDeleteComic(libraryId, currentComicDB);
1389+
1390+
if (!success) {
1391+
QMessageBox::warning(this, tr("Delete Comic"), tr("Unable to delete the comic file."));
1392+
currentComicFilePath.clear();
1393+
return;
1394+
}
1395+
1396+
// Remove the comic from siblingComics
1397+
if (currentIndex >= 0 && currentIndex < siblingComics.count()) {
1398+
siblingComics.removeAt(currentIndex);
1399+
}
1400+
1401+
currentComicFilePath.clear();
1402+
1403+
// Navigate to next/previous comic
1404+
if (hasNext && currentIndex < siblingComics.count()) {
1405+
currentComicDB = siblingComics.at(currentIndex);
1406+
currentComicFilePath = currentDirectory + currentComicDB.path;
1407+
open(currentDirectory + currentComicDB.path, currentComicDB, siblingComics);
1408+
} else if (hasPrevious && currentIndex - 1 >= 0) {
1409+
currentComicDB = siblingComics.at(currentIndex - 1);
1410+
currentComicFilePath = currentDirectory + currentComicDB.path;
1411+
open(currentDirectory + currentComicDB.path, currentComicDB, siblingComics);
1412+
}
1413+
} else {
1414+
// Standalone mode
1415+
QString pathToDelete = currentComicFilePath;
1416+
QString savedNextComicPath = nextComicPath;
1417+
QString savedPreviousComicPath = previousComicPath;
1418+
1419+
viewer->closeCurrentComic();
1420+
currentComicFilePath.clear();
1421+
1422+
QFileInfo fileCheck(pathToDelete);
1423+
bool fileDeleted = false;
1424+
QString errorDetail;
1425+
1426+
if (!fileCheck.exists()) {
1427+
errorDetail = tr("File not found: %1").arg(pathToDelete);
1428+
} else if (!fileCheck.isWritable()) {
1429+
errorDetail = tr("File is not writable: %1").arg(pathToDelete);
1430+
} else {
1431+
fileDeleted = QFile::moveToTrash(pathToDelete);
1432+
if (fileDeleted) {
1433+
QLOG_INFO() << "Comic recycled:" << pathToDelete;
1434+
} else {
1435+
QFile f(pathToDelete);
1436+
fileDeleted = f.remove();
1437+
if (fileDeleted) {
1438+
QLOG_INFO() << "Comic permanently deleted:" << pathToDelete;
1439+
} else {
1440+
errorDetail = tr("Could not delete: %1\n%2").arg(pathToDelete, f.errorString());
1441+
QLOG_ERROR() << "Failed to delete comic:" << pathToDelete << f.errorString();
1442+
}
1443+
}
1444+
}
1445+
1446+
if (!fileDeleted) {
1447+
QMessageBox::warning(this, tr("Delete Comic"), errorDetail);
1448+
return;
1449+
}
1450+
1451+
if (hasNext && !savedNextComicPath.isEmpty()) {
1452+
openSiblingComic(savedNextComicPath);
1453+
} else if (hasPrevious && !savedPreviousComicPath.isEmpty()) {
1454+
openSiblingComic(savedPreviousComicPath);
1455+
}
1456+
}
1457+
}
1458+
13281459
void MainWindowViewer::toggleFitToWidthSlider()
13291460
{
13301461
int y;

YACReader/main_window_viewer.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ public slots:
7777
void processReset();
7878
void setUpShortcutsManagement();
7979
void doubleMangaPageSwitch();
80+
void deleteCurrentComic();
8081

8182
void toggleFitToWidthSlider();
8283

@@ -152,6 +153,7 @@ public slots:
152153
QAction *showFlowAction;
153154

154155
QAction *showEditShortcutsAction;
156+
QAction *deleteCurrentComicAction;
155157

156158
QList<QAction *> mglassActions;
157159
QList<QAction *> loadedComicActions;
@@ -186,6 +188,8 @@ public slots:
186188
ComicDB currentComicDB;
187189
QList<ComicDB> siblingComics;
188190
bool isClient;
191+
QString currentComicFilePath;
192+
bool skipDeleteConfirmation = false;
189193
QString startComicPath;
190194
quint64 libraryId;
191195
OpenComicSource source;

YACReader/render.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -702,6 +702,22 @@ void Render::load(const QString &path, const ComicDB &comicDB)
702702
}
703703
}
704704

705+
void Render::releaseComic()
706+
{
707+
if (comic != nullptr) {
708+
comic->invalidate();
709+
comic->disconnect();
710+
auto *comicThread = comic->thread();
711+
if (comicThread != QThread::currentThread() && comicThread->isRunning()) {
712+
comicThread->quit();
713+
comicThread->wait();
714+
}
715+
comic->moveToThread(QThread::currentThread());
716+
delete comic;
717+
comic = nullptr;
718+
}
719+
}
720+
705721
void Render::createComic(const QString &path)
706722
{
707723
previousIndex = currentIndex = 0;

YACReader/render.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ public slots:
176176
void setBookmark();
177177
void removeBookmark();
178178
void save();
179+
void releaseComic();
179180
void reset();
180181
void reload();
181182
void updateFilters(int brightness, int contrast, int gamma);

YACReader/viewer.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1199,6 +1199,12 @@ void Viewer::resetContent()
11991199
emit reset();
12001200
}
12011201

1202+
void Viewer::closeCurrentComic()
1203+
{
1204+
render->releaseComic();
1205+
resetContent();
1206+
}
1207+
12021208
void Viewer::setLoadingMessage()
12031209
{
12041210
if (magnifyingGlassShown) {

YACReader/viewer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ public slots:
8888
void setMangaWithoutStoringSetting(bool manga);
8989
void doubleMangaPageSwitch();
9090
void resetContent();
91+
void closeCurrentComic();
9192
void setLoadingMessage();
9293
void setPageUnavailableMessage();
9394
void configureContent(QString msg);

YACReader/yacreader_local_client.cpp

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,3 +197,87 @@ bool YACReaderLocalClient::sendComicInfo(quint64 libraryId, ComicDB &comic, qulo
197197
QLOG_ERROR() << "Sending Comic Info : unable to connect to the server";
198198
return false;
199199
}
200+
201+
bool YACReaderLocalClient::sendDeleteComic(quint64 libraryId, ComicDB &comic)
202+
{
203+
localSocket->connectToServer(YACREADERLIBRARY_GUID);
204+
if (localSocket->isOpen()) {
205+
QByteArray block;
206+
QDataStream out(&block, QIODevice::WriteOnly);
207+
out.setVersion(QDataStream::Qt_4_8);
208+
out << (quint32)0;
209+
out << (quint8)YACReader::DeleteComic;
210+
out << libraryId;
211+
out << comic;
212+
out.device()->seek(0);
213+
out << (quint32)(block.size() - sizeof(quint32));
214+
215+
int written = 0;
216+
int previousWritten = 0;
217+
quint16 tries = 0;
218+
while (written != block.size() && tries < 200) {
219+
written += localSocket->write(block);
220+
localSocket->flush();
221+
if (written == previousWritten)
222+
tries++;
223+
previousWritten = written;
224+
}
225+
if (tries == 200) {
226+
localSocket->close();
227+
QLOG_ERROR() << "Delete Comic : unable to send request";
228+
return false;
229+
}
230+
231+
localSocket->waitForBytesWritten(2000);
232+
233+
// Read response (bool success)
234+
tries = 0;
235+
int dataAvailable = 0;
236+
QByteArray packageSize;
237+
localSocket->waitForReadyRead(1000);
238+
while (packageSize.size() < (int)sizeof(quint32) && tries < 20) {
239+
packageSize.append(localSocket->read(sizeof(quint32) - packageSize.size()));
240+
localSocket->waitForReadyRead(100);
241+
if (dataAvailable == packageSize.size())
242+
tries++;
243+
dataAvailable = packageSize.size();
244+
}
245+
if (tries == 20) {
246+
localSocket->close();
247+
QLOG_ERROR() << "Delete Comic : unable to read package size";
248+
return false;
249+
}
250+
QDataStream sizeStream(packageSize);
251+
sizeStream.setVersion(QDataStream::Qt_4_8);
252+
quint32 totalSize = 0;
253+
sizeStream >> totalSize;
254+
255+
QByteArray data;
256+
tries = 0;
257+
int dataRead = 0;
258+
localSocket->waitForReadyRead(1000);
259+
while ((unsigned int)data.length() < totalSize && tries < 20) {
260+
data.append(localSocket->readAll());
261+
if ((unsigned int)data.length() < totalSize)
262+
localSocket->waitForReadyRead(100);
263+
if (data.length() == dataRead)
264+
tries++;
265+
dataRead = data.length();
266+
}
267+
268+
if (tries == 20) {
269+
localSocket->close();
270+
QLOG_ERROR() << "Delete Comic : unable to read data (" << data.length() << "," << totalSize << ")";
271+
return false;
272+
}
273+
274+
QDataStream dataStream(data);
275+
bool success = false;
276+
dataStream >> success;
277+
localSocket->close();
278+
return success;
279+
} else {
280+
QLOG_ERROR() << "Delete Comic : unable to connect to the server";
281+
return false;
282+
}
283+
}

YACReader/yacreader_local_client.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ public slots:
2121
bool requestComicInfo(quint64 libraryId, ComicDB &comic, QList<ComicDB> &siblings, YACReader::OpenComicSource source);
2222
bool sendComicInfo(quint64 libraryId, ComicDB &comic);
2323
bool sendComicInfo(quint64 libraryId, ComicDB &comic, qulonglong nextComicId);
24+
bool sendDeleteComic(quint64 libraryId, ComicDB &comic);
2425

2526
private:
2627
QLocalSocket *localSocket;

YACReaderLibrary/library_window.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2693,6 +2693,14 @@ void LibraryWindow::updateViewsOnComicUpdate(quint64 libraryId, const ComicDB &c
26932693
}
26942694
}
26952695

2696+
void LibraryWindow::reloadAfterComicDeleted(quint64 libraryId, const ComicDB &comic)
2697+
{
2698+
Q_UNUSED(comic);
2699+
if (libraryId == (quint64)libraries.getId(selectedLibrary->currentText())) {
2700+
navigationController->reselectCurrentFolder();
2701+
}
2702+
}
2703+
26962704
bool LibraryWindow::exitSearchMode()
26972705
{
26982706
if (status != LibraryWindow::Searching)

YACReaderLibrary/library_window.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@ public slots:
301301
void updateViewsOnClientSync();
302302
void updateViewsOnComicUpdateWithId(quint64 libraryId, quint64 comicId);
303303
void updateViewsOnComicUpdate(quint64 libraryId, const ComicDB &comic);
304+
void reloadAfterComicDeleted(quint64 libraryId, const ComicDB &comic);
304305
void showComicVineScraper();
305306
void setRemoveError();
306307
void checkRemoveError();

0 commit comments

Comments
 (0)