diff --git a/src/nokde-stubs/prefs.cpp b/src/nokde-stubs/prefs.cpp --- a/src/nokde-stubs/prefs.cpp +++ b/src/nokde-stubs/prefs.cpp @@ -58,6 +58,7 @@ , mPrefetchTM(false) , mAutoaddTM(true) , mScanToTMOnOpen(false) + , mDeleteFromTMOnMissing(false) , mWordCompletionLength(3) , mSuggCount(10) diff --git a/src/nokde-stubs/prefs_lokalize.h b/src/nokde-stubs/prefs_lokalize.h --- a/src/nokde-stubs/prefs_lokalize.h +++ b/src/nokde-stubs/prefs_lokalize.h @@ -160,6 +160,12 @@ } protected: + static + bool deleteFromTMOnMissing() + { + return self()->mDeleteFromTMOnMissing; + } + Settings(); friend class SettingsHelper; @@ -188,6 +194,7 @@ bool mPrefetchTM; bool mAutoaddTM; bool mScanToTMOnOpen; + bool mDeleteFromTMOnMissing; int mWordCompletionLength; int mSuggCount; diff --git a/src/prefs/lokalize.kcfg b/src/prefs/lokalize.kcfg --- a/src/prefs/lokalize.kcfg +++ b/src/prefs/lokalize.kcfg @@ -114,5 +114,8 @@ false + + false + diff --git a/src/prefs/prefs_tm.ui b/src/prefs/prefs_tm.ui --- a/src/prefs/prefs_tm.ui +++ b/src/prefs/prefs_tm.ui @@ -84,6 +84,13 @@ + + + + Delete missing files from translation memory on Rescan or when clicking a missing entry + + + diff --git a/src/project/project.h b/src/project/project.h --- a/src/project/project.h +++ b/src/project/project.h @@ -111,6 +111,7 @@ } bool queryCloseForAuxiliaryWindows(); + bool isFileMissing(const QString& filePath) const; void setDefaults(); // private slots: diff --git a/src/project/project.cpp b/src/project/project.cpp --- a/src/project/project.cpp +++ b/src/project/project.cpp @@ -307,6 +307,19 @@ m_tmManagerWindow->activateWindow(); } +bool Project::isFileMissing(const QString& filePath) const +{ + if (!QFile::exists(filePath) && isLoaded()) { + //check if we are opening template + QString newPath = filePath; + newPath.replace(poDir(), potDir()); + if (!QFile::exists(newPath) && !QFile::exists(newPath += 't')) { + return true; + } + } + return false; +} + void Project::save() { m_localConfig->setFirstRun(false); diff --git a/src/tm/jobs.h b/src/tm/jobs.h --- a/src/tm/jobs.h +++ b/src/tm/jobs.h @@ -55,15 +55,16 @@ #define TMTABSELECT 100 #define UPDATE 80 #define REMOVE 70 +#define REMOVEFILE 69 #define INSERT 60 #define SELECT 50 #define BATCHSELECTFINISHED 49 #define IMPORT 30 #define EXPORT 25 +#define REMOVEMISSINGFILES 11 #define SCAN 10 #define SCANFINISHED 9 - struct TMConfig { QString markup; QString accel; @@ -89,7 +90,7 @@ explicit OpenDBJob(const QString& dbName, DbType type = TM::Local, bool reconnect = false, const ConnectionParams& connParams = ConnectionParams()); ~OpenDBJob(); - int priority()const + int priority() const { return OPENDB; } @@ -128,7 +129,7 @@ explicit CloseDBJob(const QString& dbName); ~CloseDBJob(); - int priority()const + int priority() const { return CLOSEDB; } @@ -161,7 +162,7 @@ const QString& dbName); ~SelectJob(); - int priority()const + int priority() const { return SELECT; } @@ -197,14 +198,56 @@ SelectJob* initSelectJob(Catalog*, DocPosition pos, QString db = QString(), int opt = Enqueue); +class RemoveMissingFilesJob: public QObject, public QRunnable +{ + Q_OBJECT +public: + explicit RemoveMissingFilesJob(const QString& dbName); + ~RemoveMissingFilesJob(); + int priority() const + { + return REMOVEMISSINGFILES; + } + +protected: + void run(); + + QString m_dbName; + +signals: + void done(); +}; + +class RemoveFileJob: public QObject, public QRunnable +{ + Q_OBJECT +public: + explicit RemoveFileJob(const QString& filePath, const QString& dbName, QObject *parent = nullptr); + ~RemoveFileJob(); + int priority() const + { + return REMOVEFILE; + } + +protected: + void run(); + + QString m_filePath; + QString m_dbName; + QObject m_parent; + +signals: + void done(); +}; + class RemoveJob: public QObject, public QRunnable { Q_OBJECT public: explicit RemoveJob(const TMEntry& entry); ~RemoveJob(); - int priority()const + int priority() const { return REMOVE; } @@ -241,7 +284,7 @@ ~UpdateJob() {} - int priority()const + int priority() const { return UPDATE; } @@ -266,7 +309,7 @@ explicit ScanJob(const QString& filePath, const QString& dbName); ~ScanJob(); - int priority()const + int priority() const { return SCAN; } @@ -319,7 +362,7 @@ {} ~BatchSelectFinishedJob() {}; - int priority()const + int priority() const { return BATCHSELECTFINISHED; } @@ -348,7 +391,7 @@ IndexWordsJob(QObject* parent = 0); ~IndexWordsJob(); - int priority()const + int priority() const { return 100; } @@ -374,7 +417,7 @@ const QString& dbName); ~ImportTmxJob(); - int priority()const + int priority() const { return IMPORT; } @@ -399,7 +442,7 @@ const QString& dbName); ~ExportTmxJob(); - int priority()const + int priority() const { return IMPORT; } @@ -424,7 +467,7 @@ explicit ExecQueryJob(const QString& queryString, const QString& dbName); ~ExecQueryJob(); - int priority()const + int priority() const { return TMTABSELECT; } diff --git a/src/tm/jobs.cpp b/src/tm/jobs.cpp --- a/src/tm/jobs.cpp +++ b/src/tm/jobs.cpp @@ -111,8 +111,6 @@ } - - static qlonglong getFileId(const QString& path, QSqlDatabase& db) { @@ -355,6 +353,51 @@ return query1.exec(QStringLiteral("DELETE FROM main WHERE id=") + QString::number(mainId)); } + +static bool doRemoveFile(const QString& filePath, QSqlDatabase& db) +{ + qlonglong fileId = getFileId(filePath, db); + QSqlQuery query1(db); + + if (Q_UNLIKELY(!query1.exec(U("SELECT id FROM files WHERE " + "id=") + QString::number(fileId)))) + return false; + + if (!query1.next()) + return false; + + query1.clear(); + + query1.exec(QStringLiteral("DELETE source_strings FROM source_strings, main WHERE source_strings.id = main.source AND main.file =") + QString::number(fileId)); + query1.exec(QStringLiteral("DELETE target_strings FROM target_strings, main WHERE target_strings.id = main.target AND main.file =") + QString::number(fileId)); + query1.exec(QStringLiteral("DELETE FROM main WHERE file = ") + QString::number(fileId)); + return query1.exec(QStringLiteral("DELETE FROM files WHERE id=") + QString::number(fileId)); +} + +static int doRemoveMissingFiles(QSqlDatabase& db, const QString& dbName, QObject *job) +{ + int deletedFiles = 0; + QSqlQuery query1(db); + + if (Q_UNLIKELY(!query1.exec(U("SELECT files.path FROM files")))) + return false; + + if (!query1.next()) + return false; + + do { + QString filePath = query1.value(0).toString(); + if (Project::instance()->isFileMissing(filePath)) { + qCWarning(LOKALIZE_LOG) << "Removing file " << filePath << " from translation memory"; + RemoveFileJob* job_removefile = new RemoveFileJob(filePath, dbName, job); + TM::threadPool()->start(job_removefile, REMOVEFILE); + deletedFiles++; + } + } while (query1.next()); + + return deletedFiles; +} + static QString escape(QString str) { return str.replace(QLatin1Char('\''), QStringLiteral("''")); @@ -1541,6 +1584,60 @@ m_time = a.elapsed(); } +RemoveMissingFilesJob::RemoveMissingFilesJob(const QString& dbName) + : QObject(), QRunnable() + , m_dbName(dbName) +{ + qCDebug(LOKALIZE_LOG) << "removingmissingfiles" << m_dbName; +} + + +RemoveMissingFilesJob::~RemoveMissingFilesJob() +{ + qCDebug(LOKALIZE_LOG) << "removingmissingfilesjob dtor" << m_dbName; +} + + + +void RemoveMissingFilesJob::run() +{ +// qCDebug(LOKALIZE_LOG)< #include #include +#include #include #include #include #include #include #include #include -#include - #ifndef NOKDE #include #include #include +#include +#include #endif #if defined(Q_OS_WIN) && defined(QStringLiteral) @@ -528,6 +530,10 @@ void TMTab::updateTM() { scanRecursive(QStringList(Project::instance()->poDir()), Project::instance()->projectID()); + if (Settings::deleteFromTMOnMissing()) { + RemoveMissingFilesJob* job = new RemoveMissingFilesJob(Project::instance()->projectID()); + TM::threadPool()->start(job, REMOVEMISSINGFILES); + } } void TMTab::performQuery() @@ -612,6 +618,17 @@ void TMTab::openFile() { QModelIndex item = ui_queryOptions->treeView->currentIndex(); + if (Settings::deleteFromTMOnMissing()) { + //Check if the file exists and delete it if it doesn't + QString filePath = item.sibling(item.row(), TMDBModel::Filepath).data(Qt::UserRole).toString(); + if (Project::instance()->isFileMissing(filePath)) { + //File doesn't exist + RemoveFileJob* job = new RemoveFileJob(filePath, ui_queryOptions->dbName->currentText()); + TM::threadPool()->start(job, REMOVEFILE); + KMessageBox::information(this, i18nc("@info", "The file %1 doesn't exist, it has been removed from the Translation Memory.", filePath)); + return performQuery();//We relaunch the query + } + } emit fileOpenRequested(item.sibling(item.row(), TMDBModel::Filepath).data(Qt::UserRole).toString(), item.sibling(item.row(), TMDBModel::Source).data().toString(), item.sibling(item.row(), TMDBModel::Context).data().toString()); diff --git a/src/tm/tmview.h b/src/tm/tmview.h --- a/src/tm/tmview.h +++ b/src/tm/tmview.h @@ -90,6 +90,7 @@ private: bool event(QEvent *event); + void deleteFile(const TMEntry& e, const bool showPopUp); private: diff --git a/src/tm/tmview.cpp b/src/tm/tmview.cpp --- a/src/tm/tmview.cpp +++ b/src/tm/tmview.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -571,26 +572,54 @@ } } +void TMView::deleteFile(const TMEntry& e, const bool showPopUp) +{ + QString filePath = e.file; + if (Project::instance()->isFileMissing(filePath)) { + //File doesn't exist + RemoveFileJob* job = new RemoveFileJob(e.file, e.dbName); + connect(job, SIGNAL(done()), this, SLOT(slotNewEntryDisplayed())); + TM::threadPool()->start(job, REMOVEFILE); + if (showPopUp) { + KMessageBox::information(this, i18nc("@info", "The file %1 doesn't exist, it has been removed from the Translation Memory.", e.file)); + } + return; + } +} + void TMView::contextMenu(const QPoint& pos) { int block = *m_entryPositions.lowerBound(m_browser->cursorForPosition(pos).anchor()); qCWarning(LOKALIZE_LOG) << block; if (block >= m_entries.size()) return; const TMEntry& e = m_entries.at(block); - enum {Remove, Open}; + enum {Remove, RemoveFile, Open}; QMenu popup; popup.addAction(i18nc("@action:inmenu", "Remove this entry"))->setData(Remove); if (e.file != m_catalog->url() && QFile::exists(e.file)) popup.addAction(i18nc("@action:inmenu", "Open file containing this entry"))->setData(Open); + else { + if (Settings::deleteFromTMOnMissing()) { + //Automatic deletion + deleteFile(e, true); + } else if (!QFile::exists(e.file)) { + //Still offer manual deletion if this is not the current file + popup.addAction(i18nc("@action:inmenu", "Remove this missing file from TM"))->setData(RemoveFile); + } + } QAction* r = popup.exec(m_browser->mapToGlobal(pos)); if (!r) return; if (r->data().toInt() == Remove) { removeEntry(e); } else if (r->data().toInt() == Open) { emit fileOpenRequested(e.file, e.source.string, e.ctxt); + } else if ((r->data().toInt() == RemoveFile) && + KMessageBox::Yes == KMessageBox::questionYesNo(this, i18n("Do you really want to remove this missing file:
%1
from translation memory %2?", e.file, e.dbName), + i18nc("@title:window", "Translation Memory Missing File Removal"))) { + deleteFile(e, false); } }