diff --git a/src/catalog/catalog.cpp b/src/catalog/catalog.cpp index 1ae8dcc..a4b8cbb 100644 --- a/src/catalog/catalog.cpp +++ b/src/catalog/catalog.cpp @@ -1,1063 +1,1062 @@ /* **************************************************************************** This file is part of Lokalize This file contains parts of KBabel code Copyright (C) 1999-2000 by Matthias Kiefer 2001-2005 by Stanislav Visnovsky 2006 by Nicolas Goutte 2007-2014 by Nick Shaforostoff 2018-2019 by Simon Depiets This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. **************************************************************************** */ #include "catalog.h" #include "catalog_private.h" #include "project.h" #include "projectmodel.h" //to notify about modification #include "catalogstorage.h" #include "gettextstorage.h" #include "gettextimport.h" #include "gettextexport.h" #include "xliffstorage.h" #include "tsstorage.h" #include "mergecatalog.h" #include "version.h" #include "prefs_lokalize.h" #include "jobs.h" #include "dbfilesmodel.h" #include #include #include #include #include #include #include #include #ifdef Q_OS_WIN #define U QLatin1String #else #define U QStringLiteral #endif //QString Catalog::supportedMimeFilters("text/x-gettext-translation application/x-xliff application/x-linguist"); //" text/x-gettext-translation-template") QString Catalog::supportedFileTypes(bool includeTemplates) { QString sep = QStringLiteral(";;"); QString all = i18n("All supported files (*.po *.pot *.xlf *.xliff *.ts)") + sep; return all + (includeTemplates ? i18n("Gettext (*.po *.pot)") : i18n("Gettext (*.po)")) + sep + i18n("XLIFF (*.xlf *.xliff)") + sep + i18n("Linguist (*.ts)"); } static const QString extensions[] = {U(".po"), U(".pot"), U(".xlf"), U(".xliff"), U(".ts")}; static const char* const xliff_states[] = { I18N_NOOP("New"), I18N_NOOP("Needs translation"), I18N_NOOP("Needs full localization"), I18N_NOOP("Needs adaptation"), I18N_NOOP("Translated"), I18N_NOOP("Needs translation review"), I18N_NOOP("Needs full localization review"), I18N_NOOP("Needs adaptation review"), I18N_NOOP("Final"), I18N_NOOP("Signed-off") }; const char* const* Catalog::states() { return xliff_states; } QStringList Catalog::supportedExtensions() { QStringList result; int i = sizeof(extensions) / sizeof(QString); while (--i >= 0) result.append(extensions[i]); return result; } bool Catalog::extIsSupported(const QString& path) { QStringList ext = supportedExtensions(); int i = ext.size(); while (--i >= 0 && !path.endsWith(ext.at(i))) ; return i != -1; } Catalog::Catalog(QObject *parent) : QUndoStack(parent) , d(this) , m_storage(0) { //cause refresh events for files modified from lokalize itself aint delivered automatically connect(this, QOverload::of(&Catalog::signalFileSaved), Project::instance()->model(), QOverload::of(&ProjectModel::slotFileSaved), Qt::QueuedConnection); QTimer* t = &(d._autoSaveTimer); t->setInterval(2 * 60 * 1000); t->setSingleShot(false); connect(t, &QTimer::timeout, this, &Catalog::doAutoSave); connect(this, QOverload<>::of(&Catalog::signalFileSaved), t, QOverload<>::of(&QTimer::start)); connect(this, QOverload<>::of(&Catalog::signalFileLoaded), t, QOverload<>::of(&QTimer::start)); connect(this, &Catalog::indexChanged, this, &Catalog::setAutoSaveDirty); connect(Project::local(), &Project::configChanged, this, &Catalog::projectConfigChanged); } Catalog::~Catalog() { clear(); //delete m_storage; //deleted in clear(); } void Catalog::clear() { setIndex(cleanIndex());//to keep TM in sync QUndoStack::clear(); d._errorIndex.clear(); d._nonApprovedIndex.clear(); d._nonApprovedNonEmptyIndex.clear(); d._emptyIndex.clear(); delete m_storage; m_storage = 0; d._filePath.clear(); d._lastModifiedPos = DocPosition(); d._modifiedEntries.clear(); while (!d._altTransCatalogs.isEmpty()) d._altTransCatalogs.takeFirst()->deleteLater(); d._altTranslations.clear(); /* d.msgidDiffList.clear(); d.msgstr2MsgidDiffList.clear(); d.diffCache.clear(); */ } void Catalog::push(QUndoCommand* cmd) { generatePhaseForCatalogIfNeeded(this); QUndoStack::push(cmd); } //BEGIN STORAGE TRANSLATION int Catalog::capabilities() const { if (Q_UNLIKELY(!m_storage)) return 0; return m_storage->capabilities(); } int Catalog::numberOfEntries() const { if (Q_UNLIKELY(!m_storage)) return 0; return m_storage->size(); } static DocPosition alterForSinglePlural(const Catalog* th, DocPosition pos) { //if source lang is english (implied) and target lang has only 1 plural form (e.g. Chinese) if (Q_UNLIKELY(th->numberOfPluralForms() == 1 && th->isPlural(pos))) pos.form = 1; return pos; } QString Catalog::msgid(const DocPosition& pos) const { if (Q_UNLIKELY(!m_storage)) return QString(); return m_storage->source(alterForSinglePlural(this, pos)); } QString Catalog::msgidWithPlurals(const DocPosition& pos, bool truncateFirstLine) const { if (Q_UNLIKELY(!m_storage)) return QString(); return m_storage->sourceWithPlurals(pos, truncateFirstLine); } QString Catalog::msgstr(const DocPosition& pos) const { if (Q_UNLIKELY(!m_storage)) return QString(); return m_storage->target(pos); } QString Catalog::msgstrWithPlurals(const DocPosition& pos, bool truncateFirstLine) const { if (Q_UNLIKELY(!m_storage)) return QString(); return m_storage->targetWithPlurals(pos, truncateFirstLine); } CatalogString Catalog::sourceWithTags(const DocPosition& pos) const { if (Q_UNLIKELY(!m_storage)) return CatalogString(); return m_storage->sourceWithTags(alterForSinglePlural(this, pos)); } CatalogString Catalog::targetWithTags(const DocPosition& pos) const { if (Q_UNLIKELY(!m_storage)) return CatalogString(); return m_storage->targetWithTags(pos); } CatalogString Catalog::catalogString(const DocPosition& pos) const { if (Q_UNLIKELY(!m_storage)) return CatalogString(); return m_storage->catalogString(pos.part == DocPosition::Source ? alterForSinglePlural(this, pos) : pos); } QVector Catalog::notes(const DocPosition& pos) const { if (Q_UNLIKELY(!m_storage)) return QVector(); return m_storage->notes(pos); } QVector Catalog::developerNotes(const DocPosition& pos) const { if (Q_UNLIKELY(!m_storage)) return QVector(); return m_storage->developerNotes(pos); } Note Catalog::setNote(const DocPosition& pos, const Note& note) { if (Q_UNLIKELY(!m_storage)) return Note(); return m_storage->setNote(pos, note); } QStringList Catalog::noteAuthors() const { if (Q_UNLIKELY(!m_storage)) return QStringList(); return m_storage->noteAuthors(); } void Catalog::attachAltTransCatalog(Catalog* altCat) { d._altTransCatalogs.append(altCat); if (numberOfEntries() != altCat->numberOfEntries()) qCWarning(LOKALIZE_LOG) << altCat->url() << "has different number of entries"; } void Catalog::attachAltTrans(int entry, const AltTrans& trans) { d._altTranslations.insert(entry, trans); } QVector Catalog::altTrans(const DocPosition& pos) const { QVector result; if (m_storage) result = m_storage->altTrans(pos); foreach (Catalog* altCat, d._altTransCatalogs) { if (pos.entry >= altCat->numberOfEntries()) { qCDebug(LOKALIZE_LOG) << "ignoring" << altCat->url() << "this time because" << pos.entry << "<" << altCat->numberOfEntries(); continue; } if (altCat->source(pos) != source(pos)) { qCDebug(LOKALIZE_LOG) << "ignoring" << altCat->url() << "this time because s don't match"; continue; } QString target = altCat->msgstr(pos); if (!target.isEmpty() && altCat->isApproved(pos)) { result << AltTrans(); result.last().target = target; result.last().type = AltTrans::Reference; result.last().origin = altCat->url(); } } if (d._altTranslations.contains(pos.entry)) result << d._altTranslations.value(pos.entry); return result; } QStringList Catalog::sourceFiles(const DocPosition& pos) const { if (Q_UNLIKELY(!m_storage)) return QStringList(); return m_storage->sourceFiles(pos); } QString Catalog::id(const DocPosition& pos) const { if (Q_UNLIKELY(!m_storage)) return QString(); return m_storage->id(pos); } QStringList Catalog::context(const DocPosition& pos) const { if (Q_UNLIKELY(!m_storage)) return QStringList(); return m_storage->context(pos); } QString Catalog::setPhase(const DocPosition& pos, const QString& phase) { if (Q_UNLIKELY(!m_storage)) return QString(); return m_storage->setPhase(pos, phase); } void Catalog::setActivePhase(const QString& phase, ProjectLocal::PersonRole role) { - //qCDebug(LOKALIZE_LOG)<<"setting active phase"<size(); while (pos.entry < limit) { if (m_storage->isEmpty(pos)) d._emptyIndex << pos.entry; if (!isApproved(pos)) { d._nonApprovedIndex << pos.entry; if (!m_storage->isEmpty(pos)) { d._nonApprovedNonEmptyIndex << pos.entry; } } ++(pos.entry); } emit signalNumberOfFuzziesChanged(); emit signalNumberOfEmptyChanged(); } QString Catalog::phase(const DocPosition& pos) const { if (Q_UNLIKELY(!m_storage)) return QString(); return m_storage->phase(pos); } Phase Catalog::phase(const QString& name) const { return m_storage->phase(name); } QList Catalog::allPhases() const { return m_storage->allPhases(); } QVector Catalog::phaseNotes(const QString& phase) const { return m_storage->phaseNotes(phase); } QVector Catalog::setPhaseNotes(const QString& phase, QVector notes) { return m_storage->setPhaseNotes(phase, notes); } QMap Catalog::allTools() const { return m_storage->allTools(); } bool Catalog::isPlural(uint index) const { return m_storage && m_storage->isPlural(DocPosition(index)); } bool Catalog::isApproved(uint index) const { if (Q_UNLIKELY(!m_storage)) return false; bool extendedStates = m_storage->capabilities()&ExtendedStates; return (extendedStates &&::isApproved(state(DocPosition(index)), activePhaseRole())) || (!extendedStates && m_storage->isApproved(DocPosition(index))); } TargetState Catalog::state(const DocPosition& pos) const { if (Q_UNLIKELY(!m_storage)) return NeedsTranslation; if (m_storage->capabilities()&ExtendedStates) return m_storage->state(pos); else return closestState(m_storage->isApproved(pos), activePhaseRole()); } bool Catalog::isEmpty(uint index) const { return m_storage && m_storage->isEmpty(DocPosition(index)); } bool Catalog::isEmpty(const DocPosition& pos) const { return m_storage && m_storage->isEmpty(pos); } bool Catalog::isEquivTrans(const DocPosition& pos) const { return m_storage && m_storage->isEquivTrans(pos); } int Catalog::binUnitsCount() const { return m_storage ? m_storage->binUnitsCount() : 0; } int Catalog::unitById(const QString& id) const { return m_storage ? m_storage->unitById(id) : 0; } QString Catalog::mimetype() { if (Q_UNLIKELY(!m_storage)) return QString(); return m_storage->mimetype(); } QString Catalog::fileType() { if (Q_UNLIKELY(!m_storage)) return QString(); return m_storage->fileType(); } CatalogType Catalog::type() { if (Q_UNLIKELY(!m_storage)) return Gettext; return m_storage->type(); } QString Catalog::sourceLangCode() const { if (Q_UNLIKELY(!m_storage)) return QString(); return m_storage->sourceLangCode(); } QString Catalog::targetLangCode() const { if (Q_UNLIKELY(!m_storage)) return QString(); return m_storage->targetLangCode(); } void Catalog::setTargetLangCode(const QString& targetLangCode) { if (Q_UNLIKELY(!m_storage)) return; bool notify = m_storage->targetLangCode() != targetLangCode; m_storage->setTargetLangCode(targetLangCode); if (notify) emit signalFileLoaded(); } //END STORAGE TRANSLATION //BEGIN OPEN/SAVE #define DOESNTEXIST -1 #define ISNTREADABLE -2 #define UNKNOWNFORMAT -3 KAutoSaveFile* Catalog::checkAutoSave(const QString& url) { KAutoSaveFile* autoSave = 0; QList staleFiles = KAutoSaveFile::staleFiles(QUrl::fromLocalFile(url)); foreach (KAutoSaveFile *stale, staleFiles) { if (stale->open(QIODevice::ReadOnly) && !autoSave) { autoSave = stale; autoSave->setParent(this); } else stale->deleteLater(); } if (autoSave) qCInfo(LOKALIZE_LOG) << "autoSave" << autoSave->fileName(); return autoSave; } int Catalog::loadFromUrl(const QString& filePath, const QString& saidUrl, int* fileSize, bool fast) { QFileInfo info(filePath); if (Q_UNLIKELY(!info.exists() || info.isDir())) return DOESNTEXIST; if (Q_UNLIKELY(!info.isReadable())) return ISNTREADABLE; bool readOnly = !info.isWritable(); QTime a; a.start(); QFile file(filePath); if (!file.open(QIODevice::ReadOnly)) return ISNTREADABLE;//TODO CatalogStorage* storage = nullptr; if (filePath.endsWith(QLatin1String(".po")) || filePath.endsWith(QLatin1String(".pot"))) storage = new GettextCatalog::GettextStorage; else if (filePath.endsWith(QLatin1String(".xlf")) || filePath.endsWith(QLatin1String(".xliff"))) storage = new XliffStorage; else if (filePath.endsWith(QLatin1String(".ts"))) storage = new TsStorage; else { //try harder QTextStream in(&file); int i = 0; bool gettext = false; while (!in.atEnd() && ++i < 64 && !gettext) gettext = in.readLine().contains(QLatin1String("msgid")); if (gettext) storage = new GettextCatalog::GettextStorage; else return UNKNOWNFORMAT; } int line = storage->load(&file); file.close(); if (Q_UNLIKELY(line != 0 || (!storage->size() && (line == -1)))) { delete storage; return line; } if (a.elapsed() > 100) qCDebug(LOKALIZE_LOG) << filePath << "opened in" << a.elapsed(); //ok... clear(); //commit transaction m_storage = storage; updateApprovedEmptyIndexCache(); d._numberOfPluralForms = storage->numberOfPluralForms(); d._autoSaveDirty = true; d._readOnly = readOnly; d._filePath = saidUrl.isEmpty() ? filePath : saidUrl; //set some sane role, a real phase with a nmae will be created later with the first edit command setActivePhase(QString(), Project::local()->role()); if (!fast) { KAutoSaveFile* autoSave = checkAutoSave(d._filePath); d._autoSaveRecovered = autoSave; if (autoSave) { d._autoSave->deleteLater(); d._autoSave = autoSave; //restore 'modified' status for entries MergeCatalog* mergeCatalog = new MergeCatalog(this, this); int errorLine = mergeCatalog->loadFromUrl(autoSave->fileName()); if (Q_LIKELY(errorLine == 0)) mergeCatalog->copyToBaseCatalog(); mergeCatalog->deleteLater(); d._autoSave->close(); } else d._autoSave->setManagedFile(QUrl::fromLocalFile(d._filePath)); } if (fileSize) *fileSize = file.size(); emit signalFileLoaded(); emit signalFileLoaded(d._filePath); return 0; } bool Catalog::save() { return saveToUrl(d._filePath); } //this function is not called if QUndoStack::isClean() ! bool Catalog::saveToUrl(QString localFilePath) { if (Q_UNLIKELY(!m_storage)) return true; bool nameChanged = localFilePath.length(); if (Q_LIKELY(!nameChanged)) localFilePath = d._filePath; QString localPath = QFileInfo(localFilePath).absolutePath(); if (!QFileInfo::exists(localPath)) if (!QDir::root().mkpath(localPath)) return false; QFile file(localFilePath); if (Q_UNLIKELY(!file.open(QIODevice::WriteOnly))) //i18n("Wasn't able to open file %1",filename.ascii()); return false; bool belongsToProject = localFilePath.contains(Project::instance()->poDir()); if (Q_UNLIKELY(!m_storage->save(&file, belongsToProject))) return false; file.close(); d._autoSave->remove(); d._autoSaveRecovered = false; setClean(); //undo/redo if (nameChanged) { d._filePath = localFilePath; d._autoSave->setManagedFile(QUrl::fromLocalFile(localFilePath)); } //Settings::self()->setCurrentGroup("Bookmarks"); //Settings::self()->addItemIntList(d._filePath.url(),d._bookmarkIndex); emit signalFileSaved(); emit signalFileSaved(localFilePath); return true; /* else if (status==NO_PERMISSIONS) { if (KMessageBox::warningContinueCancel(this, i18n("You do not have permission to write to file:\n%1\n" "Do you want to save to another file or cancel?", _currentURL.prettyUrl()), i18n("Error"),KStandardGuiItem::save())==KMessageBox::Continue) return fileSaveAs(); } */ } void Catalog::doAutoSave() { if (isClean() || !(d._autoSaveDirty)) return; if (Q_UNLIKELY(!m_storage)) return; if (!d._autoSave->open(QIODevice::WriteOnly)) { emit signalFileAutoSaveFailed(d._autoSave->fileName()); return; } qCInfo(LOKALIZE_LOG) << "doAutoSave" << d._autoSave->fileName(); m_storage->save(d._autoSave); d._autoSave->close(); d._autoSaveDirty = false; } void Catalog::projectConfigChanged() { setActivePhase(activePhase(), Project::local()->role()); } QByteArray Catalog::contents() { QBuffer buf; buf.open(QIODevice::WriteOnly); m_storage->save(&buf); buf.close(); return buf.data(); } //END OPEN/SAVE /** * helper method to keep db in a good shape :) * called on * 1) entry switch * 2) automatic editing code like replace or undo/redo operation **/ static void updateDB( const QString& filePath, const QString& ctxt, const CatalogString& english, const CatalogString& newTarget, int form, bool approved, const QString& dbName //const DocPosition&,//for back tracking ) { TM::UpdateJob* j = new TM::UpdateJob(filePath, ctxt, english, newTarget, form, approved, dbName); TM::threadPool()->start(j); } //BEGIN UNDO/REDO const DocPosition& Catalog::undo() { QUndoStack::undo(); return d._lastModifiedPos; } const DocPosition& Catalog::redo() { QUndoStack::redo(); return d._lastModifiedPos; } void Catalog::flushUpdateDBBuffer() { if (!Settings::autoaddTM()) return; DocPosition pos = d._lastModifiedPos; if (pos.entry == -1 || pos.entry >= numberOfEntries()) { //nothing to flush //qCWarning(LOKALIZE_LOG)<<"nothing to flush or new file opened"; return; } QString dbName; if (Project::instance()->targetLangCode() == targetLangCode()) { dbName = Project::instance()->projectID(); } else { dbName = sourceLangCode() + '-' + targetLangCode(); qCInfo(LOKALIZE_LOG) << "updating" << dbName << "because target language of project db does not match" << Project::instance()->targetLangCode() << targetLangCode(); if (!TM::DBFilesModel::instance()->m_configurations.contains(dbName)) { TM::OpenDBJob* openDBJob = new TM::OpenDBJob(dbName, TM::Local, true); connect(openDBJob, &TM::OpenDBJob::done, TM::DBFilesModel::instance(), &TM::DBFilesModel::updateProjectTmIndex); openDBJob->m_setParams = true; openDBJob->m_tmConfig.markup = Project::instance()->markup(); openDBJob->m_tmConfig.accel = Project::instance()->accel(); openDBJob->m_tmConfig.sourceLangCode = sourceLangCode(); openDBJob->m_tmConfig.targetLangCode = targetLangCode(); TM::DBFilesModel::instance()->openDB(openDBJob); } } int form = -1; if (isPlural(pos.entry)) form = pos.form; updateDB(url(), context(pos.entry).first(), sourceWithTags(pos), targetWithTags(pos), form, isApproved(pos.entry), dbName); d._lastModifiedPos = DocPosition(); } void Catalog::setLastModifiedPos(const DocPosition& pos) { if (pos.entry >= numberOfEntries()) //bin-units return; bool entryChanged = DocPos(d._lastModifiedPos) != DocPos(pos); if (entryChanged) flushUpdateDBBuffer(); d._lastModifiedPos = pos; } bool CatalogPrivate::addToEmptyIndexIfAppropriate(CatalogStorage* storage, const DocPosition& pos, bool alreadyEmpty) { if ((!pos.offset) && (storage->target(pos).isEmpty()) && (!alreadyEmpty)) { insertInList(_emptyIndex, pos.entry); return true; } return false; } void Catalog::targetDelete(const DocPosition& pos, int count) { if (Q_UNLIKELY(!m_storage)) return; bool alreadyEmpty = m_storage->isEmpty(pos); m_storage->targetDelete(pos, count); if (d.addToEmptyIndexIfAppropriate(m_storage, pos, alreadyEmpty)) emit signalNumberOfEmptyChanged(); emit signalEntryModified(pos); } bool CatalogPrivate::removeFromUntransIndexIfAppropriate(CatalogStorage* storage, const DocPosition& pos) { if ((!pos.offset) && (storage->isEmpty(pos))) { _emptyIndex.removeAll(pos.entry); return true; } return false; } void Catalog::targetInsert(const DocPosition& pos, const QString& arg) { if (Q_UNLIKELY(!m_storage)) return; if (d.removeFromUntransIndexIfAppropriate(m_storage, pos)) emit signalNumberOfEmptyChanged(); m_storage->targetInsert(pos, arg); emit signalEntryModified(pos); } void Catalog::targetInsertTag(const DocPosition& pos, const InlineTag& tag) { if (Q_UNLIKELY(!m_storage)) return; if (d.removeFromUntransIndexIfAppropriate(m_storage, pos)) emit signalNumberOfEmptyChanged(); m_storage->targetInsertTag(pos, tag); emit signalEntryModified(pos); } InlineTag Catalog::targetDeleteTag(const DocPosition& pos) { if (Q_UNLIKELY(!m_storage)) return InlineTag(); bool alreadyEmpty = m_storage->isEmpty(pos); InlineTag tag = m_storage->targetDeleteTag(pos); if (d.addToEmptyIndexIfAppropriate(m_storage, pos, alreadyEmpty)) emit signalNumberOfEmptyChanged(); emit signalEntryModified(pos); return tag; } void Catalog::setTarget(DocPosition pos, const CatalogString& s) { //TODO for case of markup present m_storage->setTarget(pos, s.string); } TargetState Catalog::setState(const DocPosition& pos, TargetState state) { bool extendedStates = m_storage && m_storage->capabilities()&ExtendedStates; bool approved =::isApproved(state, activePhaseRole()); if (Q_UNLIKELY(!m_storage || (extendedStates && m_storage->state(pos) == state) || (!extendedStates && m_storage->isApproved(pos) == approved))) return this->state(pos); TargetState prevState; if (extendedStates) { prevState = m_storage->setState(pos, state); d._statesIndex[prevState].removeAll(pos.entry); insertInList(d._statesIndex[state], pos.entry); } else { prevState = closestState(!approved, activePhaseRole()); m_storage->setApproved(pos, approved); } if (!approved) { insertInList(d._nonApprovedIndex, pos.entry); if (!m_storage->isEmpty(pos)) insertInList(d._nonApprovedNonEmptyIndex, pos.entry); } else { d._nonApprovedIndex.removeAll(pos.entry); d._nonApprovedNonEmptyIndex.removeAll(pos.entry); } emit signalNumberOfFuzziesChanged(); emit signalEntryModified(pos); return prevState; } Phase Catalog::updatePhase(const Phase& phase) { return m_storage->updatePhase(phase); } void Catalog::setEquivTrans(const DocPosition& pos, bool equivTrans) { if (m_storage) m_storage->setEquivTrans(pos, equivTrans); } bool Catalog::setModified(DocPos entry, bool modified) { if (modified) { if (d._modifiedEntries.contains(entry)) return false; d._modifiedEntries.insert(entry); } else d._modifiedEntries.remove(entry); return true; } bool Catalog::isModified(DocPos entry) const { return d._modifiedEntries.contains(entry); } bool Catalog::isModified(int entry) const { if (!isPlural(entry)) return isModified(DocPos(entry, 0)); int f = numberOfPluralForms(); while (--f >= 0) if (isModified(DocPos(entry, f))) return true; return false; } //END UNDO/REDO int findNextInList(const QLinkedList& list, int index) { int nextIndex = -1; foreach (int key, list) { if (Q_UNLIKELY(key > index)) { nextIndex = key; break; } } return nextIndex; } int findPrevInList(const QLinkedList& list, int index) { int prevIndex = -1; foreach (int key, list) { if (Q_UNLIKELY(key >= index)) break; prevIndex = key; } return prevIndex; } void insertInList(QLinkedList& list, int index) { QLinkedList::Iterator it = list.begin(); while (it != list.end() && index > *it) ++it; list.insert(it, index); } void Catalog::setBookmark(uint idx, bool set) { if (set) insertInList(d._bookmarkIndex, idx); else d._bookmarkIndex.removeAll(idx); } bool isApproved(TargetState state, ProjectLocal::PersonRole role) { static const TargetState marginStates[] = {Translated, Final, SignedOff}; return state >= marginStates[role]; } bool isApproved(TargetState state) { static const TargetState marginStates[] = {Translated, Final, SignedOff}; return state == marginStates[0] || state == marginStates[1] || state == marginStates[2]; } TargetState closestState(bool approved, ProjectLocal::PersonRole role) { Q_ASSERT(role != ProjectLocal::Undefined); static const TargetState approvementStates[][3] = { {NeedsTranslation, NeedsReviewTranslation, NeedsReviewTranslation}, {Translated, Final, SignedOff} }; return approvementStates[approved][role]; } bool Catalog::isObsolete(int entry) const { if (Q_UNLIKELY(!m_storage)) return false; return m_storage->isObsolete(entry); } QString Catalog::originalOdfFilePath() { if (Q_UNLIKELY(!m_storage)) return QString(); return m_storage->originalOdfFilePath(); } void Catalog::setOriginalOdfFilePath(const QString& odfFilePath) { if (Q_UNLIKELY(!m_storage)) return; m_storage->setOriginalOdfFilePath(odfFilePath); } diff --git a/src/glossary/glossary.cpp b/src/glossary/glossary.cpp index 41f455b..cd8d887 100644 --- a/src/glossary/glossary.cpp +++ b/src/glossary/glossary.cpp @@ -1,737 +1,734 @@ /* **************************************************************************** This file is part of Lokalize Copyright (C) 2007-2011 by Nick Shaforostoff 2018-2019 by Simon Depiets This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **************************************************************************** */ #include "glossary.h" #include "lokalize_debug.h" #include "stemming.h" // #include "tbxparser.h" #include "project.h" #include "prefs_lokalize.h" #include "domroutines.h" #include #include #include #include #include #include #include #include #include using namespace GlossaryNS; static const QString defaultLang = QStringLiteral("en_US"); static const QString xmlLang = QStringLiteral("xml:lang"); static const QString ntig = QStringLiteral("ntig"); static const QString tig = QStringLiteral("tig"); static const QString termGrp = QStringLiteral("termGrp"); static const QString langSet = QStringLiteral("langSet"); static const QString term = QStringLiteral("term"); static const QString id = QStringLiteral("id"); QList Glossary::idsForLangWord(const QString& lang, const QString& word) const { return idsByLangWord[lang].values(word); } Glossary::Glossary(QObject* parent) : QObject(parent) , m_clean(true) { } //BEGIN DISK bool Glossary::load(const QString& newPath) { QElapsedTimer a; a.start(); //BEGIN NEW QIODevice* device = new QFile(newPath); if (!device->open(QFile::ReadOnly | QFile::Text)) { delete device; //return; device = new QBuffer(); static_cast(device)->setData(QByteArray( "\n" "\n" "\n" " \n" " \n" " \n" " \n" "\n" )); } QXmlSimpleReader reader; reader.setFeature("http://qt-project.org/xml/features/report-whitespace-only-CharData",true); reader.setFeature("http://xml.org/sax/features/namespaces", false); QXmlInputSource source(device); QDomDocument newDoc; QString errorMsg; int errorLine;//+errorColumn; bool success = newDoc.setContent(&source, &reader, &errorMsg, &errorLine/*,errorColumn*/); delete device; if (!success) { qCWarning(LOKALIZE_LOG) << errorMsg; return false; //errorLine+1; } clear();//does setClean(true); m_path = newPath; m_doc = newDoc; //QDomElement file=m_doc.elementsByTagName("file").at(0).toElement(); m_entries = m_doc.elementsByTagName(QStringLiteral("termEntry")); for (int i = 0; i < m_entries.size(); i++) hashTermEntry(m_entries.at(i).toElement()); m_idsForEntriesById = m_entriesById.keys(); //END NEW #if 0 TbxParser parser(this); QXmlSimpleReader reader1; reader1.setContentHandler(&parser); QFile file(p); if (!file.open(QFile::ReadOnly | QFile::Text)) return; QXmlInputSource xmlInputSource(&file); if (!reader1.parse(xmlInputSource)) qCWarning(LOKALIZE_LOG) << "failed to load " << path; #endif emit loaded(); if (a.elapsed() > 50) qCDebug(LOKALIZE_LOG) << "glossary loaded in" << a.elapsed(); return true; } bool Glossary::save() { if (m_path.isEmpty()) return false; QFile* device = new QFile(m_path); if (!device->open(QFile::WriteOnly | QFile::Truncate)) { device->deleteLater(); return false; } QTextStream stream(device); m_doc.save(stream, 2); device->deleteLater(); setClean(true); return true; } void Glossary::setClean(bool clean) { m_clean = clean; emit changed();//may be emitted multiple times in a row. so what? :) } //END DISK //BEGIN MODEL #define FETCH_SIZE 128 void GlossarySortFilterProxyModel::setFilterRegExp(const QString& s) { if (!sourceModel()) return; //static const QRegExp lettersOnly("^[a-z]"); QSortFilterProxyModel::setFilterRegExp(s); fetchMore(QModelIndex()); } void GlossarySortFilterProxyModel::fetchMore(const QModelIndex&) { int expectedCount = rowCount() + FETCH_SIZE / 2; while (rowCount(QModelIndex()) < expectedCount && sourceModel()->canFetchMore(QModelIndex())) { sourceModel()->fetchMore(QModelIndex()); - //qCDebug(LOKALIZE_LOG)<<"filter:"<rowCount(); QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers); } } GlossaryModel::GlossaryModel(QObject* parent) : QAbstractListModel(parent) , m_visibleCount(0) , m_glossary(Project::instance()->glossary()) { connect(m_glossary, &Glossary::loaded, this, &GlossaryModel::forceReset); } void GlossaryModel::forceReset() { beginResetModel(); m_visibleCount = 0; endResetModel(); } bool GlossaryModel::canFetchMore(const QModelIndex&) const { return false;//!parent.isValid() && m_glossary->size()!=m_visibleCount; } void GlossaryModel::fetchMore(const QModelIndex& parent) { int newVisibleCount = qMin(m_visibleCount + FETCH_SIZE, m_glossary->size()); beginInsertRows(parent, m_visibleCount, newVisibleCount - 1); m_visibleCount = newVisibleCount; endInsertRows(); } int GlossaryModel::rowCount(const QModelIndex& parent) const { if (parent.isValid()) return 0; return m_glossary->size();//m_visibleCount; } QVariant GlossaryModel::headerData(int section, Qt::Orientation /*orientation*/, int role) const { if (role != Qt::DisplayRole) return QVariant(); switch (section) { //case ID: return i18nc("@title:column","ID"); case English: return i18nc("@title:column Original text", "Source");; case Target: return i18nc("@title:column Text in target language", "Target"); case SubjectField: return i18nc("@title:column", "Subject Field"); } return QVariant(); } QVariant GlossaryModel::data(const QModelIndex& index, int role) const { //if (role==Qt::SizeHintRole) // return QVariant(QSize(50, 30)); if (role != Qt::DisplayRole) return QVariant(); static const QString nl = QStringLiteral(" ") + QChar(0x00B7) + ' '; static Project* project = Project::instance(); Glossary* glossary = m_glossary; QByteArray id = glossary->id(index.row()); switch (index.column()) { case ID: return id; case English: return glossary->terms(id, project->sourceLangCode()).join(nl); case Target: return glossary->terms(id, project->targetLangCode()).join(nl); case SubjectField: return glossary->subjectField(id); } return QVariant(); } /* QModelIndex GlossaryModel::index (int row,int column,const QModelIndex& parent) const { return createIndex (row, column); } */ int GlossaryModel::columnCount(const QModelIndex&) const { return GlossaryModelColumnCount; } /* Qt::ItemFlags GlossaryModel::flags ( const QModelIndex & index ) const { return Qt::ItemIsSelectable|Qt::ItemIsEnabled; //if (index.column()==FuzzyFlag) // return Qt::ItemIsSelectable|Qt::ItemIsUserCheckable|Qt::ItemIsEnabled; //return QAbstractItemModel::flags(index); } */ //END MODEL general (GlossaryModel continues below) //BEGIN EDITING QByteArray Glossary::generateNewId() { // generate unique ID int idNumber = 0; QList busyIdNumbers; QString authorId(Settings::authorName().toLower()); authorId.replace(' ', '_'); QRegExp rx('^' + authorId + QStringLiteral("\\-([0-9]*)$")); foreach (const QByteArray& id, m_idsForEntriesById) { if (rx.exactMatch(QString::fromLatin1(id))) busyIdNumbers.append(rx.cap(1).toInt()); } int i = removedIds.size(); while (--i >= 0) { if (rx.exactMatch(QString::fromLatin1(removedIds.at(i)))) busyIdNumbers.append(rx.cap(1).toInt()); } if (!busyIdNumbers.isEmpty()) { std::sort(busyIdNumbers.begin(), busyIdNumbers.end()); while (busyIdNumbers.contains(idNumber)) ++idNumber; } return authorId.toLatin1() + '-' + QByteArray::number(idNumber); } QStringList Glossary::subjectFields() const { QSet result; foreach (const QByteArray& id, m_idsForEntriesById) result.insert(subjectField(id)); return result.values(); } QByteArray Glossary::id(int index) const { if (index < m_idsForEntriesById.size()) return m_idsForEntriesById.at(index); return QByteArray(); } QStringList Glossary::terms(const QByteArray& id, const QString& language) const { QString minusLang = language; minusLang.replace('_', '-'); QStringRef soleLang = language.leftRef(2); QStringList result; QDomElement n = m_entriesById.value(id).firstChildElement(langSet); while (!n.isNull()) { QString lang = n.attribute(xmlLang, defaultLang); if (language == lang || minusLang == lang || soleLang == lang) { QDomElement ntigElem = n.firstChildElement(ntig); while (!ntigElem.isNull()) { result << ntigElem.firstChildElement(termGrp).firstChildElement(term).text(); ntigElem = ntigElem.nextSiblingElement(ntig); } QDomElement tigElem = n.firstChildElement(tig); while (!tigElem.isNull()) { result << tigElem.firstChildElement(term).text(); tigElem = tigElem.nextSiblingElement(tig); } } n = n.nextSiblingElement(langSet); } return result; } // QDomElement ourLangSetElement will reference the lang tag we want (if it exists) static void getElementsForTermLangIndex(QDomElement termEntry, QString& lang, int index, QDomElement& ourLangSetElement, QDomElement& tigElement, //<-- can point to as well QDomElement& termElement) { QString minusLang = lang; minusLang.replace('_', '-'); QStringRef soleLang = lang.leftRef(2); - //qCDebug(LOKALIZE_LOG)<<"started walking over"<& wordHash, // const QString& what, // int index) void Glossary::hashTermEntry(const QDomElement& termEntry) { QByteArray entryId = termEntry.attribute(::id).toLatin1(); if (entryId.isEmpty()) return; m_entriesById.insert(entryId, termEntry); QString sourceLangCode = Project::instance()->sourceLangCode(); foreach (const QString& termText, terms(entryId, sourceLangCode)) { foreach (const QString& word, termText.split(' ', QString::SkipEmptyParts)) idsByLangWord[sourceLangCode].insert(stem(sourceLangCode, word), entryId); } } void Glossary::unhashTermEntry(const QDomElement& termEntry) { QByteArray entryId = termEntry.attribute(::id).toLatin1(); m_entriesById.remove(entryId); QString sourceLangCode = Project::instance()->sourceLangCode(); foreach (const QString& termText, terms(entryId, sourceLangCode)) { foreach (const QString& word, termText.split(' ', QString::SkipEmptyParts)) idsByLangWord[sourceLangCode].remove(stem(sourceLangCode, word), entryId); } } #if 0 void Glossary::hashTermEntry(int index) { Q_ASSERT(index < termList.size()); foreach (const QString& term, termList_.at(index).english) { foreach (const QString& word, term.split(' ', QString::SkipEmptyParts)) wordHash_.insert(stem(Project::instance()->sourceLangCode(), word), index); } } void Glossary::unhashTermEntry(int index) { Q_ASSERT(index < termList.size()); foreach (const QString& term, termList_.at(index).english) { foreach (const QString& word, term.split(' ', QString::SkipEmptyParts)) wordHash_.remove(stem(Project::instance()->sourceLangCode(), word), index); } } #endif void Glossary::removeEntry(const QByteArray& id) { if (!m_entriesById.contains(id)) return; QDomElement entry = m_entriesById.value(id); if (entry.nextSibling().isCharacterData()) entry.parentNode().removeChild(entry.nextSibling()); //nice formatting entry.parentNode().removeChild(entry); m_entriesById.remove(id); unhashTermEntry(entry); m_idsForEntriesById = m_entriesById.keys(); removedIds.append(id); //for new id generation goodness setClean(false); } static void appendTerm(QDomElement langSetElem, const QString& termText) { QDomDocument doc = langSetElem.ownerDocument(); /* QDomElement ntigElement=doc.createElement(ntig); langSetElem.appendChild(ntigElement); QDomElement termGrpElement=doc.createElement(termGrp); ntigElement.appendChild(termGrpElement); QDomElement termElement=doc.createElement(term); termGrpElement.appendChild(termElement); termElement.appendChild(doc.createTextNode(termText)); */ QDomElement tigElement = doc.createElement(tig); langSetElem.appendChild(tigElement); QDomElement termElement = doc.createElement(term); tigElement.appendChild(termElement); termElement.appendChild(doc.createTextNode(termText)); } QByteArray Glossary::append(const QStringList& sourceTerms, const QStringList& targetTerms) { if (!m_doc.elementsByTagName(QStringLiteral("body")).count()) return QByteArray(); setClean(false); QDomElement termEntry = m_doc.createElement(QStringLiteral("termEntry")); m_doc.elementsByTagName(QStringLiteral("body")).at(0).appendChild(termEntry); //m_entries=m_doc.elementsByTagName("termEntry"); QByteArray newId = generateNewId(); termEntry.setAttribute(::id, QString::fromLatin1(newId)); QDomElement sourceElem = m_doc.createElement(langSet); termEntry.appendChild(sourceElem); sourceElem.setAttribute(xmlLang, Project::instance()->sourceLangCode().replace('_', '-')); foreach (QString sourceTerm, sourceTerms) appendTerm(sourceElem, sourceTerm); QDomElement targetElem = m_doc.createElement(langSet); termEntry.appendChild(targetElem); targetElem.setAttribute(xmlLang, Project::instance()->targetLangCode().replace('_', '-')); foreach (QString targetTerm, targetTerms) appendTerm(targetElem, targetTerm); hashTermEntry(termEntry); m_idsForEntriesById = m_entriesById.keys(); return newId; } void Glossary::append(const QString& _english, const QString& _target) { append(QStringList(_english), QStringList(_target)); } void Glossary::clear() { setClean(true); //path.clear(); idsByLangWord.clear(); m_entriesById.clear(); m_idsForEntriesById.clear(); removedIds.clear(); changedIds_.clear(); addedIds_.clear(); wordHash_.clear(); termList_.clear(); langWordEntry_.clear(); subjectFields_ = QStringList(QString()); m_doc.clear(); } bool GlossaryModel::removeRows(int row, int count, const QModelIndex& parent) { beginRemoveRows(parent, row, row + count - 1); Glossary* glossary = Project::instance()->glossary(); int i = row + count; while (--i >= row) glossary->removeEntry(glossary->id(i)); endRemoveRows(); return true; } // bool GlossaryModel::insertRows(int row,int count,const QModelIndex& parent) // { // if (row!=rowCount()) // return false; QByteArray GlossaryModel::appendRow(const QString& _english, const QString& _target) { bool notify = !canFetchMore(QModelIndex()); if (notify) beginInsertRows(QModelIndex(), rowCount(), rowCount()); QByteArray id = m_glossary->append(QStringList(_english), QStringList(_target)); if (notify) { m_visibleCount++; endInsertRows(); } return id; } //END EDITING diff --git a/src/glossary/glossarywindow.cpp b/src/glossary/glossarywindow.cpp index 90e1155..a699169 100644 --- a/src/glossary/glossarywindow.cpp +++ b/src/glossary/glossarywindow.cpp @@ -1,563 +1,560 @@ /* **************************************************************************** This file is part of Lokalize Copyright (C) 2007-2014 by Nick Shaforostoff 2018-2019 by Simon Depiets This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **************************************************************************** */ #include "glossarywindow.h" #include "lokalize_debug.h" #include "glossary.h" #include "project.h" #include "languagelistmodel.h" #include "ui_termedit.h" #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace GlossaryNS; //BEGIN GlossaryTreeView GlossaryTreeView::GlossaryTreeView(QWidget *parent) : QTreeView(parent) { setSortingEnabled(true); sortByColumn(GlossaryModel::English, Qt::AscendingOrder); setItemsExpandable(false); setAllColumnsShowFocus(true); /* setSelectionMode(QAbstractItemView::ExtendedSelection); setSelectionBehavior(QAbstractItemView::SelectRows);*/ } static QByteArray modelIndexToId(const QModelIndex& item) { return item.sibling(item.row(), 0).data(Qt::DisplayRole).toByteArray(); } void GlossaryTreeView::currentChanged(const QModelIndex& current, const QModelIndex&/* previous*/) { if (current.isValid()) { //QModelIndex item=static_cast(model())->mapToSource(current); //emit currentChanged(item.row()); emit currentChanged(modelIndexToId(current)); scrollTo(current); } } void GlossaryTreeView::selectRow(int i) { QSortFilterProxyModel* proxyModel = static_cast(model()); GlossaryModel* sourceModel = static_cast(proxyModel->sourceModel()); //sourceModel->forceReset(); setCurrentIndex(proxyModel->mapFromSource(sourceModel->index(i, 0))); } //END GlossaryTreeView //BEGIN SubjectFieldModel //typedef QStringListModel SubjectFieldModel; #if 0 class SubjectFieldModel: public QAbstractItemModel { public: //Q_OBJECT SubjectFieldModel(QObject* parent); ~SubjectFieldModel() {} QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const; QModelIndex parent(const QModelIndex&) const; int rowCount(const QModelIndex& parent = QModelIndex()) const; int columnCount(const QModelIndex& parent = QModelIndex()) const; QVariant data(const QModelIndex&, int role = Qt::DisplayRole) const; bool setData(const QModelIndex&, const QVariant&, int role = Qt::EditRole); bool setItemData(const QModelIndex& index, const QMap& roles); bool insertRows(int row, int count, const QModelIndex& parent = QModelIndex()); Qt::ItemFlags flags(const QModelIndex&) const; /*private: Catalog* m_catalog;*/ }; inline SubjectFieldModel::SubjectFieldModel(QObject* parent) : QAbstractItemModel(parent) // , m_catalog(catalog) { } QModelIndex SubjectFieldModel::index(int row, int column, const QModelIndex& /*parent*/) const { return createIndex(row, column); } Qt::ItemFlags SubjectFieldModel::flags(const QModelIndex&) const { return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled; } QModelIndex SubjectFieldModel::parent(const QModelIndex& /*index*/) const { return QModelIndex(); } int SubjectFieldModel::columnCount(const QModelIndex& parent) const { if (parent.isValid()) return 0; return 1; } /* inline Qt::ItemFlags SubjectFieldModel::flags ( const QModelIndex & index ) const { if (index.column()==FuzzyFlag) return Qt::ItemIsSelectable|Qt::ItemIsUserCheckable|Qt::ItemIsEnabled; return QAbstractItemModel::flags(index); }*/ int SubjectFieldModel::rowCount(const QModelIndex& parent) const { if (parent.isValid()) return 0; return Project::instance()->glossary()->subjectFields.size(); } QVariant SubjectFieldModel::data(const QModelIndex& index, int role) const { if (role == Qt::DisplayRole || role == Qt::EditRole) return Project::instance()->glossary()->subjectFields.at(index.row()); return QVariant(); } bool SubjectFieldModel::insertRows(int row, int count, const QModelIndex& parent) { beginInsertRows(parent, row, row + count - 1); QStringList& subjectFields = Project::instance()->glossary()->subjectFields; while (--count >= 0) subjectFields.insert(row + count, QString()); endInsertRows(); return true; } bool SubjectFieldModel::setData(const QModelIndex& index, const QVariant& value, int role) { - qCDebug(LOKALIZE_LOG) << role; QStringList& subjectFields = Project::instance()->glossary()->subjectFields; subjectFields[index.row()] = value.toString(); return true; } bool SubjectFieldModel::setItemData(const QModelIndex& index, const QMap& roles) { if (roles.contains(Qt::EditRole)) { QStringList& subjectFields = Project::instance()->glossary()->subjectFields; subjectFields[index.row()] = roles.value(Qt::EditRole).toString(); } return true; } #endif //END SubjectFieldModel //BEGIN GlossaryWindow GlossaryWindow::GlossaryWindow(QWidget *parent) : KMainWindow(parent) , m_browser(new GlossaryTreeView(this)) , m_proxyModel(new GlossarySortFilterProxyModel(this)) , m_reactOnSignals(true) { //setAttribute(Qt::WA_DeleteOnClose, true); setAttribute(Qt::WA_DeleteOnClose, false); QSplitter* splitter = new QSplitter(Qt::Horizontal, this); setCentralWidget(splitter); m_proxyModel->setFilterKeyColumn(-1); m_proxyModel->setDynamicSortFilter(true);; GlossaryModel* model = new GlossaryModel(this); m_proxyModel->setSourceModel(model); m_browser->setModel(m_proxyModel); m_browser->setUniformRowHeights(true); m_browser->setAutoScroll(true); m_browser->setColumnHidden(GlossaryModel::ID, true); m_browser->setColumnWidth(GlossaryModel::English, m_browser->columnWidth(GlossaryModel::English) * 2); //man this is HACK y m_browser->setColumnWidth(GlossaryModel::Target, m_browser->columnWidth(GlossaryModel::Target) * 2); m_browser->setAlternatingRowColors(true); //left QWidget* w = new QWidget(splitter); QVBoxLayout* layout = new QVBoxLayout(w); m_filterEdit = new QLineEdit(w); m_filterEdit->setClearButtonEnabled(true); m_filterEdit->setPlaceholderText(i18n("Quick search...")); m_filterEdit->setFocus(); m_filterEdit->setToolTip(i18nc("@info:tooltip", "Activated by Ctrl+L.") + ' ' + i18nc("@info:tooltip", "Accepts regular expressions")); new QShortcut(Qt::CTRL + Qt::Key_L, this, SLOT(setFocus()), 0, Qt::WidgetWithChildrenShortcut); connect(m_filterEdit, &QLineEdit::textChanged, m_proxyModel, &GlossaryNS::GlossarySortFilterProxyModel::setFilterRegExp); layout->addWidget(m_filterEdit); layout->addWidget(m_browser); { QPushButton* addBtn = new QPushButton(w); connect(addBtn, &QPushButton::clicked, this, QOverload<>::of(&GlossaryWindow::newTermEntry)); QPushButton* rmBtn = new QPushButton(w); connect(rmBtn, &QPushButton::clicked, this, QOverload<>::of(&GlossaryWindow::rmTermEntry)); KGuiItem::assign(addBtn, KStandardGuiItem::add()); KGuiItem::assign(rmBtn, KStandardGuiItem::remove()); QPushButton* restoreBtn = new QPushButton(i18nc("@action:button reloads glossary from disk", "Restore from disk"), w); restoreBtn->setToolTip(i18nc("@info:tooltip", "Reload glossary from disk, discarding any changes")); connect(restoreBtn, &QPushButton::clicked, this, &GlossaryWindow::restore); QWidget* btns = new QWidget(w); QHBoxLayout* btnsLayout = new QHBoxLayout(btns); btnsLayout->addWidget(addBtn); btnsLayout->addWidget(rmBtn); btnsLayout->addWidget(restoreBtn); layout->addWidget(btns); //QWidget::setTabOrder(m_browser,addBtn); QWidget::setTabOrder(addBtn, rmBtn); QWidget::setTabOrder(rmBtn, restoreBtn); QWidget::setTabOrder(restoreBtn, m_filterEdit); } QWidget::setTabOrder(m_filterEdit, m_browser); splitter->addWidget(w); //right m_editor = new QWidget(splitter); m_editor->hide(); Ui_TermEdit ui_termEdit; ui_termEdit.setupUi(m_editor); splitter->addWidget(m_editor); Project* project = Project::instance(); m_sourceTermsModel = new TermsListModel(project->glossary(), project->sourceLangCode(), this); m_targetTermsModel = new TermsListModel(project->glossary(), project->targetLangCode(), this); ui_termEdit.sourceTermsView->setModel(m_sourceTermsModel); ui_termEdit.targetTermsView->setModel(m_targetTermsModel); connect(ui_termEdit.addEngTerm, &QToolButton::clicked, ui_termEdit.sourceTermsView, &TermListView::addTerm); connect(ui_termEdit.remEngTerm, &QToolButton::clicked, ui_termEdit.sourceTermsView, &TermListView::rmTerms); connect(ui_termEdit.addTargetTerm, &QToolButton::clicked, ui_termEdit.targetTermsView, &TermListView::addTerm); connect(ui_termEdit.remTargetTerm, &QToolButton::clicked, ui_termEdit.targetTermsView, &TermListView::rmTerms); m_sourceTermsView = ui_termEdit.sourceTermsView; m_targetTermsView = ui_termEdit.targetTermsView; m_subjectField = ui_termEdit.subjectField; m_definition = ui_termEdit.definition; m_definitionLang = ui_termEdit.definitionLang; //connect (m_english,SIGNAL(textChanged()), this,SLOT(applyEntryChange())); //connect (m_target,SIGNAL(textChanged()), this,SLOT(applyEntryChange())); //connect (m_definition,SIGNAL(editingFinished()),this,SLOT(applyEntryChange())); //connect (m_definition,SIGNAL(textChanged()),this,SLOT(applyEntryChange())); //connect (m_subjectField,SIGNAL(editTextChanged(QString)),this,SLOT(applyEntryChange())); connect(m_subjectField->lineEdit(), &QLineEdit::editingFinished, this, &GlossaryWindow::applyEntryChange); //m_subjectField->addItems(Project::instance()->glossary()->subjectFields()); //m_subjectField->setModel(new SubjectFieldModel(this)); QStringList subjectFields = Project::instance()->glossary()->subjectFields(); std::sort(subjectFields.begin(), subjectFields.end()); QStringListModel* subjectFieldsModel = new QStringListModel(this); subjectFieldsModel->setStringList(subjectFields); m_subjectField->setModel(subjectFieldsModel); connect(m_browser, QOverload::of(&GlossaryTreeView::currentChanged), this, &GlossaryWindow::currentChanged); connect(m_browser, QOverload::of(&GlossaryTreeView::currentChanged), this, &GlossaryWindow::showEntryInEditor); connect(m_definitionLang, QOverload::of(&KComboBox::activated), this, &GlossaryWindow::showDefinitionForLang); m_definitionLang->setModel(LanguageListModel::emptyLangInstance()->sortModel()); m_definitionLang->setCurrentIndex(LanguageListModel::emptyLangInstance()->sortModelRowForLangCode(m_defLang));//empty lang //TODO //connect(m_targetTermsModel,SIGNAL(dataChanged(QModelIndex,QModelIndex)),m_browser,SLOT(setFocus())); setAutoSaveSettings(QLatin1String("GlossaryWindow"), true); //Glossary* glossary=Project::instance()->glossary(); /*setCaption(i18nc("@title:window","Glossary"), !glossary->changedIds.isEmpty()||!glossary->addedIds.isEmpty()||!glossary->removedIds.isEmpty()); */ } void GlossaryWindow::setFocus() { m_filterEdit->setFocus(); m_filterEdit->selectAll(); } void GlossaryWindow::showEntryInEditor(const QByteArray& id) { if (m_editor->isVisible()) applyEntryChange(); else m_editor->show(); m_id = id; m_reactOnSignals = false; Project* project = Project::instance(); Glossary* glossary = project->glossary(); m_subjectField->setCurrentItem(glossary->subjectField(id),/*insert*/true); QStringList langsToTry = QStringList(m_defLang) << QStringLiteral("en") << QStringLiteral("en_US") << project->targetLangCode(); foreach (const QString& lang, langsToTry) { QString d = glossary->definition(m_id, lang); if (!d.isEmpty()) { if (m_defLang != lang) m_definitionLang->setCurrentIndex(LanguageListModel::emptyLangInstance()->sortModelRowForLangCode(lang)); m_defLang = lang; break; } } m_definition->setPlainText(glossary->definition(m_id, m_defLang)); m_sourceTermsModel->setEntry(id); m_targetTermsModel->setEntry(id); //m_sourceTermsModel->setStringList(glossary->terms(id,project->sourceLangCode())); //m_targetTermsModel->setStringList(glossary->terms(id,project->targetLangCode())); m_reactOnSignals = true; } void GlossaryWindow::currentChanged(int i) { Q_UNUSED(i); m_reactOnSignals = false; m_editor->show(); m_reactOnSignals = true; } void GlossaryWindow::showDefinitionForLang(int langModelIndex) { applyEntryChange(); m_defLang = LanguageListModel::emptyLangInstance()->langCodeForSortModelRow(langModelIndex); m_definition->setPlainText(Project::instance()->glossary()->definition(m_id, m_defLang)); } void GlossaryWindow::applyEntryChange() { if (!m_reactOnSignals || !m_browser->currentIndex().isValid()) return; QByteArray id = m_id; //modelIndexToId(m_browser->currentIndex()); Project* project = Project::instance(); Glossary* glossary = project->glossary(); if (m_subjectField->currentText() != glossary->subjectField(id)) glossary->setSubjectField(id, QString(), m_subjectField->currentText()); if (m_definition->toPlainText() != glossary->definition(id, m_defLang)) glossary->setDefinition(id, m_defLang, m_definition->toPlainText()); //HACK to force finishing of the listview editing QWidget* prevFocusWidget = QApplication::focusWidget(); m_browser->setFocus(); if (prevFocusWidget) prevFocusWidget->setFocus(); // QSortFilterProxyModel* proxyModel=static_cast(model()); //GlossaryModel* sourceModel=static_cast(m_proxyModel->sourceModel()); const QModelIndex& idx = m_proxyModel->mapToSource(m_browser->currentIndex()); if (!idx.isValid()) return; //TODO display filename, optionally stripped like for filetab names setCaption(i18nc("@title:window", "Glossary"), !glossary->isClean()); } void GlossaryWindow::selectEntry(const QByteArray& id) { //let it fetch the rows QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers | QEventLoop::WaitForMoreEvents, 100); QModelIndexList items = m_proxyModel->match(m_proxyModel->index(0, 0), Qt::DisplayRole, QVariant(id), 1, 0); if (items.count()) { m_browser->setCurrentIndex(items.first()); m_browser->scrollTo(items.first(), QAbstractItemView::PositionAtCenter); - //qCDebug(LOKALIZE_LOG)<setCurrentIndex(QModelIndex()); showEntryInEditor(id); - //qCDebug(LOKALIZE_LOG)<(m_proxyModel->sourceModel()); QByteArray id = sourceModel->appendRow(_english, _target); selectEntry(id); } void GlossaryWindow::rmTermEntry() { rmTermEntry(-1); } void GlossaryWindow::rmTermEntry(int i) { setCaption(i18nc("@title:window", "Glossary"), true); //QSortFilterProxyModel* proxyModel=static_cast(model()); GlossaryModel* sourceModel = static_cast(m_proxyModel->sourceModel()); if (i == -1) { //NOTE actually we should remove selected items, not current one const QModelIndex& current = m_browser->currentIndex(); if (!current.isValid()) return; i = m_proxyModel->mapToSource(current).row(); } sourceModel->removeRow(i); } void GlossaryWindow::restore() { setCaption(i18nc("@title:window", "Glossary"), false); Glossary* glossary = Project::instance()->glossary(); glossary->load(glossary->path()); m_reactOnSignals = false; showEntryInEditor(m_id); m_reactOnSignals = true; } bool GlossaryWindow::save() { //TODO add error message return Project::instance()->glossary()->save(); } bool GlossaryWindow::queryClose() { Glossary* glossary = Project::instance()->glossary(); applyEntryChange(); if (glossary->isClean()) return true; switch (KMessageBox::warningYesNoCancel(this, i18nc("@info", "The glossary contains unsaved changes.\n\ Do you want to save your changes or discard them?"), i18nc("@title:window", "Warning"), KStandardGuiItem::save(), KStandardGuiItem::discard())) { case KMessageBox::Yes: return save(); case KMessageBox::No: restore(); return true; default: return false; } } //END GlossaryWindow void TermsListModel::setEntry(const QByteArray& id) { m_id = id; QStringList terms = m_glossary->terms(m_id, m_lang); terms.append(QString()); //allow adding new terms setStringList(terms); } bool TermsListModel::setData(const QModelIndex& index, const QVariant& value, int role) { Q_UNUSED(role); m_glossary->setTerm(m_id, m_lang, index.row(), value.toString()); setEntry(m_id); //allow adding new terms return true; } bool TermsListModel::removeRows(int row, int count, const QModelIndex& parent) { Q_UNUSED(count) if (row == rowCount() - 1) return false;// cannot delete non-existing item m_glossary->rmTerm(m_id, m_lang, row); return QStringListModel::removeRows(row, 1, parent); } void TermListView::addTerm() { setCurrentIndex(model()->index(model()->rowCount() - 1, 0)); edit(currentIndex()); } void TermListView::rmTerms() { foreach (const QModelIndex& row, selectionModel()->selectedRows()) model()->removeRow(row.row()); } diff --git a/src/lokalizemainwindow.cpp b/src/lokalizemainwindow.cpp index 666a6bb..9df022c 100644 --- a/src/lokalizemainwindow.cpp +++ b/src/lokalizemainwindow.cpp @@ -1,1089 +1,1087 @@ /* **************************************************************************** This file is part of Lokalize Copyright (C) 2008-2015 by Nick Shaforostoff 2018-2019 by Simon Depiets This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **************************************************************************** */ #include "lokalizemainwindow.h" #include "lokalize_debug.h" #include "actionproxy.h" #include "editortab.h" #include "projecttab.h" #include "tmtab.h" #include "jobs.h" #include "filesearchtab.h" #include "prefs_lokalize.h" // #define WEBQUERY_ENABLE #include "project.h" #include "projectmodel.h" #include "projectlocal.h" #include "prefs.h" #include "tools/widgettextcaptureconfig.h" #include "multieditoradaptor.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include LokalizeMainWindow::LokalizeMainWindow() : KXmlGuiWindow() , m_mdiArea(new LokalizeMdiArea) , m_prevSubWindow(0) , m_projectSubWindow(0) , m_translationMemorySubWindow(0) , m_editorActions(new QActionGroup(this)) , m_managerActions(new QActionGroup(this)) , m_spareEditor(new EditorTab(this, false)) , m_multiEditorAdaptor(new MultiEditorAdaptor(m_spareEditor)) , m_projectScriptingPlugin(0) { m_spareEditor->hide(); m_mdiArea->setViewMode(QMdiArea::TabbedView); m_mdiArea->setActivationOrder(QMdiArea::ActivationHistoryOrder); m_mdiArea->setDocumentMode(true); m_mdiArea->setTabsMovable(true); m_mdiArea->setTabsClosable(true); setCentralWidget(m_mdiArea); connect(m_mdiArea, &QMdiArea::subWindowActivated, this, &LokalizeMainWindow::slotSubWindowActivated); setupActions(); //prevent relayout of dockwidgets m_mdiArea->setOption(QMdiArea::DontMaximizeSubWindowOnActivation, true); connect(Project::instance(), QOverload::of(&Project::fileOpenRequested), this, QOverload::of(&LokalizeMainWindow::fileOpen_), Qt::QueuedConnection); connect(Project::instance(), &Project::configChanged, this, &LokalizeMainWindow::projectSettingsChanged); connect(Project::instance(), &Project::closed, this, &LokalizeMainWindow::closeProject); showProjectOverview(); showTranslationMemory(); //fix for #342558 for (int i = ID_STATUS_CURRENT; i <= ID_STATUS_ISFUZZY; i++) { m_statusBarLabels.append(new QLabel()); statusBar()->insertWidget(i, m_statusBarLabels.last(), 2); } setAttribute(Qt::WA_DeleteOnClose, true); if (!qApp->isSessionRestored()) { KConfig config; KConfigGroup stateGroup(&config, "State"); readProperties(stateGroup); } registerDBusAdaptor(); QTimer::singleShot(0, this, &LokalizeMainWindow::initLater); } void LokalizeMainWindow::initLater() { if (!m_prevSubWindow && m_projectSubWindow) slotSubWindowActivated(m_projectSubWindow); if (!Project::instance()->isTmSupported()) { KNotification* notification = new KNotification("NoSqlModulesAvailable", this); notification->setText(i18nc("@info", "No Qt Sql modules were found. Translation memory will not work.")); notification->sendEvent(); } } LokalizeMainWindow::~LokalizeMainWindow() { TM::cancelAllJobs(); KConfig config; KConfigGroup stateGroup(&config, "State"); if (!m_lastEditorState.isEmpty()) { stateGroup.writeEntry("DefaultDockWidgets", m_lastEditorState); } saveProjectState(stateGroup); m_multiEditorAdaptor->deleteLater(); //Disconnect the signals pointing to this MainWindow object QMdiSubWindow* sw; for (int i = 0; i < m_fileToEditor.values().count(); i++) { sw = m_fileToEditor.values().at(i); disconnect(sw, &QMdiSubWindow::destroyed, this, &LokalizeMainWindow::editorClosed); EditorTab* w = static_cast(sw->widget()); disconnect(w, &EditorTab::aboutToBeClosed, this, &LokalizeMainWindow::resetMultiEditorAdaptor); disconnect(w, QOverload::of(&EditorTab::fileOpenRequested), this, QOverload::of(&LokalizeMainWindow::fileOpen)); disconnect(w, QOverload::of(&EditorTab::tmLookupRequested), this, QOverload::of(&LokalizeMainWindow::lookupInTranslationMemory)); } qCWarning(LOKALIZE_LOG) << "MainWindow destroyed"; } void LokalizeMainWindow::slotSubWindowActivated(QMdiSubWindow* w) { //QTime aaa;aaa.start(); if (!w || m_prevSubWindow == w) return; w->setUpdatesEnabled(true); //QTBUG-23289 if (m_prevSubWindow) { m_prevSubWindow->setUpdatesEnabled(false); LokalizeSubwindowBase* prevEditor = static_cast(m_prevSubWindow->widget()); prevEditor->hideDocks(); guiFactory()->removeClient(prevEditor->guiClient()); prevEditor->statusBarItems.unregisterStatusBar(); if (qobject_cast(prevEditor)) { EditorTab* w = static_cast(prevEditor); EditorState state = w->state(); m_lastEditorState = state.dockWidgets.toBase64(); } /* QMenu* projectActions=static_cast(factory()->container("project_actions",this)); QList actionz=projectActions->actions(); int i=actionz.size(); //projectActions->menuAction()->setVisible(i); //qCWarning(LOKALIZE_LOG)<<"adding object"<=0) { disconnect(w, SIGNAL(signalNewEntryDisplayed()),actionz.at(i),SLOT(signalNewEntryDisplayed())); //static_cast(actionz.at(i))->addObject(static_cast( editor )->adaptor(),"Editor",Kross::ChildrenInterface::AutoConnectSignals); //static_cast(actionz.at(i))->trigger(); } } */ } LokalizeSubwindowBase* editor = static_cast(w->widget()); editor->reloadUpdatedXML(); if (qobject_cast(editor)) { EditorTab* w = static_cast(editor); w->setProperFocus(); EditorState state = w->state(); m_lastEditorState = state.dockWidgets.toBase64(); m_multiEditorAdaptor->setEditorTab(w); // connect(m_multiEditorAdaptor,SIGNAL(srcFileOpenRequested(QString,int)),this,SLOT(showTM())); /* QMenu* projectActions=static_cast(factory()->container("project_actions",this)); QList actionz=projectActions->actions(); int i=actionz.size(); //projectActions->menuAction()->setVisible(i); //qCWarning(LOKALIZE_LOG)<<"adding object"<=0) { connect(w, SIGNAL(signalNewEntryDisplayed()),actionz.at(i),SLOT(signalNewEntryDisplayed())); //static_cast(actionz.at(i))->addObject(static_cast( editor )->adaptor(),"Editor",Kross::ChildrenInterface::AutoConnectSignals); //static_cast(actionz.at(i))->trigger(); }*/ QTabBar* tw = m_mdiArea->findChild(); if (tw) tw->setTabToolTip(tw->currentIndex(), w->currentFilePath()); emit editorActivated(); } else if (w == m_projectSubWindow && m_projectSubWindow) { QTabBar* tw = m_mdiArea->findChild(); if (tw) tw->setTabToolTip(tw->currentIndex(), Project::instance()->path()); } editor->showDocks(); editor->statusBarItems.registerStatusBar(statusBar(), m_statusBarLabels); guiFactory()->addClient(editor->guiClient()); m_prevSubWindow = w; //qCWarning(LOKALIZE_LOG)<<"finished"< editors = m_mdiArea->subWindowList(); int i = editors.size(); while (--i >= 0) { //if (editors.at(i)==m_projectSubWindow) if (!qobject_cast(editors.at(i)->widget())) continue; if (!static_cast(editors.at(i)->widget())->queryClose()) return false; } bool ok = Project::instance()->queryCloseForAuxiliaryWindows(); if (ok) { QThreadPool::globalInstance()->clear(); Project::instance()->model()->threadPool()->clear(); } return ok; } EditorTab* LokalizeMainWindow::fileOpen_(QString filePath, const bool setAsActive) { return fileOpen(filePath, 0, setAsActive); } EditorTab* LokalizeMainWindow::fileOpen(QString filePath, int entry, bool setAsActive, const QString& mergeFile, bool silent) { if (filePath.length()) { FileToEditor::const_iterator it = m_fileToEditor.constFind(filePath); if (it != m_fileToEditor.constEnd()) { qCWarning(LOKALIZE_LOG) << "already opened:" << filePath; if (QMdiSubWindow* sw = it.value()) { m_mdiArea->setActiveSubWindow(sw); return static_cast(sw->widget()); } } } QByteArray state = m_lastEditorState; EditorTab* w = new EditorTab(this); QMdiSubWindow* sw = 0; //create QMdiSubWindow BEFORE fileOpen() because it causes some strange QMdiArea behaviour otherwise if (filePath.length()) sw = m_mdiArea->addSubWindow(w); QString suggestedDirPath; QMdiSubWindow* activeSW = m_mdiArea->currentSubWindow(); if (activeSW && qobject_cast(activeSW->widget())) { QString fp = static_cast(activeSW->widget())->currentFilePath(); if (fp.length()) suggestedDirPath = QFileInfo(fp).absolutePath(); } if (!w->fileOpen(filePath, suggestedDirPath, m_fileToEditor, silent)) { if (sw) { m_mdiArea->removeSubWindow(sw); sw->deleteLater(); } w->deleteLater(); return 0; } filePath = w->currentFilePath(); m_openRecentFileAction->addUrl(QUrl::fromLocalFile(filePath));//(w->currentUrl()); if (!sw) sw = m_mdiArea->addSubWindow(w); w->showMaximized(); sw->showMaximized(); if (!state.isEmpty()) { w->restoreState(QByteArray::fromBase64(state)); m_lastEditorState = state; } else { //Dummy restore to "initialize" widgets w->restoreState(w->saveState()); } if (entry/* || offset*/) w->gotoEntry(DocPosition(entry/*, DocPosition::Target, 0, offset*/)); if (setAsActive) { m_toBeActiveSubWindow = sw; QTimer::singleShot(0, this, &LokalizeMainWindow::applyToBeActiveSubWindow); } else { m_mdiArea->setActiveSubWindow(activeSW); sw->setUpdatesEnabled(false); //QTBUG-23289 } if (!mergeFile.isEmpty()) w->mergeOpen(mergeFile); connect(sw, &QMdiSubWindow::destroyed, this, &LokalizeMainWindow::editorClosed); connect(w, &EditorTab::aboutToBeClosed, this, &LokalizeMainWindow::resetMultiEditorAdaptor); connect(w, QOverload::of(&EditorTab::fileOpenRequested), this, QOverload::of(&LokalizeMainWindow::fileOpen)); connect(w, QOverload::of(&EditorTab::tmLookupRequested), this, QOverload::of(&LokalizeMainWindow::lookupInTranslationMemory)); QStringRef fnSlashed = filePath.midRef(filePath.lastIndexOf('/')); FileToEditor::const_iterator i = m_fileToEditor.constBegin(); while (i != m_fileToEditor.constEnd()) { if (i.key().endsWith(fnSlashed)) { static_cast(i.value()->widget())->setFullPathShown(true); w->setFullPathShown(true); } ++i; } m_fileToEditor.insert(filePath, sw); sw->setAttribute(Qt::WA_DeleteOnClose, true); emit editorAdded(); return w; } void LokalizeMainWindow::resetMultiEditorAdaptor() { m_multiEditorAdaptor->setEditorTab(m_spareEditor); //it will be reparented shortly if there are other editors } void LokalizeMainWindow::editorClosed(QObject* obj) { m_fileToEditor.remove(m_fileToEditor.key(static_cast(obj))); } EditorTab* LokalizeMainWindow::fileOpen(const QString& filePath, const QString& source, const QString& ctxt, const bool setAsActive) { EditorTab* w = fileOpen(filePath, 0, setAsActive); if (!w) return 0;//TODO message w->findEntryBySourceContext(source, ctxt); return w; } EditorTab* LokalizeMainWindow::fileOpen(const QString& filePath, DocPosition docPos, int selection, const bool setAsActive) { EditorTab* w = fileOpen(filePath, 0, setAsActive); if (!w) return 0;//TODO message w->gotoEntry(docPos, selection); return w; } QObject* LokalizeMainWindow::projectOverview() { if (!m_projectSubWindow) { ProjectTab* w = new ProjectTab(this); m_projectSubWindow = m_mdiArea->addSubWindow(w); w->showMaximized(); m_projectSubWindow->showMaximized(); connect(w, QOverload::of(&ProjectTab::fileOpenRequested), this, QOverload::of(&LokalizeMainWindow::fileOpen_)); connect(w, QOverload::of(&ProjectTab::projectOpenRequested), this, QOverload::of(&LokalizeMainWindow::openProject)); connect(w, QOverload<>::of(&ProjectTab::projectOpenRequested), this, QOverload<>::of(&LokalizeMainWindow::openProject)); connect(w, QOverload::of(&ProjectTab::searchRequested), this, QOverload::of(&LokalizeMainWindow::addFilesToSearch)); } if (m_mdiArea->currentSubWindow() == m_projectSubWindow) return m_projectSubWindow->widget(); return 0; } void LokalizeMainWindow::showProjectOverview() { projectOverview(); m_mdiArea->setActiveSubWindow(m_projectSubWindow); } TM::TMTab* LokalizeMainWindow::showTM() { if (!Project::instance()->isTmSupported()) { KMessageBox::information(nullptr, i18n("TM facility requires SQLite Qt module."), i18n("No SQLite module available")); return 0; } if (!m_translationMemorySubWindow) { TM::TMTab* w = new TM::TMTab(this); m_translationMemorySubWindow = m_mdiArea->addSubWindow(w); w->showMaximized(); m_translationMemorySubWindow->showMaximized(); connect(w, QOverload::of(&TM::TMTab::fileOpenRequested), this, QOverload::of(&LokalizeMainWindow::fileOpen)); } m_mdiArea->setActiveSubWindow(m_translationMemorySubWindow); return static_cast(m_translationMemorySubWindow->widget()); } FileSearchTab* LokalizeMainWindow::showFileSearch(bool activate) { EditorTab* precedingEditor = qobject_cast(activeEditor()); if (!m_fileSearchSubWindow) { FileSearchTab* w = new FileSearchTab(this); m_fileSearchSubWindow = m_mdiArea->addSubWindow(w); w->showMaximized(); m_fileSearchSubWindow->showMaximized(); connect(w, QOverload::of(&FileSearchTab::fileOpenRequested), this, QOverload::of(&LokalizeMainWindow::fileOpen)); connect(w, QOverload::of(&FileSearchTab::fileOpenRequested), this, QOverload::of(&LokalizeMainWindow::fileOpen_)); } if (activate) { m_mdiArea->setActiveSubWindow(m_fileSearchSubWindow); if (precedingEditor) { if (precedingEditor->selectionInSource().length()) static_cast(m_fileSearchSubWindow->widget())->setSourceQuery(precedingEditor->selectionInSource()); if (precedingEditor->selectionInTarget().length()) static_cast(m_fileSearchSubWindow->widget())->setTargetQuery(precedingEditor->selectionInTarget()); } } return static_cast(m_fileSearchSubWindow->widget()); } void LokalizeMainWindow::fileSearchNext() { FileSearchTab* w = showFileSearch(/*activate=*/false); //TODO fill search params based on current selection w->fileSearchNext(); } void LokalizeMainWindow::addFilesToSearch(const QStringList& files) { FileSearchTab* w = showFileSearch(); w->addFilesToSearch(files); } void LokalizeMainWindow::applyToBeActiveSubWindow() { m_mdiArea->setActiveSubWindow(m_toBeActiveSubWindow); } void LokalizeMainWindow::setupActions() { //all operations that can be done after initial setup //(via QTimer::singleShot) go to initLater() QElapsedTimer aaa; aaa.start(); setStandardToolBarMenuEnabled(true); QAction *action; KActionCollection* ac = actionCollection(); KActionCategory* actionCategory; KActionCategory* file = new KActionCategory(i18nc("@title actions category", "File"), ac); //KActionCategory* config=new KActionCategory(i18nc("@title actions category","Settings"), ac); KActionCategory* glossary = new KActionCategory(i18nc("@title actions category", "Glossary"), ac); KActionCategory* tm = new KActionCategory(i18nc("@title actions category", "Translation Memory"), ac); KActionCategory* proj = new KActionCategory(i18nc("@title actions category", "Project"), ac); actionCategory = file; // File //KStandardAction::open(this, SLOT(fileOpen()), ac); file->addAction(KStandardAction::Open, this, SLOT(fileOpen())); m_openRecentFileAction = KStandardAction::openRecent(this, SLOT(fileOpen(QUrl)), ac); file->addAction(KStandardAction::Quit, qApp, SLOT(closeAllWindows())); //Settings SettingsController* sc = SettingsController::instance(); KStandardAction::preferences(sc, &SettingsController::showSettingsDialog, ac); #define ADD_ACTION_SHORTCUT(_name,_text,_shortcut)\ action = actionCategory->addAction(QStringLiteral(_name));\ ac->setDefaultShortcut(action, QKeySequence( _shortcut ));\ action->setText(_text); //Window //documentBack //KStandardAction::close(m_mdiArea, SLOT(closeActiveSubWindow()), ac); actionCategory = file; ADD_ACTION_SHORTCUT("next-tab", i18n("Next tab"), Qt::CTRL + Qt::Key_Tab) connect(action, &QAction::triggered, m_mdiArea, &LokalizeMdiArea::activateNextSubWindow); ADD_ACTION_SHORTCUT("prev-tab", i18n("Previous tab"), Qt::CTRL + Qt::SHIFT + Qt::Key_Tab) connect(action, &QAction::triggered, m_mdiArea, &LokalizeMdiArea::activatePreviousSubWindow); ADD_ACTION_SHORTCUT("prev-active-tab", i18n("Previously active tab"), Qt::CTRL + Qt::Key_BracketLeft) connect(action, &QAction::triggered, m_mdiArea, &QMdiArea::activatePreviousSubWindow); //Tools actionCategory = glossary; Project* project = Project::instance(); ADD_ACTION_SHORTCUT("tools_glossary", i18nc("@action:inmenu", "Glossary"), Qt::CTRL + Qt::ALT + Qt::Key_G) connect(action, &QAction::triggered, project, &Project::showGlossary); actionCategory = tm; ADD_ACTION_SHORTCUT("tools_tm", i18nc("@action:inmenu", "Translation memory"), Qt::Key_F7) connect(action, &QAction::triggered, this, &LokalizeMainWindow::showTM); action = tm->addAction("tools_tm_manage", project, SLOT(showTMManager())); action->setText(i18nc("@action:inmenu", "Manage translation memories")); //Project actionCategory = proj; ADD_ACTION_SHORTCUT("project_overview", i18nc("@action:inmenu", "Project overview"), Qt::Key_F4) connect(action, &QAction::triggered, this, &LokalizeMainWindow::showProjectOverview); action = proj->addAction(QStringLiteral("project_configure"), sc, SLOT(projectConfigure())); action->setText(i18nc("@action:inmenu", "Configure project...")); action = proj->addAction(QStringLiteral("project_create"), sc, SLOT(projectCreate())); action->setText(i18nc("@action:inmenu", "Create software translation project...")); action = proj->addAction(QStringLiteral("project_create_odf"), Project::instance(), SLOT(projectOdfCreate())); action->setText(i18nc("@action:inmenu", "Create OpenDocument translation project...")); action = proj->addAction(QStringLiteral("project_open"), this, SLOT(openProject())); action->setText(i18nc("@action:inmenu", "Open project...")); action->setIcon(QIcon::fromTheme("project-open")); action = proj->addAction(QStringLiteral("project_close"), this, SLOT(closeProject())); action->setText(i18nc("@action:inmenu", "Close project")); action->setIcon(QIcon::fromTheme("project-close")); m_openRecentProjectAction = new KRecentFilesAction(i18nc("@action:inmenu", "Open recent project"), this); action = proj->addAction(QStringLiteral("project_open_recent"), m_openRecentProjectAction); connect(m_openRecentProjectAction, QOverload::of(&KRecentFilesAction::urlSelected), this, QOverload::of(&LokalizeMainWindow::openProject)); //Qt::QueuedConnection: defer until event loop is running to eliminate QWidgetPrivate::showChildren(bool) startup crash connect(Project::instance(), &Project::loaded, this, &LokalizeMainWindow::projectLoaded, Qt::QueuedConnection); ADD_ACTION_SHORTCUT("tools_filesearch", i18nc("@action:inmenu", "Search and replace in files"), Qt::Key_F6) connect(action, &QAction::triggered, this, &LokalizeMainWindow::showFileSearch); ADD_ACTION_SHORTCUT("tools_filesearch_next", i18nc("@action:inmenu", "Find next in files"), Qt::META + Qt::Key_F3) connect(action, &QAction::triggered, this, &LokalizeMainWindow::fileSearchNext); action = ac->addAction(QStringLiteral("tools_widgettextcapture"), this, SLOT(widgetTextCapture())); action->setText(i18nc("@action:inmenu", "Widget text capture")); setupGUI(Default, QStringLiteral("lokalizemainwindowui.rc")); //qCDebug(LOKALIZE_LOG)<<"action setup finished"<subWindowList()) { if (subwindow == m_translationMemorySubWindow && m_translationMemorySubWindow) subwindow->deleteLater(); else if (qobject_cast(subwindow->widget())) { m_fileToEditor.remove(static_cast(subwindow->widget())->currentFilePath());//safety m_mdiArea->removeSubWindow(subwindow); subwindow->deleteLater(); } else if (subwindow == m_projectSubWindow && m_projectSubWindow) static_cast(m_projectSubWindow->widget())->showWelcomeScreen(); } Project::instance()->load(QString()); //TODO scripts return true; } void LokalizeMainWindow::openProject(QString path) { path = SettingsController::instance()->projectOpen(path, false); //dry run if (path.isEmpty()) return; if (closeProject()) SettingsController::instance()->projectOpen(path, true);//really open } void LokalizeMainWindow::saveProperties(KConfigGroup& stateGroup) { saveProjectState(stateGroup); } void LokalizeMainWindow::saveProjectState(KConfigGroup& stateGroup) { QList editors = m_mdiArea->subWindowList(); QStringList files; QStringList mergeFiles; QList dockWidgets; //QList offsets; QList entries; QMdiSubWindow* activeSW = m_mdiArea->currentSubWindow(); int activeSWIndex = -1; int i = editors.size(); while (--i >= 0) { //if (editors.at(i)==m_projectSubWindow) if (!editors.at(i) || !qobject_cast(editors.at(i)->widget())) continue; EditorState state = static_cast(editors.at(i)->widget())->state(); if (editors.at(i) == activeSW) { activeSWIndex = files.size(); m_lastEditorState = state.dockWidgets.toBase64(); } files.append(state.filePath); mergeFiles.append(state.mergeFilePath); dockWidgets.append(state.dockWidgets.toBase64()); entries.append(state.entry); //offsets.append(state.offset); //qCWarning(LOKALIZE_LOG)<(editors.at(i)->widget() )->state().url; } //if (activeSWIndex==-1 && activeSW==m_projectSubWindow) if (files.size() == 0 && !m_lastEditorState.isEmpty()) { dockWidgets.append(m_lastEditorState); // save last state if no editor open } if (stateGroup.isValid()) stateGroup.writeEntry("Project", Project::instance()->path()); KConfig config; KConfigGroup projectStateGroup(&config, "State-" + Project::instance()->path()); projectStateGroup.writeEntry("Active", activeSWIndex); projectStateGroup.writeEntry("Files", files); projectStateGroup.writeEntry("MergeFiles", mergeFiles); projectStateGroup.writeEntry("DockWidgets", dockWidgets); //stateGroup.writeEntry("Offsets",offsets); projectStateGroup.writeEntry("Entries", entries); if (m_projectSubWindow) { ProjectTab *w = static_cast(m_projectSubWindow->widget()); if (w->unitsCount() > 0) projectStateGroup.writeEntry("UnitsCount", w->unitsCount()); } QString nameSpecifier = Project::instance()->path(); if (!nameSpecifier.isEmpty()) nameSpecifier.prepend('-'); KConfig* c = stateGroup.isValid() ? stateGroup.config() : &config; m_openRecentFileAction->saveEntries(KConfigGroup(c, "RecentFiles" + nameSpecifier)); - m_openRecentProjectAction->saveEntries(KConfigGroup(&config, "RecentProjects")); } void LokalizeMainWindow::readProperties(const KConfigGroup& stateGroup) { KConfig config; - const KConfig* c = stateGroup.isValid() ? stateGroup.config() : &config; - m_openRecentProjectAction->loadEntries(KConfigGroup(c, "RecentProjects")); - + m_openRecentProjectAction->loadEntries(KConfigGroup(&config, "RecentProjects")); QString path = stateGroup.readEntry("Project", QString()); if (Project::instance()->isLoaded() || path.isEmpty()) { //1. we weren't existing yet when the signal was emitted //2. defer until event loop is running to eliminate QWidgetPrivate::showChildren(bool) startup crash QTimer::singleShot(0, this, &LokalizeMainWindow::projectLoaded); } else Project::instance()->load(path); } void LokalizeMainWindow::projectLoaded() { QString projectPath = Project::instance()->path(); - qCDebug(LOKALIZE_LOG) << projectPath; - m_openRecentProjectAction->addUrl(QUrl::fromLocalFile(projectPath)); + qCDebug(LOKALIZE_LOG) << "Loaded project : " << projectPath; + if (!projectPath.isEmpty()) + m_openRecentProjectAction->addUrl(QUrl::fromLocalFile(projectPath)); KConfig config; QString nameSpecifier = projectPath; if (!nameSpecifier.isEmpty()) nameSpecifier.prepend('-'); m_openRecentFileAction->loadEntries(KConfigGroup(&config, "RecentFiles" + nameSpecifier)); //if project isn't loaded, still restore opened files from "State-" KConfigGroup stateGroup(&config, "State"); KConfigGroup projectStateGroup(&config, "State-" + projectPath); QStringList files; QStringList mergeFiles; QList dockWidgets; //QList offsets; QList entries; projectOverview(); if (m_projectSubWindow) { ProjectTab *w = static_cast(m_projectSubWindow->widget()); w->setLegacyUnitsCount(projectStateGroup.readEntry("UnitsCount", 0)); QTabBar* tw = m_mdiArea->findChild(); if (tw) for (int i = 0; i < tw->count(); i++) if (tw->tabText(i) == w->windowTitle()) tw->setTabToolTip(i, Project::instance()->path()); } entries = projectStateGroup.readEntry("Entries", entries); if (Settings::self()->restoreRecentFilesOnStartup()) files = projectStateGroup.readEntry("Files", files); mergeFiles = projectStateGroup.readEntry("MergeFiles", mergeFiles); dockWidgets = projectStateGroup.readEntry("DockWidgets", dockWidgets); int i = files.size(); int activeSWIndex = projectStateGroup.readEntry("Active", -1); QStringList failedFiles; while (--i >= 0) { if (i < dockWidgets.size()) { m_lastEditorState = dockWidgets.at(i); } if (!fileOpen(files.at(i), entries.at(i)/*, offsets.at(i)*//*,&activeSW11*/, activeSWIndex == i, mergeFiles.at(i),/*silent*/true)) failedFiles.append(files.at(i)); } if (!failedFiles.isEmpty()) { qCDebug(LOKALIZE_LOG) << "failedFiles" << failedFiles; // KMessageBox::error(this, i18nc("@info","Error opening the following files:")+ // "
  • "+failedFiles.join("
  • ")+"
  • " ); KNotification* notification = new KNotification("FilesOpenError", this); notification->setText(i18nc("@info", "Error opening the following files:\n\n") + "" + failedFiles.join("
    ") + ""); notification->sendEvent(); } if (files.isEmpty() && dockWidgets.size() > 0) { m_lastEditorState = dockWidgets.last(); // restore last state if no editor open } else { m_lastEditorState = stateGroup.readEntry("DefaultDockWidgets", m_lastEditorState); // restore default state if no last editor for this project } if (activeSWIndex == -1) { m_toBeActiveSubWindow = m_projectSubWindow; QTimer::singleShot(0, this, &LokalizeMainWindow::applyToBeActiveSubWindow); } projectSettingsChanged(); QTimer::singleShot(0, this, &LokalizeMainWindow::loadProjectScripts); } void LokalizeMainWindow::projectSettingsChanged() { //TODO show langs setCaption(Project::instance()->projectID()); } void LokalizeMainWindow::widgetTextCapture() { WidgetTextCaptureConfig* w = new WidgetTextCaptureConfig(this); w->show(); } //BEGIN DBus interface //#include "plugin.h" #include "mainwindowadaptor.h" #include #include using namespace Kross; class MyScriptingPlugin: public Kross::ScriptingPlugin { public: MyScriptingPlugin(QObject* lokalize, QObject* editor) : Kross::ScriptingPlugin(lokalize) { addObject(lokalize, "Lokalize"); addObject(Project::instance(), "Project"); addObject(editor, "Editor"); setXMLFile("scriptsui.rc", true); } ~MyScriptingPlugin() {} }; #define PROJECTRCFILE "scripts.rc" #define PROJECTRCFILEDIR Project::instance()->projectDir()+"/lokalize-scripts" #define PROJECTRCFILEPATH Project::instance()->projectDir()+"/lokalize-scripts" "/" PROJECTRCFILE //TODO be lazy creating scripts dir ProjectScriptingPlugin::ProjectScriptingPlugin(QObject* lokalize, QObject* editor) : Kross::ScriptingPlugin(Project::instance()->kind(), PROJECTRCFILEPATH, Project::instance()->kind(), lokalize) { if (Project::instance()->projectDir().isEmpty()) return; QString filepath = PROJECTRCFILEPATH; // Remove directory "scripts.rc" if it is empty. It could be // mistakenly created by Lokalize 15.04.x. if (QFileInfo(filepath).isDir() && !QDir().rmdir(filepath)) { qCCritical(LOKALIZE_LOG) << "Failed to remove directory" << filepath << "to create scripting configuration file with at the same path. " << "The directory may be not empty."; return; } if (!QFile::exists(filepath)) { QDir().mkdir(QFileInfo(filepath).dir().path()); QFile f(filepath); if (!f.open(QIODevice::WriteOnly)) return; QTextStream out(&f); out << ""; f.close(); } //qCWarning(LOKALIZE_LOG)<collection(Project::instance()->kind()); if (!collection) return; foreach (const QString &collectionname, collection->collections()) { Kross::ActionCollection* c = collection->collection(collectionname); if (!c->isEnabled()) continue; foreach (Kross::Action* action, c->actions()) { if (action->property("autorun").toBool()) action->trigger(); if (action->property("first-run").toBool() && Project::local()->firstRun()) action->trigger(); } } } ProjectScriptingPlugin::~ProjectScriptingPlugin() { Kross::ActionCollection* collection = Kross::Manager::self().actionCollection()->collection(Project::instance()->kind()); if (!collection) return; QString scriptsrc = PROJECTRCFILE; QDir rcdir(PROJECTRCFILEDIR); qCDebug(LOKALIZE_LOG) << rcdir.entryList(QStringList("*.rc"), QDir::Files); foreach (const QString& rc, QDir(PROJECTRCFILEDIR).entryList(QStringList("*.rc"), QDir::Files)) if (rc != scriptsrc) qCWarning(LOKALIZE_LOG) << rc << collection->readXmlFile(rcdir.absoluteFilePath(rc)); } /* void LokalizeMainWindow::checkForProjectAlreadyOpened() { QStringList services=QDBusConnection::sessionBus().interface()->registeredServiceNames().value(); int i=services.size(); while(--i>=0) { if (services.at(i).startsWith("org.kde.lokalize")) //QDBusReply QDBusConnectionInterface::servicePid ( const QString & serviceName ) const; QDBusConnection::callWithCallback(QDBusMessage::createMethodCall(services.at(i),"/ThisIsWhatYouWant","org.kde.Lokalize.MainWindow","currentProject"), this, SLOT(), const char * errorMethod); } } */ void LokalizeMainWindow::registerDBusAdaptor() { new MainWindowAdaptor(this); QDBusConnection::sessionBus().registerObject(QLatin1String("/ThisIsWhatYouWant"), this); //qCWarning(LOKALIZE_LOG)<registeredServiceNames().value(); #ifndef Q_OS_MAC //TODO really fix!!! guiFactory()->addClient(new MyScriptingPlugin(this, m_multiEditorAdaptor)); #endif //QMenu* projectActions=static_cast(factory()->container("project_actions",this)); /* KActionCollection* actionCollection = mWindow->actionCollection(); actionCollection->action("file_save")->setEnabled(canSave); actionCollection->action("file_save_as")->setEnabled(canSave); */ } void LokalizeMainWindow::loadProjectScripts() { if (m_projectScriptingPlugin) { guiFactory()->removeClient(m_projectScriptingPlugin); delete m_projectScriptingPlugin; } //a HACK to get new .rc files shown w/o requiring a restart m_projectScriptingPlugin = new ProjectScriptingPlugin(this, m_multiEditorAdaptor); //guiFactory()->addClient(m_projectScriptingPlugin); //guiFactory()->removeClient(m_projectScriptingPlugin); delete m_projectScriptingPlugin; m_projectScriptingPlugin = new ProjectScriptingPlugin(this, m_multiEditorAdaptor); guiFactory()->addClient(m_projectScriptingPlugin); } int LokalizeMainWindow::lookupInTranslationMemory(DocPosition::Part part, const QString& text) { TM::TMTab* w = showTM(); if (!text.isEmpty()) w->lookup(part == DocPosition::Source ? text : QString(), part == DocPosition::Target ? text : QString()); return w->dbusId(); } int LokalizeMainWindow::lookupInTranslationMemory(const QString& source, const QString& target) { TM::TMTab* w = showTM(); w->lookup(source, target); return w->dbusId(); } int LokalizeMainWindow::showTranslationMemory() { /*activateWindow(); raise(); show();*/ return lookupInTranslationMemory(DocPosition::UndefPart, QString()); } int LokalizeMainWindow::openFileInEditorAt(const QString& path, const QString& source, const QString& ctxt) { EditorTab* w = fileOpen(path, source, ctxt, true); if (!w) return -1; return w->dbusId(); } int LokalizeMainWindow::openFileInEditor(const QString& path) { return openFileInEditorAt(path, QString(), QString()); } QObject* LokalizeMainWindow::activeEditor() { //QList editors=m_mdiArea->subWindowList(); QMdiSubWindow* activeSW = m_mdiArea->currentSubWindow(); if (!activeSW || !qobject_cast(activeSW->widget())) return 0; return activeSW->widget(); } QObject* LokalizeMainWindow::editorForFile(const QString& path) { FileToEditor::const_iterator it = m_fileToEditor.constFind(QFileInfo(path).canonicalFilePath()); if (it == m_fileToEditor.constEnd()) return 0; QMdiSubWindow* w = it.value(); if (!w) return 0; return static_cast(w->widget()); } int LokalizeMainWindow::editorIndexForFile(const QString& path) { EditorTab* editor = static_cast(editorForFile(path)); if (!editor) return -1; return editor->dbusId(); } QString LokalizeMainWindow::currentProject() { return Project::instance()->path(); } #ifdef Q_OS_WIN #include int LokalizeMainWindow::pid() { return GetCurrentProcessId(); } #else #include int LokalizeMainWindow::pid() { return getpid(); } #endif QString LokalizeMainWindow::dbusName() { return QStringLiteral("org.kde.lokalize-%1").arg(pid()); } void LokalizeMainWindow::busyCursor(bool busy) { busy ? QApplication::setOverrideCursor(Qt::WaitCursor) : QApplication::restoreOverrideCursor(); } void LokalizeMdiArea::activateNextSubWindow() { this->setActivationOrder((QMdiArea::WindowOrder)Settings::tabSwitch()); this->QMdiArea::activateNextSubWindow(); this->setActivationOrder(QMdiArea::ActivationHistoryOrder); } void LokalizeMdiArea::activatePreviousSubWindow() { this->setActivationOrder((QMdiArea::WindowOrder)Settings::tabSwitch()); this->QMdiArea::activatePreviousSubWindow(); this->setActivationOrder(QMdiArea::ActivationHistoryOrder); } MultiEditorAdaptor::MultiEditorAdaptor(EditorTab *parent) : EditorAdaptor(parent) { setObjectName(QStringLiteral("MultiEditorAdaptor")); connect(parent, QOverload::of(&EditorTab::destroyed), this, QOverload::of(&MultiEditorAdaptor::handleParentDestroy)); } void MultiEditorAdaptor::setEditorTab(EditorTab* e) { if (parent()) disconnect(parent(), QOverload::of(&EditorTab::destroyed), this, QOverload::of(&MultiEditorAdaptor::handleParentDestroy)); if (e) connect(e, QOverload::of(&EditorTab::destroyed), this, QOverload::of(&MultiEditorAdaptor::handleParentDestroy)); setParent(e); setAutoRelaySignals(false); setAutoRelaySignals(true); } void MultiEditorAdaptor::handleParentDestroy(QObject* p) { Q_UNUSED(p); setParent(0); } //END DBus interface DelayedFileOpener::DelayedFileOpener(const QVector& urls, LokalizeMainWindow* lmw) : QObject() , m_urls(urls) , m_lmw(lmw) { //do the work just after project loading is finished //(i.e. all the files from previous project session are loaded) QTimer::singleShot(1, this, &DelayedFileOpener::doOpen); } void DelayedFileOpener::doOpen() { int lastIndex = m_urls.count() - 1; for (int i = 0; i <= lastIndex; i++) m_lmw->fileOpen(m_urls.at(i), 0, /*set as active*/i == lastIndex); deleteLater(); } #include "moc_lokalizesubwindowbase.cpp" #include "moc_multieditoradaptor.cpp"