diff --git a/src/glossary/glossary.cpp b/src/glossary/glossary.cpp index 3de92e5..f1655be 100644 --- a/src/glossary/glossary.cpp +++ b/src/glossary/glossary.cpp @@ -1,734 +1,733 @@ /* **************************************************************************** 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()); 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); QDomElement n = termEntry.firstChildElement(langSet); QDomDocument document = n.ownerDocument(); int i = 0; while (!n.isNull()) { QString nLang = n.attribute(xmlLang, defaultLang); if (lang == nLang || minusLang == nLang || soleLang == nLang) { ourLangSetElement = n; QDomElement ntigElem = n.firstChildElement(ntig); while (!ntigElem.isNull()) { if (i == index) { tigElement = ntigElem; termElement = ntigElem.firstChildElement(termGrp).firstChildElement(term); return; } ntigElem = ntigElem.nextSiblingElement(ntig); i++; } QDomElement tigElem = n.firstChildElement(tig); while (!tigElem.isNull()) { //qCDebug(LOKALIZE_LOG)<& 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